Repository navigation
Docs: Add docs for Table Maintenance in Flink - #13771
JeonDaehong wants to merge 4 commits into
Conversation
- Introduced comprehensive documentation for Flink Table Maintenance in Apache Iceberg. - Covers automated snapshot expiration, file compaction, and orphan file cleanup. - Includes configuration options, examples, scheduling, and best practices. - Provides lock management details and troubleshooting tips.
|
@pvary @Guosmilesmile |
Guosmilesmile
left a comment
There was a problem hiding this comment.
@JeonDaehong Thank you very much for your PR! Regarding the documentation part, I feel there are a few points that need to be discussed:
-
A new md file was added but not included in mkdocs.yml. I think this page will not take effect. I feel it might be unnecessary to add a new page; instead, we could add the content to the existing flink-actions.md, distinguishing between streaming and batch. However, this is just my personal opinion, and I would like to hear @pvary Peter’s thoughts.
-
Could we add a section about which table maintenance operations Flink currently supports, what their purposes are, and describe the corresponding configurations within each relevant feature, rather than putting all configurations in one section?
-
Regarding the locking part, there is no Java API configuration provided; the configuration options given are for SQL, and SQL only uses them when compaction is enabled in sink v2. I suggest separating this and describing it in an independent section.
| ```java | ||
| StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); | ||
| TableLoader tableLoader = TableLoader.fromHadoopTable("path/to/table"); | ||
| TriggerLockFactory lockFactory = TriggerLockFactory.defaultLockFactory(); |
There was a problem hiding this comment.
I think we do not have a method called defaultLockFactory; instead, we need to create a specific LockFactory.
| | `scheduleOnCommitCount(int)` | Trigger after N commits | No automatic scheduling | int | | ||
| | `scheduleOnDataFileCount(int)` | Trigger after N data files | No automatic scheduling | int | | ||
| | `scheduleOnDataFileSize(long)` | Trigger after total data file size (bytes) | No automatic scheduling | long | | ||
| | `scheduleOnIntervalSecond(long)` | Trigger after time interval (seconds) | No automatic scheduling | long | | ||
| | `parallelism(int)` | Parallelism for this specific task | Inherits from TableMaintenance | int | |
There was a problem hiding this comment.
Can we extract the common configurations belonging to the MaintenanceTaskBuilder, instead of having them appear in each functional configuration separately?
|
|
||
| You can also configure maintenance behavior through Flink configuration: | ||
|
|
||
| | Configuration Key | Description | Default Value | Type | | ||
| |-------------------|-------------|---------------|------| | ||
| | `iceberg.maintenance.rate-limit-seconds` | Rate limit in seconds | 60 | long | | ||
| | `iceberg.maintenance.lock-check-delay-seconds` | Lock check delay in seconds | 30 | long | | ||
| | `iceberg.maintenance.rewrite.max-bytes` | Maximum rewrite bytes | Long.MAX_VALUE | long | | ||
| | `iceberg.maintenance.rewrite.schedule.commit-count` | Schedule on commit count | 10 | int | | ||
| | `iceberg.maintenance.rewrite.schedule.data-file-count` | Schedule on data file count | 1000 | int | | ||
| | `iceberg.maintenance.rewrite.schedule.data-file-size` | Schedule on data file size | 100GB | long | | ||
| | `iceberg.maintenance.rewrite.schedule.interval-second` | Schedule interval in seconds | 600 | long | |
There was a problem hiding this comment.
From my perspective, this part seems a bit strange. How is it used? I can't think of where these configurations come from.
|
|
||
| | Method | Description | Default Value | Type | | ||
| |--------|-------------|---------------|------| | ||
| | `maxSnapshotAge(Duration)` | Maximum age of snapshots to retain | No limit | Duration | |
There was a problem hiding this comment.
Default value is 5 days ago
| | `partialProgressEnabled(boolean)` | Enable partial progress commits | false | boolean | | ||
| | `partialProgressMaxCommits(int)` | Maximum commits for partial progress | 10 | int | | ||
| | `scheduleOnCommitCount(int)` | Trigger after N commits | 10 | int | | ||
| | `scheduleOnDataFileCount(int)` | Trigger after N data files | 1000 | int | | ||
| | `scheduleOnDataFileSize(long)` | Trigger after total data file size (bytes) | 100GB | long | | ||
| | `scheduleOnIntervalSecond(long)` | Trigger after time interval (seconds) | 600 (10 minutes) | long | |
| | Method | Description | Default Value | Type | | ||
| |--------|-------------|---------------|------| | ||
| | `targetFileSizeBytes(long)` | Target size for rewritten files | Table property or 512MB | long | | ||
| | `partialProgressEnabled(boolean)` | Enable partial progress commits | false | boolean | |
There was a problem hiding this comment.
Perhaps "Enable partial progress commits, allowing compacted data files to be committed in batches." would be easier to understand.
|
|
||
|
|
||
|
|
||
| ### RewriteDataFiles Configuration |
There was a problem hiding this comment.
Some configurations are still missing from the documentation:
minFileSizeBytes
maxFileSizeBytes
minInputFiles
deleteFileThreshold
rewriteAll
maxFileGroupSizeBytes
maxFilesToRewrite
filter
| ## Scheduling Options | ||
|
|
||
| Maintenance tasks can be triggered based on various conditions: | ||
|
|
||
| ### Time-based Scheduling | ||
| ```java | ||
| ExpireSnapshots.builder() | ||
| .scheduleOnIntervalSecond(3600) | ||
| ``` | ||
|
|
||
| ### Commit-based Scheduling | ||
| ```java | ||
| RewriteDataFiles.builder() | ||
| .scheduleOnCommitCount(50) | ||
| ``` | ||
|
|
||
| ### Data Volume-based Scheduling | ||
| ```java | ||
| RewriteDataFiles.builder() | ||
| .scheduleOnDataFileCount(500) | ||
| .scheduleOnDataFileSize(50L * 1024 * 1024 * 1024) | ||
| ``` |
There was a problem hiding this comment.
It would be better to integrate these with the common configuration section provided above.
| .addPostCommitTopology( | ||
| TableMaintenance.forTable(env, tableLoader, TriggerLockFactory.defaultLockFactory()) | ||
| .rateLimit(Duration.ofMinutes(10)) | ||
| .add(ExpireSnapshots.builder().scheduleOnCommitCount(10)) | ||
| .add(RewriteDataFiles.builder().scheduleOnDataFileCount(50)) | ||
| ) |
There was a problem hiding this comment.
It is not invoked in this way; specific cases can be found here.
https://github.com/apache/iceberg/blob/main/flink/v2.0/flink/src/test/java/org/apache/iceberg/flink/sink/TestIcebergSinkCompact.java
There was a problem hiding this comment.
I have reviewed it. Thank you !
| ## Troubleshooting | ||
|
|
||
| ### Common Issues | ||
|
|
||
| **OutOfMemoryError during file deletion:** | ||
| ```java | ||
| .deleteBatchSize(500) | ||
| ``` | ||
|
|
||
| **Lock conflicts:** | ||
| ```java | ||
| .lockCheckDelay(Duration.ofMinutes(1)) | ||
| .rateLimit(Duration.ofMinutes(10)) | ||
| ``` | ||
|
|
||
| **Slow rewrite operations:** | ||
| ```java | ||
| .partialProgressEnabled(true) | ||
| .partialProgressMaxCommits(3) | ||
| .maxRewriteBytes(1024 * 1024 * 1024) | ||
| ``` No newline at end of file |
There was a problem hiding this comment.
What is the specific scenario here? Providing numerical recommendations directly without detailed reasoning can seem a bit odd.
@Guosmilesmile Based on your comments, I will make the necessary changes and commit them again. First, I will continue adding the content to the flink-table-maintenance.md file and update the mkdocs.yml file accordingly. If @pvary prefers the content to be appended to flink-action.md instead, I will revise it and move it there at that time. Thank you. |
docs: add flink-table-maintenacne
Based on the maintainer's feedback, I have revised the content.
|
Hello :D @Guosmilesmile @pvary If you have any further feedback, I will gladly make additional updates accordingly. Also, I have added the entry to the yml file for now. However, if @pvary suggests that it would be better to add this content to flink-actions.md, I will revise it to include it under the actions section. Thank you always for your help! :D |
|
Sorry, I've been quite busy lately. I'll find some time to review it in a few days. |
Please take a look whenever you have time. 😄 I have reviewed all the work you’ve done in your recent PRs. It seems you’ve been working quite busily lately. I’ll also continue to study hard and contribute through PRs so that I can become a Maintainer and take part in the core development of Iceberg’s code. 😄 |
Guosmilesmile
left a comment
There was a problem hiding this comment.
Many of the configuration items need to be rechecked for their default values. Thank you.
| jdbcProps.put("user", "flink"); | ||
| jdbcProps.put("password", "flinkpw"); |
There was a problem hiding this comment.
I think this part should be jdbc.user and jdbc.password .
| | `scheduleOnCommitCount(int)` | Trigger after N commits | No automatic scheduling | int | | ||
| | `scheduleOnDataFileCount(int)` | Trigger after N data files | No automatic scheduling | int | | ||
| | `scheduleOnDataFileSize(long)` | Trigger after total data file size (bytes) | No automatic scheduling | long | | ||
| | `scheduleOnIntervalSecond(long)` | Trigger after time interval (seconds) | No automatic scheduling | long | |
There was a problem hiding this comment.
The configuration here is not complete, and there is no scheduleOnIntervalSecond . It should be scheduleOnInterval. Can we refer to https://github.com/apache/iceberg/blob/main/flink/v2.0/flink/src/main/java/org/apache/iceberg/flink/maintenance/api/MaintenanceTaskBuilder.java for adjustment?
| | `maxRewriteBytes(long)` | Maximum bytes to rewrite per execution | Long.MAX_VALUE | long | | ||
|
|
||
|
|
||
| ### Scheduling Examples |
There was a problem hiding this comment.
I personally think that the "Scheduling Examples" section is not necessary. Instead, we could add a part before "Configuration Options" to explain which table maintenance features are currently supported by Flink. Each feature could be explained separately, detailing how to use it, somewhat like this: https://iceberg.incubator.apache.org/docs/nightly/spark-procedures/#expire_snapshots
| | `retainLast(int)` | Minimum number of snapshots to retain | 1 | int | | ||
| | `deleteBatchSize(int)` | Number of files to delete in each batch | 1000 | int | | ||
| | `planningWorkerPoolSize(int)` | Number of worker threads for planning snapshot expiration | System default | int | | ||
| | `cleanExpiredMetadata(boolean)` | Remove expired metadata files when expiring snapshots | true | boolean | |
There was a problem hiding this comment.
I think the default value is false, please help to check it agin ,thanks
| | `minFileSizeBytes(long)` | Minimum size of files eligible for compaction | 0 | long | | ||
| | `maxFileSizeBytes(long)` | Maximum size of files eligible for compaction | Long.MAX_VALUE | long | | ||
| | `minInputFiles(int)` | Minimum number of files to trigger rewrite | 1 | int | | ||
| | `deleteFileThreshold(double)` | Fraction of delete files to trigger rewrite | 0.0 | double | |
There was a problem hiding this comment.
Defaults to Integer.MAX_VALUE, which means this feature is not enabled by default.
| ``` | ||
|
|
||
|
|
||
| ## Flink Configuration Options |
There was a problem hiding this comment.
I think the content of this part is incorrect, and it is not necessary.
| IcebergSink.forRowData(dataStream) | ||
| .table(table) | ||
| .tableLoader(tableLoader) | ||
| .setAll(flinkConf) | ||
| .append(); | ||
| ``` | ||
|
|
There was a problem hiding this comment.
It would be great if we could add some examples for the SQL part.
| flink-maintenance.lock.type=jdbc | ||
| flink-maintenance.lock.lock-id=catalog.db.table | ||
| flink-maintenance.lock.jdbc.uri=jdbc:postgresql://localhost:5432/iceberg | ||
| flink-maintenance.lock.jdbc.user=flink | ||
| flink-maintenance.lock.jdbc.password=flinkpw | ||
| flink-maintenance.lock.jdbc.init-lock-tables=true |
There was a problem hiding this comment.
This is only used in SQL and is only applicable when writing with compression. I suggest presenting the configuration items in a tabular format.
| flink-maintenance.lock.type=zookeeper | ||
| flink-maintenance.lock.zookeeper.uri=zk://zk1:2181,zk2:2181 | ||
| flink-maintenance.lock.zookeeper.session-timeout-ms=60000 | ||
| flink-maintenance.lock.zookeeper.connection-timeout-ms=15000 | ||
| flink-maintenance.lock.zookeeper.max-retries=3 | ||
| flink-maintenance.lock.zookeeper.base-sleep-ms=3000 | ||
| ``` |
| flink-maintenance.lock.zookeeper.max-retries=3 | ||
| flink-maintenance.lock.zookeeper.base-sleep-ms=3000 | ||
| ``` | ||
| Use ZooKeeper-based locks only in high-availability or multi-process coordination environments. |
There was a problem hiding this comment.
I feel that this is not necessary.
Thank you for the feedback. I’ll also review the default values in the related classes. :D |
This PR adds documentation for issue
( Docs update for Flink Table Maintenance API - Fixes #13307 )
It incorporates feedback received from the previously closed PR #13360.
Please review and let me know if any changes are needed. I will update promptly.