The retention ladder: how long each granularity of history is kept.
A level says "roll operations up into windows this wide, and keep them this long". The default
ladder is 15 s for an hour, 1 min for a day, 1 h for a week, 1 d for 90 days, 1 mo forever, which
gives per-second restore granularity on today's history and per-month depth on last year's.
What rolling up actually buys is measured and is not what it looks like. Collapsing repeated
operations on the same subject is worth almost nothing below the day level: on a 50,000-user site
with 262,000 subjects and 82,892 writes a day, the coupon-collector expectation for a one-hour
window is 1.00x, for a day 1.07x, and only at monthly rollups does it reach 4.05x. The reason to
compact a fine level is therefore recompression - zstd level 1 to level 19 with a dictionary,
4.28x to 5.86x - and delta re-anchoring, not fewer operations.
Windows in seconds with retention in seconds, coarsest last. A retention of zero means
forever. Each window must be wider than the one below it, or a rollup would produce
segments the level below already covers.
Throws
\InvalidArgumentException
When the ladder is empty, a window is not positive, a retention is negative, or the windows
do not increase.
Returns
mixed
—
fromSettings()
fromSettings(array> $configured) : self
Builds a policy from the module's settings array.
Parameters
array>
$configured
The retention.levels setting.
Returns
self
—
The policy, falling back to the shipped ladder when nothing is configured.
depth()
depth() : int
How many levels the ladder has.
Returns
int
—
The count.
window()
window(int $level) : int
The window width at a level.
Parameters
int
$level
Level number, zero-based.
Throws
\InvalidArgumentException
When the level is not on the ladder.
Returns
int
—
Window in seconds.
retention()
retention(int $level) : int
How long a level's history is kept.
Parameters
int
$level
Level number, zero-based.
Throws
\InvalidArgumentException
When the level is not on the ladder.
Returns
int
—
Retention in seconds; zero means forever.
isPermanent()
isPermanent(int $level) : bool
Whether a level's history is kept indefinitely.
Parameters
int
$level
Level number, zero-based.
Throws
\InvalidArgumentException
When the level is not on the ladder.
Returns
bool
—
TRUE when nothing at this level is ever pruned.
promotes()
promotes(int $level) : int|null
The level a segment is rolled up into.
Parameters
int
$level
Level number, zero-based.
Returns
int|null
—
The next level up, or NULL when this is the coarsest and nothing rolls up out of it.
windowStart()
windowStart(int $second, int $level) : int
The window a moment in time falls into at a level.
Windows are anchored on the epoch rather than on the first segment, so two hosts compacting
the same history independently produce the same window boundaries.
Parameters
int
$second
Unix seconds.
int
$level
Level number, zero-based.
Throws
\InvalidArgumentException
When the level is not on the ladder.
Returns
int
—
The unix second the window starts at.
isDueForRollup()
isDueForRollup(int $second, int $level, int $now) : bool
Whether history at a level is old enough to roll up.
A window that has not closed is still being written to, so rolling it up would produce a
segment the next flush contradicts.
Parameters
int
$second
Unix second the window starts at.
int
$level
Level number, zero-based.
int
$now
Unix seconds now.
Throws
\InvalidArgumentException
When the level is not on the ladder.
Returns
bool
—
TRUE when the window has closed and its retention has elapsed.
cutoff()
cutoff(int $level, int $now) : int|null
The unix second before which a level's history is past its retention.
Parameters
int
$level
Level number, zero-based.
int
$now
Unix seconds now.
Throws
\InvalidArgumentException
When the level is not on the ladder.
Returns
int|null
—
The cutoff, or NULL when this level is kept forever.
levels()
levels() : list<array{window: int, keep: int}>
The whole ladder, for the settings form and the storage explorer.