Repository navigation
Description
Description
RecalculateAndUpdateStats (period 1 s) recalculates the statistics of a table after a "major" change. Once more than updateStatsDropLRUThreshold (default 1) major changes have accumulated, it calls ALRUMap.forceRemoveAllLRU(100). In 6.x the task is registered only in dev mode, so it affects every performance measurement on a dev-mode server (for example, one started from the IDE). In master the task has been registered in every mode since 07ea181 (2025-09-30, #1518), so in 7.0 the same will happen on production servers.
A materialized table that is normally empty is enough to trigger it, if creating a document adds rows to it and deleting the document removes them. Creating and then deleting one 20-line document gives two major changes (0 → 20, then 20 → 0), and all LRU caches of the server are dropped. The next APPLY of any document then rebuilds them.
Measured on 6.3 (build 6.3-20261104.141624-74), on one copy of an ERP database (50 GB), with the same sequence of steps through /eval. Dev mode was compared with -Dlsfusion.server.devmode=false; memory is the allocation of the APPLY thread.
| Step | dev mode (task on) | no dev mode (task off) |
|---|---|---|
| APPLY creating a 20-line document right after a 20-line document was deleted | 3.05 GB, 5.7 s | 0.27 GB, 1.6 s |
| the next delete of a 20-line document | 3.1–3.3 GB | 0.68–0.80 GB |
| explain node of the first stored property written in that APPLY (here the numerator) | 806–821 MB, 0.9–1.3 s | 0.5 MB |
the same after Service.dropLRU with 100 % (control, no dev mode) |
— | 820 MB, 1.0 s; the next APPLY 0.5 MB |
What triggers the drop and what does not:
- Deleting a 1- or 4-line document is not a major change and drops nothing. Deleting a 20- or 100-line document does.
- In a numbered document the first stored write is the numerator, so the cost shows up on the numerator write. If the number is entered by hand, the next stored property written in that APPLY pays the same ~0.86 GB.
Reason
- The counter counts recalculations, not the distance from the statistics the caches were built on. A table that goes 0 → 20 → 0 ends with the same estimate as before, but this still counts as two major changes and triggers the drop.
- The drop also removes caches whose result does not depend on statistics. The ~0.8 GB node above is the rebuild of
getOrderMapSingleApplyDependsandgetDataChangeEvents. Both are@IdentityLazy, so they live incommonLruCache.getDataChangeEventsis the set of data properties that have events. It is purely structural.- The dependency graph depends on statistics only through
OldProperty.noUsePrevHeur→Property.hasAlotKeys, which compares the interface statistics withALOT.reduce(2)(degree 5000). The statistics of real data never come close: 10^12 rows is degree ~17. So, as far as we can see, the graph is the same before and after any statistics update. - In the explain of that node, 385 MB of 816 MB are 584 calls of
getInterfaceCostStat(thehasAlotKeyschecks). The rest is the graph walk.
- The drop is not logged, either by the task or by
Service.updateStats. It shows up only as an unexplained slow APPLY, andlru.loglogs only the memory tuner.
Suggested changes
- Count a table or class as changed only when its estimate has moved away from its estimate at the last drop — by
majorStatChangeDegree— rather than on every recalculation. Alternatively, ignore changes where both the old and the new row counts are below a small threshold, for examplestatDegree ^ majorStatChangeDegree= 25 rows. Either way, creating and deleting the same document no longer drops anything. - Keep the structural caches out of
forceRemoveAllLRU:getDataChangeEvents,getOrderMapSingleApplyDepends, or at least thehasAlotKeysresults. Alternatively, rebuild them only when ahasAlotKeysresult can change. This removes ~0.8 GB and ~1 s from the first APPLY after every drop, including the drops made byService.updateStats. - Log every drop by statistics (for example to
lru.log), with the tables and classes and their old and new estimates.
Metadata
Metadata
Assignees
Labels
Type
Fields
Priority
Activity