[Refactor] Introduce Iceberg TableRuntimePlugin#4073
[Refactor] Introduce Iceberg TableRuntimePlugin#4073majin1102 wants to merge 6 commits intoapache:masterfrom
Conversation
0f1a1a0 to
40120af
Compare
c3ccf93 to
72eef46
Compare
Codecov Report✅ All modified and coverable lines are covered by tests.
Additional details and impacted files@@ Coverage Diff @@
## master #4073 +/- ##
============================================
- Coverage 28.63% 22.39% -6.24%
+ Complexity 3942 2552 -1390
============================================
Files 654 458 -196
Lines 52293 42116 -10177
Branches 6621 5917 -704
============================================
- Hits 14973 9433 -5540
+ Misses 36230 31871 -4359
+ Partials 1090 812 -278
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
| tableService.addHandlerChain(chain); | ||
| } | ||
| private List<TableRuntimePlugin> initTablePlugins() { | ||
| LOG.info("Setting up AMS table executors..."); |
There was a problem hiding this comment.
Setting up AMS Table Plugins ...
| private List<TableRuntimePlugin> initTablePlugins() { | ||
| LOG.info("Setting up AMS table executors..."); | ||
| InlineTableExecutors.getInstance().setup(tableService, serviceConfig); | ||
| IcebergTablePlugin icebergTablePlugin = |
There was a problem hiding this comment.
How to load different table runtime plugins?
There was a problem hiding this comment.
I think we should still use SPI(PluginManager) to do this. I'm preparing to create a LanceTableRuntimePlugin to handle lance tables and we could see the path clearly.
But Iceberg table runtime plugin is automatically loaded.
How do you feel
|
|
||
| @Override | ||
| public boolean accept(ServerTableIdentifier tableIdentifier) { | ||
| return tableIdentifier.getFormat() == TableFormat.ICEBERG |
There was a problem hiding this comment.
IcebergTablePlugin will accept other formats? It should not be named as IcebergTablePlugin
There was a problem hiding this comment.
I think TableFormatPlugin should not be implemented in the current PR. The current PR is for renaming and organizing, which I can provide later
There was a problem hiding this comment.
IcebergTablePlugin will accept other formats? It should not be named as
IcebergTablePlugin
I name this as IcebergTablePlugin because the current three are all based on Iceberg. I'm open to have another name. Do you have another idea.
There was a problem hiding this comment.
How about IcebergLikeTablePlugin
|
|
||
| @Override | ||
| public void initialize(List<TableRuntime> tableRuntimes) { | ||
| if (headHandler != null) { |
There was a problem hiding this comment.
Already checked null in construct function
| } | ||
| private void initTableRuntimePlugins() { | ||
| List<TableRuntime> tableRuntimes = new ArrayList<>(tableRuntimeMap.values()); | ||
| tableRuntimePlugins.forEach(plugin -> plugin.initialize(tableRuntimes)); |
There was a problem hiding this comment.
Plugin is invoked in initTableRuntimes when create tableRuntime. Should we check the method workflow?
There was a problem hiding this comment.
Plugin is initialized in this initTableRuntimePlugins, not creating table runtime? what do you mean by check the method workflow?
There was a problem hiding this comment.
I'm not sure if it occur NPE when invoke plugin functions
|
|
||
| void initialize(List<TableRuntime> tableRuntimes); | ||
|
|
||
| void onTableCreated(@Nullable AmoroTable<?> amoroTable, TableRuntime tableRuntime); |
There was a problem hiding this comment.
The interface is named TableXXX. Should we simplify the method names, for example to onCreate?
|
|
||
| import java.util.List; | ||
|
|
||
| public interface TableRuntimePlugin { |
There was a problem hiding this comment.
1. TableRuntimePlugin violates Single Responsibility Principle
This interface mixes two distinct concerns:
- Factory/decorator role: accept() + createTableRuntime() intercepts and decorates TableRuntime creation.
- Lifecycle listener role: initialize() / onTableCreated() / onTableDropped() / dispose() observes table lifecycle events.
These should be separated. The factory/decorator logic belongs in the TableRuntimeFactory hierarchy (e.g. as a decorator pattern), while the lifecycle observation should be its own interface. Mixing them forces every plugin implementor to deal with both concerns even if they only care about one.
2. Semantic overlap between TableRuntimePlugin.accept() and TableRuntimeFactory.accept()
In DefaultTableService.createTableRuntime(), there is a two-phase accept chain: first TableRuntimeFactory.accept(identifier, properties) selects the factory, then TableRuntimePlugin.accept(identifier) selects the plugin to intercept creation. These two have overlapping semantics — both decide "does this component handle this table?" — but with inconsistent signatures (TableRuntimeFactory receives table properties, TableRuntimePlugin does not). This dual-accept flow is confusing and error-prone. Consider unifying the entry point so there is a single, clear decision path for table runtime creation.
Why are the changes needed?
Refactoring work for format processing extensions
Brief change log
How was this patch tested?
Add some test cases that check the changes thoroughly including negative and positive cases if possible
Add screenshots for manual tests if appropriate
Run test locally before making a pull request
Documentation