Recorded while implementing #10347 and deliberately left undecided by that diff, which says so in a comment at the site.
The shape
LifecycleSchema accepts all three together: retention and ttl are both bounding policies, and the archive.after === retention.maxAge refine is satisfied when the two match. For example:
{class: 'audit',retention: {maxAge: '90d'},ttl: {field: 'expires_at',expireAfter: '30d'},archive: {after: '90d',to: 'datalake'},}reapObject short-circuits into archiveObject for any object declaring archive, so the Archiver's candidate read is the only selection that happens.
So the count of inert keys on this triple is unchanged at one — which one is inert has swapped. #10347's ruling was about the pair ttl + archive and is silent on the triple; the fix implements exactly the ruled option rather than inventing semantics for a shape nobody in this repo declares (censused: sys_audit_log and sys_metadata_audit are the only archive-declaring objects and both are retention + archive, no ttl).
The dispositions, for triage
- Union the two windows — a row is due when either the age bound or the ttl cutoff says so, which is exactly how the Reaper treats two declared policies on a non-archive object (both reaps run, effects union). It costs an
$or in the Archiver's candidate read, issued at a driver's find rather than through the engine, and makes LifecycleSweepEntry.cutoff ambiguous (two cutoffs, one field). - Refuse the triple at parse time, or require
ttl.expireAfter to line up with archive.after the way retention.maxAge must. This narrows the acceptance face (Clause ②) and lives in packages/spec. - Leave it, on the ground that nothing declares the triple and an author who writes both bounds beside
archive most likely means the per-row one. Then say so where an author can read it — the archive.after doc currently promises "rows older than this are copied to the archive datasource before hot deletion", with no hint that a sibling ttl displaces it.
No measured business pull behind any of them: zero objects in this repo, in examples/, or in the docs declare the triple. Filed so the choice is made deliberately rather than inherited from whichever branch ran last.
Recorded while implementing #10347 and deliberately left undecided by that diff, which says so in a comment at the site.
The shape
LifecycleSchemaaccepts all three together:retentionandttlare both bounding policies, and thearchive.after === retention.maxAgerefine is satisfied when the two match. For example:reapObjectshort-circuits intoarchiveObjectfor any object declaringarchive, so the Archiver's candidate read is the only selection that happens.ttlandarchiveparses, but the ttl policy is silently inert —reapObjectreturns intoarchiveObjectbefore the ttl reap runs #10347: the read wascreated_at < now - archive.after, so the age bound (==retention.maxAge) was enforced and the declaredttldid nothing.ttlandarchiveparses, but the ttl policy is silently inert —reapObjectreturns intoarchiveObjectbefore the ttl reap runs #10347: the read isexpires_at < now - ttl.expireAfter, so the declaredttlis enforced and the age bound no longer separately bounds the hot store. A row whoseexpires_atis far in the future stays hot pastretention.maxAge.So the count of inert keys on this triple is unchanged at one — which one is inert has swapped. #10347's ruling was about the pair
ttl+archiveand is silent on the triple; the fix implements exactly the ruled option rather than inventing semantics for a shape nobody in this repo declares (censused:sys_audit_logandsys_metadata_auditare the only archive-declaring objects and both areretention+archive, nottl).The dispositions, for triage
$orin the Archiver's candidate read, issued at a driver'sfindrather than through the engine, and makesLifecycleSweepEntry.cutoffambiguous (two cutoffs, one field).ttl.expireAfterto line up witharchive.afterthe wayretention.maxAgemust. This narrows the acceptance face (Clause ②) and lives inpackages/spec.archivemost likely means the per-row one. Then say so where an author can read it — thearchive.afterdoc currently promises "rows older than this are copied to the archive datasource before hot deletion", with no hint that a siblingttldisplaces it.No measured business pull behind any of them: zero objects in this repo, in
examples/, or in the docs declare the triple. Filed so the choice is made deliberately rather than inherited from whichever branch ran last.