NestDream opened a new pull request, #10089:
URL: https://github.com/apache/paimon/pull/10089

   ### Purpose
   
   fix #10088
   
   `CALL sys.expire_snapshots(`table` => 'default.T', retain_min => 0, 
older_than => '2030-01-01 00:00:00')` is accepted by the Flink procedure and by 
the Flink `expire_snapshots` action, and so is any negative `retain_min`; the 
Spark procedure passes `retain_min` through the same 
`ProcedureUtils.fillInSnapshotOptions` into the same `ExpireSnapshotsImpl`. 
`ExpireSnapshotsImpl.expire()` only checks `retainMax >= retainMin`, so with 
`retain_min => 0` the exclusive upper bound `latestSnapshotId - retainMin + 1` 
becomes `latestSnapshotId + 1`. `innerExpireUntil` then collects every existing 
snapshot, deletes the changelog files of all of them including the latest one, 
and deletes the data files that a later snapshot marks as removed even though 
the older snapshots in the range still reference them. Only after that it 
notices that the end snapshot `latestSnapshotId + 1` does not exist, returns 0 
and leaves every snapshot file and manifest in place. The table ends up with 
all its snapshots
  listed in `$snapshots` and some of them pointing at files that are gone. The 
table option `snapshot.num-retained.min` is validated to be at least 1 in 
`SchemaValidation`; the procedure arguments bypass that.
   
   I reproduced it on a local Flink 1.20.1 MiniCluster against the unmodified 
module jar (master 54d8596ce). On a `changelog-producer = input` table with 
three inserts the call above returns `0`, `$snapshots` still lists 1, 2, 3, the 
three changelog files are deleted, and a batch read of the changelog of 
snapshot 3 (`/*+ OPTIONS('incremental-between'='2,3', 
'incremental-between-scan-mode'='changelog') */`) fails with 
`FileNotFoundException: File '.../bucket-0/changelog-....parquet' not found`. 
On a table where snapshot 3 is an `INSERT OVERWRITE`, the same call deletes the 
two data files of snapshots 1 and 2 while keeping their snapshot files, so 
`SELECT * FROM T /*+ OPTIONS('scan.snapshot-id'='2') */` fails with the same 
`FileNotFoundException`. `retain_min => -1` behaves the same way. In 
`ExpireSnapshotsTest` with five commits on a `changelog-producer = input` 
store, `expire()` with `snapshotRetainMin(0)` returns 0, keeps all five 
snapshot files and deletes all 20 changelog files.
   
   This PR adds `checkArgument(retainMin >= 1, ...)` next to the existing 
`retainMax >= retainMin` check in `ExpireSnapshotsImpl`, with a message in the 
style of the existing one. With the fix the call above fails with 
`IllegalArgumentException: retainMin (0) must be at least 1.` before anything 
is deleted. `retain_max` alone needs no new check: with `retainMin >= 1` 
enforced first, the existing check already rejects `retainMax <= 0`.
   
   ### Tests
   
   New `ExpireSnapshotsTest#testExpireRejectsNonPositiveRetainMin`: five 
commits on a `changelog-producer = input` store, then `expire()` with 
`snapshotRetainMin(0)` and `snapshotRetainMin(-1)`; asserts the 
`IllegalArgumentException` with the message above, that the set of files under 
the table is unchanged, that all five snapshots are still readable, and 
`assertCleaned()`. Without the fix it fails at the first assertion (`Expecting 
code to raise a throwable`), and the same setup deletes all 20 changelog files 
while every snapshot file survives. The whole `ExpireSnapshotsTest` class 
passes (38 tests including the new one).
   
   The MiniCluster scenario above (three inserts plus `retain_min => 0`, three 
inserts plus `retain_min => -1`, the `INSERT OVERWRITE` table, and `retain_min 
=> 1` as a control) was run three times each against the unmodified and the 
fixed module jar. Unmodified: `0` returned, files deleted, 
`FileNotFoundException` on read, in all three runs. Fixed: the argument error, 
no file deleted, snapshot 3's changelog and snapshot 2 read back correctly. The 
control expires snapshots 1 and 2 and keeps 3 on both.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to