englefly opened a new pull request, #68282:
URL: https://github.com/apache/doris/pull/68282

   
   ### What problem does this PR solve?
   
   Issue Number: None (no upstream issue filed; the case is described below)
   
   Problem Summary:
   
   Reproduction (5 rows loaded into a 1 bucket duplicate table, then):
   
       TRUNCATE TABLE t;
       INSERT INTO t VALUES (1,1),(2,2),(3,3);   -- immediately
       EXPLAIN SELECT * FROM t;                  -- immediately
   
   Before this change the scan of `t` is planned with `cardinality=1`, i.e.
   `StatsCalculator.getOlapTableRowCount()` returns -1 even though the table is 
not empty.
   
   Root cause: `getOlapTableRowCount()` has two sources for the row count of a 
table
   
       1. `olapTable.getRowCountForIndex(selectedIndexId, true)`, which returns 
-1 until the
          backends have reported the row count of *every* tablet of the index. 
After TRUNCATE
          TABLE all tablets are new, and the row count loaded right after the 
truncation is not
          in that report either, so this returns -1 within a window of up to
          `tablet_stat_update_interval_second` (60s by default);
       2. `TableStatsMeta.getRowCount(indexId) + 
TableStatsMeta.getBaseIndexDeltaRowCount()`,
          which is the fallback used while (1) is -1.
   
   `InternalCatalog.truncateTable()` removed the `TableStatsMeta` of the 
truncated table. As a
   consequence the fallback did not exist at all, and, more importantly, the 
rows of every load
   performed after the truncation were dropped by 
`AnalysisManager.replayUpdateRowsRecord()`
   (which only accumulates into an existing record), so the row count could not 
be recovered
   even later. The result was -1 with no way back.
   
   Fix:
   
   * `InternalCatalog.truncateTable()` calls 
`AnalysisManager.resetTableStats()` instead of
     `removeTableStats()` for a whole-table truncation. The record is reset to 
the state of an
     empty table (updatedRows = 0, indexesRowCount = 0 for every index, 
partitionUpdateRows and
     the per column stats cleared, partitionChanged and userInjected reset) and 
kept, so that the
     rows loaded afterwards are accumulated and reported as the delta row count 
of the table. A
     record is created when the table does not have one yet. The reset is 
journaled, so the
     followers and the image stay consistent with the master.
   * `TableStatsMeta.getBaseIndexDeltaRowCount()` no longer returns 0 for an 
empty
     `colToColStatsMeta`. An empty map means no baseline has ever been 
collected, which is
     exactly the state of a reset record, so all the rows of `updatedRows` are 
the delta. Without
     this, the delta of a reset record would have been `updatedRows - <row 
count captured by the
     analysis before the truncation>`, a negative number. The `userInjected` 
guard is kept, which
     is the only way an empty map was reached before (a record created by
     `ALTER TABLE ... SET STATS` has empty job columns).
   * `SHOW TABLE STATS` now prints an empty timestamp instead of `1970-01-01 
08:00:00` for a
     record which was never analyzed, which is now possible for a table 
truncated before being
     analyzed.
   
   TRUNCATE TABLE still drops the collected statistics themselves (the column 
stats rows and the
   in-memory cache), only the record is kept; the `columns` column of `SHOW 
TABLE STATS` is
   therefore `[]` instead of empty, and the existing 
`test_drop_stats_and_truncate` case is
   updated accordingly.
   
   End to end result of the reproduction above (manual test on a real cluster, 
single BE,
   `EXPLAIN` prints the cardinality of the scan):
   
       before: cardinality=1   (getOlapTableRowCount() == -1)
       after:  cardinality=3
   
   The same holds when the table was never analyzed before the truncation
   (`TRUNCATE TABLE` + load 4 rows -> `cardinality=4`).
   
   ### Release note
   
   The row count of a table is no longer reported as unknown (-1) after 
`TRUNCATE TABLE`
   followed by an immediate load and query.
   
   ### Check List (For Author)
   
   - Test: Regression test 
(`regression-test/suites/statistics/test_row_count_after_truncate.groovy`,
     run against a local cluster: passed), updated 
`test_drop_stats_and_truncate.groovy`
     (passed), Unit test (`TableStatsMetaTest`, 4 tests, passed), Manual test 
on a local
     FE + BE cluster for the before/after numbers above.
   - Behavior changed: Yes. `TRUNCATE TABLE` keeps the table stats record 
instead of deleting
     it, so `SHOW TABLE STATS` shows a record with `columns=[]` after a 
truncation, and the
     row count of the rows loaded after the truncation is reported to the 
optimizer instead of
     -1. Column statistics are still dropped as before.
   - Does this need documentation: No
   


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