feat(storage): data-failure policy — fail visits on constraint violations (crosslink #46) - #1203
Draft
vringar wants to merge 1 commit into
Draft
feat(storage): data-failure policy — fail visits on constraint violations (crosslink #46)#1203vringar wants to merge 1 commit into
vringar wants to merge 1 commit into
Conversation
Contributor
Author
|
Sub-decision resolved (maintainer): partial-keep confirmed for the Arrow path. When one table's record trips the schema mid-visit, keep the already-validated batches from the other tables and mark the visit incomplete — no all-or-nothing rollback. Rationale: the first postprocessing step routinely drops every record associated with a failed visit_id, so the partial data never pollutes analysis — but it stays useful for debugging why the visit failed. |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## fix/storage-forward-progress #1203 +/- ##
================================================================
- Coverage 62.72% 60.23% -2.50%
================================================================
Files 40 41 +1
Lines 3965 4197 +232
================================================================
+ Hits 2487 2528 +41
- Misses 1478 1669 +191 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
vringar
force-pushed
the
fix/storage-forward-progress
branch
from
July 20, 2026 22:54
e815b75 to
a73b4a8
Compare
vringar
force-pushed
the
feat/data-failure-policy
branch
from
July 20, 2026 22:54
13d662c to
18ff2d2
Compare
vringar
force-pushed
the
fix/storage-forward-progress
branch
from
July 21, 2026 09:45
a73b4a8 to
1dba4a9
Compare
…ions Implements the data-failure policy (crosslink #46) in the StorageController and storage providers, building on the forward-progress fix. Classifies a record that trips the storage schema as a CONSTRAINT VIOLATION (a data fault) and a write/flush blip as a TRANSIENT error (an infra fault), and handles them differently: - Constraint violation: the record could not be stored because it tripped the schema (DB IntegrityError / type mismatch / NOT-NULL / PK / unknown table/column / ArrowInvalid). This is caught, a clear investigable error is logged (visit_id, table, which constraint, record key context), and the owning VISIT is marked FAILED and recorded in incomplete_visits. It is never silently dropped and never retried (a malformed record cannot succeed on retry); the site is surfaced by repeatedly failing with a good error. - Transient error: a write/flush blip is retried, per the existing forward-progress fix. New errors.ConstraintViolation carries table/visit_id/reason. SQLiteStorage provider and ArrowProvider raise it instead of silently dropping; the StorageController isolates per-record failures at the store-task boundary so: (1) no started visit is ever stranded — it always reaches a terminal state (failed counts); (2) no callback-bearing CommandSequence hangs — finalize never aborts and the completion queue is always drained; (3) one bad record can no longer tear down the SHARED DataSocket connection, so the rest of the crawl survives (adversarial G1b); (4) nothing is silently dropped. A permanent flush failure on shutdown surfaces the real error (not a masked timeout) and still drains every pending visit as failed so shutdown neither hangs nor strands a visit (adversarial G2). Brings in the adversarial G1/G1b/G2 scenarios as PASSING tests (previously xfail in the adversarial suite) plus a constraint-violation test and the wire-protocol socket suite; keeps the forward-progress liveness tests green.
vringar
force-pushed
the
feat/data-failure-policy
branch
from
July 21, 2026 10:07
18ff2d2 to
8417fc9
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Data-failure policy (crosslink #46)
Implements the data-failure policy in the core
StorageController/ providerpath. This is the keystone that resolves the adversarial G1 / G1b / G2
robustness gaps and aligns the in-flight storage work.
The policy
A failed
store_record/ finalize is classified into two failure classes:IntegrityError, type mismatch, NOT-NULL / PK / UNIQUE, unknown table/column, parquetArrowInvalidincomplete_visits). Never silently dropped, never retried (a malformed record can't succeed on retry). The site surfaces by repeatedly failing with a good error message.PutObjectfailure)What changed
openwpm/errors.py— newConstraintViolation(message, *, table, visit_id, reason): the typed data-fault signal carrying enough context to investigate.openwpm/storage/sql_provider.py—store_recordnow raisesConstraintViolationon schema-tripping errors (IntegrityError/ProgrammingError/InterfaceError, and the schema-mismatch subset ofOperationalError— "no such table/column"), instead of logging "Unsupported record" and silently dropping. Genuinely transientOperationalErrors ("database is locked", "disk I/O error") are re-raised for the retry path.openwpm/storage/arrow_storage.py—_create_batchraisesConstraintViolationonpa.lib.ArrowInvalidinstead of silently dropping the batch.openwpm/storage/storage_controller.py:_guarded_store_recordisolates every per-record store at the task boundary: aConstraintViolation(or any unexpected exception) is logged with a clear, investigable message and recorded infailed_visits; it never re-raises into the connection handler or the finalize path.finalize_visit_idforces the visit's effective success toFalseif any of its records tripped the schema (even if the browser sentsuccess=True), and handles aConstraintViolationsurfaced lazily by the Arrow provider at finalize time — re-finalizing the visit as interrupted so it still reaches a FAILED terminal state.How the invariants now hold
The four non-negotiables (and the adversarial invariants) hold:
finalize_visit_id, so the visit is always enqueued (as failed). (G1:test_raising_store_record_visit_still_finalizes,..._unfinalized_visit_enqueued_on_shutdown)CommandSequenceblocked on a failed record always gets a terminal result. A permanent write fault no longer leaves a finalize token unresolved and blocking shutdown. (G2:test_permanent_write_table_fault_visit_still_completes)DataSocket— the per-record failure is isolated so the connection (and the rest of the crawl) survives one bad record. (G1b:test_raising_store_record_does_not_break_shared_connection)incomplete_visits. (test_constraint_violation_fails_visit_and_records_incomplete)Tests
Brings the relevant adversarial scenarios in as passing tests (they were
xfail(strict=True)in the adversarial suite — under this policy they now pass):test/storage/test_adversarial_storage_controller.py— G1, G1b, G2, a dedicated constraint-violation test, plus the surviving S4/S6 scenarios.test/storage/test_adversarial_socket.py— wire-protocol hostility (S3).test/storage/conftest.py—adversarial_mp_loggerfixture (errors are expected).The existing forward-progress liveness tests (
test_storage_controller_liveness.py) stay green, as dotest_storage_controller.py,test_storage_providers.py, andtest_arrow_cache.py.mypyandpre-commitpass.Cross-PR coordination
test/adversarial-robustness): the fourxfail(strict=True)G1/G1b/G2 tests flip to PASS under this policy. That suite should rebase onto / be superseded by this PR — its xfail markers must be removed (they would XPASS-fail otherwise).store_recordneeds the same constraint→fail-visit handling (raiseConstraintViolationon DB integrity/schema errors). Its provider isn't on this base, so that's a follow-up on its own branch.Open sub-decision for a maintainer
On the Arrow path, a constraint violation that hits one of several tables in a visit keeps the already-validated batches from the other tables (partial-keep) and still records the visit as incomplete. This is the non-destructive default (don't roll back valid data). If a failed visit should instead be all-or-nothing (roll back every batch for that visit), that's a deliberate change — flagging it rather than guessing.