feat(#203-A3): ingestor writes file-bearing assets under ds_<hex> - #437
Conversation
D9 phase 2 / D16. #255 (mount) + #569 (engine path) both re-keyed onto the physical ds_<hex> handle; the ingestor still hard-refused file-bearing categories under PER_INGESTION_TABLES because their assets landed in the shared label tree (STORAGE_PATH/<label>), where a same-label re-ingest would overwrite an earlier dataset's files. This lifts the refusal by moving the file tree onto the same handle. - config: DEST_PATH now keys on a new DEST_TABLE (default = TABLE_NAME label, injectable via set_dest_table). One knob — every file-copy primitive, the text profiler, the dup validator, and the reclaim path already read DEST_PATH off the run config, so they all follow. - base: when per_ingestion_tables, inject the ds_<hex> handle via config.set_dest_table(physical_table_name), so file-bearing assets land in STORAGE_PATH/ds_<hex>/ (matching the scoped mount + get_dataset_path). Remove the file-bearing refusal. - tests: flip the "refuses file-bearing" test to assert the handle injection; add a real-Config DEST_PATH test. Flag off is byte-for-byte unchanged. Unblocks image/detection/segmentation/keypoint datasets under per-ingestion isolation, and the on-HOLD client#490. Full suite 1903 passed; black clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
bugbot run |
The package-change version guard (data-ingestors#420) requires a bump when tracebloc_ingestor/ changes. A3 completes the per-ingestion file path started in 0.8.0 (#408); this is the image client#490 pins. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
bugbot run |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 110d3d8. Configure here.
saqlainsyed007
left a comment
There was a problem hiding this comment.
Verdict: APPROVE.
Reviewed the full-file context, not just the hunks. The change is correct and consistent with the row-store side of RFC-0003 D9 phase 2.
What I verified:
- DEST_PATH repoint —
DEST_TABLEdefaults toTABLE_NAME, so flag-off is byte-for-byte unchanged;set_dest_table(physical_table_name)is the only writer, and every consumer (file_transfercopy primitives,text_profile,duplicate_validator,reclaim_source) readsDEST_PATHoff the same run config, so the single knob genuinely propagates. - Path safety —
physical_table_nameis alwaysds_<uuid4().hex>(hex-only), so no traversal/injection surface is introduced by moving the file tree. - Reclaim —
_reclaim_sourcestill keys the source gate onTABLE_NAME(the CLI's.tracebloc-staging/<label>dir) and only usesDEST_PATHfor the overlap backstop; staging (<label>) and dest (ds_<hex>) don't overlap, so reclaim still fires correctly. - Table lock — stays on the label via
TableLock(self.table_name, …), matching the scope note; concurrency-only, not a data-isolation unit. - Refusal removal — clean:
_FILE_BEARING_CATEGORIESis still used (base.py:547, 1031), no dead import. - Version bump — 0.8.0→0.8.1 is single-sourced in
__init__.pyand parsed bysetup.py:_read_version; nothing else to update. - Tests — real assertions (handle injection via
set_dest_table.assert_called_once_with, and a real-ConfigDEST_PATH repoint asserting label untouched), not vacuous.
Non-blocking observation (no change requested): set_dest_table permanently mutates the shared Config override with no reset path. This is safe because cli/run.py builds one Config → one ingestor per process; if a future caller ever reused one Config across a flag-on then flag-off ingestor, the stale ds_<hex> DEST_TABLE would leak into the flag-off run. Worth a one-line comment noting the single-run-per-config invariant, but not reachable today.
…st (data-ingestors#439) (#440) * fix(#203-A3): reclaim the ds_<hex> file tree on a failed per-ingestion ingest data-ingestors#439 (Bugbot Medium on the #437 promotion): a failed file-bearing ingest under PER_INGESTION_TABLES leaves assets under its unique ds_<hex> handle. Unlike the flag-off label tree — which a same-label re-run overwrites — the retry mints a FRESH handle, so those files are never overwritten and nothing reclaims them: a lasting PVC leak for image-sized payloads. (Dormant in prod today only because the flag defaults off; a blocker to close before enablement.) - file_transfer.reclaim_dest_tree() — guarded, best-effort rmtree of DEST_PATH. SAFETY (opt-in, provable): removes it ONLY when the basename is a ds_<hex> handle (never a user label) AND it resolves to a direct child of STORAGE_PATH; realpath before check + delete; any error logged + swallowed so it can't mask the ingest's original error. Mirrors reclaim_source's safety posture. - base.py failure handler calls it for per-ingestion file-bearing runs (NOT gated on inserted_records — files can land before any row). Flag-off is a safe no-op (the handle guard only matches ds_<hex>). - +4 tests (removes handle tree; leaves flag-off label tree; refuses a non-direct-child handle; no-op on missing dest). SCOPE: this closes the GRACEFUL-failure path. Hard kills (OOMKilled/SIGKILL) bypass the except-branch entirely — those orphaned file trees are the edge-side husk sweep's job (client-runtime, like the DB husk tables); tracked separately. Full suite 1907 passed; black clean. __version__ 0.8.1 -> 0.8.2. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(#439): gate dest-tree reclaim on `not dataset_registered` (Bugbot High) Bugbot: reclaim_dest_tree ran on every graceful per-ingestion file-bearing failure without checking dataset_registered. A LATE failure after send_ingest_summary succeeded (the case the row compensating-delete already guards) would rmtree the REGISTERED dataset's ds_<hex> tree while the backend still points at it — permanent asset loss. - Add `and not dataset_registered` to the reclaim condition, mirroring the row compensating-delete guard: only a genuinely-unregistered failed run's files are reclaimed; a registered dataset's files are never touched. - +2 tests: unregistered failure reclaims the dest tree; late failure after registration does NOT (mutation check — dropping the guard fails it). Full suite green; black clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
|
/fr-pass |
#203 Track A3 — RFC-0003 D9 phase 2 / D16
Today's #255 (scoped mount) and #569 (engine path) both re-keyed dataset access onto the physical
ds_<hex>handle. But the ingestor still hard-refused file-bearing categories underPER_INGESTION_TABLES, because their assets landed in the shared label tree (STORAGE_PATH/<label>) where a same-label re-ingest would overwrite an earlier dataset's files. This PR moves the file tree onto the same handle and lifts the refusal.Change
config.py—DEST_PATHnow keys on a newDEST_TABLE(defaults to theTABLE_NAMElabel, injectable viaset_dest_table). This is the one knob: every file-copy primitive, the text profiler, the duplicate validator, and the source-reclaim path already readDEST_PATHoff the run config, so they all follow automatically.ingestors/base.py— whenper_ingestion_tables, inject theds_<hex>handle:config.set_dest_table(physical_table_name). File-bearing assets now land inSTORAGE_PATH/ds_<hex>/, matching the scoped mount (refactor(P3b): move validator factories into the ModalityRegistry #255) andget_dataset_path(#569). The file-bearing refusal is removed.ConfigDEST_PATHtest. Flag off is byte-for-byte unchanged (DEST_TABLE= label,set_dest_tablenever called).Why it matters
Unlocks image / object-detection / semantic-segmentation / keypoint datasets (the bulk of tracebloc's use-cases) under per-ingestion isolation, and completes the D9-phase-2 file half that #255 + #569 were built to enable. It's the ingestor change the on-HOLD client#490 v0.8.0 image is waiting on.
Scope note
The file table lock intentionally stays keyed on the label — that's pre-existing #408 behavior (the row store already moved to
ds_<hex>while the lock stayed on the label); it's a concurrency guard, not a data-isolation unit, so it's out of scope here.Verified
Full ingestor suite 1903 passed, 1 xfailed;
blackclean.Targets
develop.🤖 Generated with Claude Code
Note
Medium Risk
Changes on-disk layout for file-bearing datasets when PER_INGESTION_TABLES is on; misalignment with scoped mounts or backend path resolution could strand or mis-associate assets, though flag-off behavior is unchanged.
Overview
With PER_INGESTION_TABLES enabled, file-bearing ingests no longer error at construction; instead the run repoints the on-disk file tree from the shared label directory to the same
ds_<hex>handle used for the MySQL row store.Config introduces
DEST_TABLE(defaults to the user label) andset_dest_table, soDEST_PATHbecomesSTORAGE_PATH/<DEST_TABLE>. BaseIngestor callsset_dest_table(physical_table_name)when the flag is on, which pulls file copy, text profiling, duplicate checks, and source reclaim along without separate changes.TABLE_NAMEand flag-off behavior stay as today.Tests now assert handle injection for image-classification under the flag and
DEST_PATHrepointing; package version 0.8.0 → 0.8.1.Reviewed by Cursor Bugbot for commit 110d3d8. Bugbot is set up for automated code reviews on this repo. Configure here.