cleanup#
Data lifecycle cleanup: tombstone receipts, consumption checks, and the cleanup() and compact() entry points.
The on-disk state of downstream outputs is the consumption ledger: every decision here is recomputed from disk (plus per-file receipts), so sequential notebook runs, driver scripts, and parallel cluster jobs reach the same conclusions from the same evidence.
An output with retention ‘until_consumed’ may be deleted iff every consumer’s output passes its completeness check. Deletion leaves a tombstone receipt (.consumed.json) beside the would-be output path so skip-if-exists logic can honor the deliberate deletion without re-ingesting.
Classes#
Exclusive lock file under the data root for destructive operations. |
Functions#
|
True when running under an orchestrator (e.g. Snakemake). |
|
Path of the tombstone receipt for an output file or directory. |
|
Atomically write a tombstone receipt beside an output path. |
|
Return the receipt dict for an output path, or None if absent/corrupt. |
|
Remove the receipt for an output path (tolerant of it being absent). |
|
True when a recipe output physically exists and passes its checks. |
|
True when the output exists on disk OR a valid receipt stands in. |
|
True when a tombstone receipt justifies skipping regeneration. |
|
Reclaim consumed intermediate outputs upstream of a terminal recipe. |
|
Reclaim the consumed direct inputs of one recipe for one admin unit. |
|
Garbage-collect the data buckets: report and optionally delete. |
Module Contents#
- openplaces.io.cleanup.is_orchestrated() bool#
True when running under an orchestrator (e.g. Snakemake).
Orchestrated runs must produce the physical output file, so receipt-based skips are voided.
- openplaces.io.cleanup.receipt_path(output_path) pathlib.Path#
Path of the tombstone receipt for an output file or directory.
- openplaces.io.cleanup.write_receipt(output_path, receipt: dict) pathlib.Path#
Atomically write a tombstone receipt beside an output path.
- openplaces.io.cleanup.read_receipt(output_path) dict | None#
Return the receipt dict for an output path, or None if absent/corrupt.
- openplaces.io.cleanup.discard_receipt(output_path) None#
Remove the receipt for an output path (tolerant of it being absent).
- openplaces.io.cleanup.is_output_complete(recipe, admin_id, required_partitions=None) bool#
True when a recipe output physically exists and passes its checks.
plain parquet: exists AND the footer is readable AND registry-known columns pass the (schema-only) dtype check
aggregated/partitioned parquet with a coverage footer: additionally, the recorded coverage is a superset of required_partitions
enrich evidence: coverage covers all sub-admin units, or the COVERAGE_ALL sentinel
- Parameters:
recipe (str or dict) – Recipe ID or loaded recipe dictionary.
admin_id (str or AdminId or None) – Admin unit of the output.
required_partitions (iterable of str, optional) – Partition or sub-admin IDs that the output’s coverage footer must include (ignored when the footer is absent or COVERAGE_ALL).
- openplaces.io.cleanup.output_conceptually_exists(recipe, admin_id) bool#
True when the output exists on disk OR a valid receipt stands in.
The receipt cascade rule (design section 4.3): a consumer that was itself cleaned up still counts as existing, so receipts of its inputs stay valid and nothing is needlessly re-ingested.
- openplaces.io.cleanup.receipt_justifies_skip(recipe, admin_id, orchestrated=None) bool#
True when a tombstone receipt justifies skipping regeneration.
Requires (design section 4.3): retention.cleanup.honor_receipts enabled; not running under an orchestrator; a readable receipt; every recorded consumer output conceptually exists (physically, or via its own receipt); and the consumer set recomputed from the current recipe tree contains no consumer absent from the receipt (a recipe added after the deletion voids the skip).
- class openplaces.io.cleanup.DataLock(admin_id=None, timeout_s=10.0, stale_after_s=3600.0)#
Exclusive lock file under the data root for destructive operations.
A county-scoped lock (.openplaces.<admin_id>.lock) lets cleanups of different counties run concurrently; the global lock (.openplaces.lock) serializes data-root-wide operations like compact(). Stale locks (older than stale_after_s) are taken over.
- openplaces.io.cleanup.cleanup(recipe, admin_ids=None, stages=None, include_images=False, aggressive=False, dry_run=True, verbose=True, force=False) pandas.DataFrame#
Reclaim consumed intermediate outputs upstream of a terminal recipe.
Walks the dependency DAG rooted at recipe, finds outputs with retention ‘until_consumed’ whose consumers are all complete (section 4.2 of the design), deletes them, and writes tombstone receipts. Every decision is recomputed from disk.
- Parameters:
recipe (str or dict) – Terminal recipe the DAG is rooted at (e.g. ‘US_footprint-cheer-2026’).
admin_ids (str or list, optional) – Admin units in scope; None scans the terminal recipe’s outputs on disk.
stages (tuple of str, optional) – Only consider upstream recipes of these stages (e.g. (‘ingest’,) to only reclaim the cache).
include_images (bool) – Image caches are only deleted with this explicit opt-in (or retention.cleanup.include_images in the config); they are always listed in the report.
aggressive (bool) – Additionally treat ‘core’ outputs as until_consumed for this call (kept only until the curated outputs exist). Enrich evidence stays ‘keep’ regardless: its input images may be gone.
dry_run (bool) – Default True: only report. Pass dry_run=False to delete.
verbose (bool) – Print a short summary.
force (bool) – Skip the cluster-queue guard.
- Returns:
Columns: path, bucket, class, action, size_mb, recipe_id, admin_id, blocked_by.
- Return type:
pd.DataFrame
- openplaces.io.cleanup.cleanup_consumed_inputs(recipe, admin_id, include_images=False, verbose=False) pandas.DataFrame#
Reclaim the consumed direct inputs of one recipe for one admin unit.
Backs the stage entrypoints’ cleanup=’consumed’ hook: after a stage finishes an admin unit, each of its direct inputs is deleted iff every consumer in the recipe tree is complete. Safe when called early — consumers with no output yet block deletion (e.g. the NSI parquet survives the footprint-spine hook until the parcel spine also exists). No-op when retention.cleanup.enabled is false.
- Parameters:
recipe (str or dict) – The stage recipe whose inputs to consider.
admin_id (str or AdminId) – The admin unit just finished.
include_images (bool) – Opt-in for image-cache deletion (or retention.cleanup.include_images in the config).
verbose (bool) – Print a one-line summary when something was reclaimed.
- openplaces.io.cleanup.compact(buckets=('cache', 'heap', 'external', 'core', 'out'), recipes=None, admin_ids=None, delete=(), dry_run=True, orphan_min_age_days=14, min_size_mb=0.0, include_shared=False, force=False) pandas.DataFrame#
Garbage-collect the data buckets: report and optionally delete.
Existent-first scan: every file on disk in the requested buckets is parsed for its recipe and admin unit and matched against the active recipe tree, then classified:
‘final’: expected output with retention ‘keep’ (report only)
‘intermediate/needed’: until_consumed with incomplete consumers
‘intermediate/consumed’: until_consumed, all consumers complete; deleted when ‘consumed’ is in delete
‘heap’: anything under the heap; deleted when ‘heap’ is in delete
‘orphan’: matches no recipe in the current tree; deleted only when ‘orphans’ is in delete (guards below)
‘receipt/stale’: receipt whose recorded consumers all vanished; pruned automatically when any deletion is enabled
Orphan-GC guards (all mandatory): compact aborts destructive runs when any recipe fails to parse; refuses orphan deletion when implausibly few recipes loaded; and never touches files matching retention.cleanup.exclude_patterns.
- Parameters:
buckets (tuple of str) – Buckets to scan.
recipes (list of str, optional) – Restrict the recipe tree to these recipe IDs (None = all).
admin_ids (list, optional) – Restrict to files under these admin units.
delete (tuple of str) – Subset of {‘consumed’, ‘heap’, ‘orphans’}; empty = report only.
dry_run (bool) – Must be False IN ADDITION to a non-empty delete for any deletion (two explicit acts).
orphan_min_age_days (int) – A file younger than this is never classified as orphan.
min_size_mb (float) – Drop report rows smaller than this.
include_shared (bool) – Allow orphan deletion in shared buckets (external, share, raw).
force (bool) – Skip the cluster-queue guard.
- Returns:
Columns: path, bucket, class, action, size_mb, recipe_id, admin_id, blocked_by.
- Return type:
pd.DataFrame