englefly commented on code in PR #68282:
URL: https://github.com/apache/doris/pull/68282#discussion_r4079935522


##########
fe/fe-core/src/main/java/org/apache/doris/datasource/InternalCatalog.java:
##########
@@ -3904,7 +3904,7 @@ public void truncateTable(String dbName, String 
tableName, PartitionNamesInfo pa
             oldPartitions = truncateTableInternal(olapTable, newPartitions,
                     truncateEntireTable, recyclePartitionParamMap, forceDrop, 
version, versionTimeMs);
             if (truncateEntireTable) {
-                
Env.getCurrentEnv().getAnalysisManager().removeTableStats(olapTable.getId());
+                
Env.getCurrentEnv().getAnalysisManager().resetTableStats(olapTable);

Review Comment:
   Fixed in 19756ddc9de, and the reproduction you describe is now covered by a 
deterministic replay-order unit test.
   
   Confirmed the mechanism exactly as you describe it: `resetTableStats()` 
created the record when the table had none, so the truncate entry could 
re-create a record which a concurrent journaled deletion (`DROP STATS`, the 
analyzer's empty table cleanup, the statistics cleaner) had removed. In your 
ordering (deletion applied before the truncate reset, but journaled before the 
truncate entry) the master ends up without the record while the replay brought 
it back, so the record the user dropped reappeared on every other frontend and 
after a restart.
   
   Fix: the truncate entry now carries what the truncate did to the record, and 
the replay applies exactly that instead of guessing.
   
   * `AnalysisManager.resetTableStats()` reports whether it had to create the 
record.
   * `InternalCatalog.truncateTable()` writes that into the new 
`TruncateTableInfo.tableStatsRecordCreated`.
   * `InternalCatalog.replayTruncateTable()` calls the new 
`AnalysisManager.replayResetTableStats(table,recordCreated)`, which resets the 
record when it exists and creates it only when the truncate created it. A 
truncate entry can therefore no longer resurrect a record which another 
journaled transition removed, while a table which had no record still gets one 
on every frontend, so the rows loaded after the truncation stay accounted for 
(an entry written before this change carries no flag and only resets an 
existing record).
   
   Deterministic replay-order test, as you asked: 
`AnalysisManagerTest.testReplayOfTruncateFollowsTheRecordedStatsTransition` 
applies the deletion first and then replays truncate entries for the three 
cases (entry which did not create the record -> the record stays absent; entry 
which created it -> the record exists with `updatedRows = 0`; entry which did 
not create it on a table without a record -> no record).
   
   One residual, stated explicitly rather than left implicit: the transitions 
are still not *serialized* with each other. The two `(mutate record, write 
journal entry)` pairs of a truncate and of a `DROP STATS` are not protected by 
a common lock, so there remains an interleaving where their apply order and 
their journal order disagree — it now only requires the record to be absent 
when the truncate creates it and the concurrent deletion to be applied after 
that creation but journaled before the truncate entry, and in that case the 
master ends absent while the replay creates the record. Closing that properly 
means coordinating both pairs (writing the deletion entry under the same lock 
as the record removal, and the truncate entry under the same lock as the 
reset), which touches the deletion paths of `DROP STATS`/analyzer/cleaner and 
their journaling; I would rather do that as a separate change than fold it into 
this one, and I am happy to send it if you want it here.



-- 
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]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to