When to Use
Use this skill in market data infrastructure and quantitative data lakes to plan multi-tier storage lifecycles. High-frequency L2/L3 data accumulates faster than any retention budget, and keeping multi-year tick history on HOT NVMe is a large recurring bill for data nobody queries. This engine places each dataset on an age-driven tier ladder, applies the applicable retention period as a safety gate over that ladder, and quantifies the gross monthly cost delta with the cloud-storage caveats that make naive lifecycle rules cost more than they save.
Use it when you need the tiering decision to be auditable — a persisted record of why each dataset moved, or why it deliberately did not.
When NOT to Use
- Not a compliance oracle. It does not determine which retention period
applies to a record. That is a legal determination, supplied as the
regulatory_retention_yearsinput. - Not an executor. It emits recommendations. It does not call AWS, apply lifecycle rules, or delete anything.
- Not a billing forecast. Savings are gross steady-state storage figures; see Prerequisites for what is excluded.
- Not for hot-path query routing. Tier placement here is a cost/retention decision, not a latency-tiering cache policy.
Prerequisites
- Dataset metadata:
dataset_id,data_type,size_gb,age_days(of the newest record),current_tier,regulatory_retention_years. Optionallyobject_countanddays_in_current_tier— without them the engine cannot check small-object or early-deletion exposure and says so innotes. - A retention period determined by compliance for each record type. SEC Rule 17a-4 is not a single 6-year rule: 17a-4(a) imposes 6 years for the records it enumerates, 17a-4(b)(1) imposes 3 years for others including order memoranda, and both require the first two years in an "easily accessible place".
- A storage pricing map. The bundled
TIER_PRICING_USD_PER_GBholds illustrative us-east-1 list prices (HOT_NVME$0.20,WARM_PARQUET_S3$0.023,COLD_GLACIER_INSTANT$0.004,DEEP_ARCHIVE$0.00099 per GB-month). The model uses S3 Standard's first-50 TB band as a flat rate and excludes retrieval fees, data transfer and volume tiering — pass your ownpricing_mapbefore quoting a number to anyone.
Workflow
- Place the dataset on the age ladder — age-driven only; the retention
period never moves a rung:
- $\le 30$ days $\implies$
HOT_NVME - $31$–$365$ days $\implies$
WARM_PARQUET_S3 - $366$–$2555$ days $\implies$
COLD_GLACIER_INSTANT - $> 2555$ days $\implies$
DEEP_ARCHIVE, subject to steps 2 and 3.
- $\le 30$ days $\implies$
- Apply the retention gate.
retention_days = \lceil \text{years} \times 365.25 \rceil(rounded up, so leap-day truncation never expires a record early);retention_expired = age\_days > retention\_days. If retention has not expired, deletion is prohibited. If it has, the dataset is purge-eligible, which is not purge-recommended:PURGEis emitted only when the engine was constructed withpurge_expired_records=Trueand thedata_typeis not a regulated record type. Otherwise the answer isDEEP_ARCHIVEplus a note saying why deletion was withheld. - Apply the easily-accessible floor. No tier requiring a restore job is
recommended for a record younger than
min_instant_access_days(default 730). ADEEP_ARCHIVEtarget inside that window is clamped toCOLD_GLACIER_INSTANT— Deep Archive restores take hours and cannot satisfy 17a-4's "easily accessible place" or 17a-4(j)'s "furnish promptly". - Cost the move, then read the notes. Gross delta is
$\text{Size}{\text{GB}} \times (\text{Price}{\text{current}} - \text{Price}_{\text{target}})$,
plus per-object Glacier metadata (32 KB at the archive rate + 8 KB at S3
Standard rates) when
object_countis given. A positive savings figure next to a small-object or early-deletion warning is frequently a net loss. - Emit and persist. Generate S3 Lifecycle rules and Parquet compaction jobs
from the recommendations; persist the whole
DataRetentionAuditReport,notesandretention_expiredincluded, as the audit trail. Deletion stays a separate, human-approved step.
Full procedure: see
references/workflows.md. Standards reference: seereferences/standards.md. Printable pre-flight checklist: seeassets/checklist.md.
Common Pitfalls
- Deriving the purge boundary from the retention period. If retention is
treated as the last ladder rung, a dataset with
regulatory_retention_years=0becomes "expired" on day zero and is recommended for deletion the moment it ages past WARM — while the documented policy promises a seven-year floor. The ladder must be age-driven and retention must be a gate laid over it. - Archiving records that are still inside the two-year accessible window. A 13-month-old order-memorandum dataset with a 1-year retention period is expired but still inside 17a-4(b)(1)'s "first two years in an easily accessible place". Deep Archive restores take hours; that placement is a compliance failure even though the retention arithmetic looked satisfied.
- Archiving millions of small Parquet parts. Since September 2024, S3 Lifecycle by default refuses to transition objects under 128 KB at all (an object-size filter is required to override it), Standard-IA and Glacier Instant Retrieval bill a 128 KB minimum per object, and Glacier/Deep Archive adds 40 KB of per-object metadata — 32 KB at the archive rate plus 8 KB at full S3 Standard rates. For 10 million objects that 8 KB slice alone is ~76 GB billed at Standard rates, which can exceed the archive storage charge itself. Compact first.
- Ignoring minimum storage durations. Glacier Instant Retrieval has a 90-day minimum and Deep Archive 180 days. A COLD→DEEP hop on day 30, or purging 60 days after archiving, is billed for the remainder. A single lifecycle rule cannot even express a chained transition inside the first class's minimum.
- Quoting gross savings as the bill impact. The savings figure excludes
retrieval fees, transfer, request charges and S3 Standard's volume tiering.
Supply
transition_price_per_1000_requests_usdto get a payback period; a payback longer than the dataset's remaining retention life destroys value. - Letting an unknown tier name through. A
current_tierthe pricing map does not recognise must raise, not silently price at some default — that produces an authoritative-looking savings number from a typo.
Verification
- Instantiate
DataRetentionPolicyEngine(). Input a 100,000 GB dataset atage_days=120inHOT_NVME: expectWARM_PARQUET_S3, $20,000/mo → $2,300/mo ($17,700/mo gross saving). Input a 50,000 GB dataset atage_days=500inWARM_PARQUET_S3: expectCOLD_GLACIER_INSTANT, $1,150/mo → $200/mo. - Safety check: a dataset at
age_days=366withregulatory_retention_years=0.0must recommendCOLD_GLACIER_INSTANT, neverPURGE, even withpurge_expired_records=True. - Safety check: a
TRADE_AUDIT_LOGatage_days=400must never be routed toDEEP_ARCHIVE, whateverdeep_archive_after_daysis configured to. - Run
python -m unittest discover -s skills/data-retention-policy-and-storage-tiering/scripts.