Skip to content

Blob × replication regression tests (Tier 1: deterministic cluster integration) #411

Description

@kriszyp

Motivation

A cluster of production incidents on the JJill preprod Fabric cluster (and others) all share one shape that our test suite does not cover: blobs × TTL/eviction × replication/base-copy × failure-and-recovery. Each was found in the field, not CI:

Today integrationTests/cluster has blob fault-injection fixtures (fixture-blob-fail-injector, fixture-blob-fail-transient, fixture-large-blob-authoritative) driving blobSaveRejectionContainment.test.mjs. That proves containment (a failed inbound save doesn't crash the receiver). It never asserts convergence or no-orphan. This issue closes that gap with deterministic, per-PR regression tests; harper-pro#412 covers the randomized chaos/stress tier.

The invariant oracle (shared by both tiers)

After an op sequence + fault injection, quiesce, then on every node assert:

  1. No orphaned references — for every record carrying a blob, the blob is readable and its content-hash equals the source's.
  2. Convergence / liveness — all nodes reach an identical (primaryKey → blobHash) set within a bound; cluster_status shows blobReplicationFailures stabilized and the resume cursor advancing (never permanently pinned).
  3. Bounded orphan files — leaked blob files (the safe direction of #1364: a skipped unlink) are reclaimed by cleanupOrphans; blob-store file count does not grow unbounded across cycles.
  4. Isolation — system/deploy replication stays live while a data-DB blob copy is backpressured/wedged.

A small shared assertion helper implementing (1)–(4) should be the first deliverable; every scenario below ends by calling it.

Scope — deterministic cluster integration tests (one per failure mode)

Small, seeded, fixed fault points (in the style of the existing .test.mjs). Extend the injector as needed beyond receive-side write-fail to also: skip the blob transfer entirely (commit metadata, never send the blob), delete a blob file out from under a live record, and drop blob chunks mid-stream.

  • Shared invariant-oracle helper (items 1–4 above), usable from cluster tests.
  • Base-copy past a missing blob (the JJill wedge, highest value): create records whose blobs are missing everywhere, trigger a full base-copy (node restart/join), assert the copy advances past them and the cluster converges — does not stall.
  • TTL-orphan race (#1364 Path 1): write short-TTL blob records on an edge node, fail the transfer to the leader, let TTL evict on the edge, trigger a copy → assert convergence + no surviving orphaned reference.
  • Expiration force-commit (#1364 Path 2): short TTL + volume sufficient to trip the long-transaction force-commit (storage.debugLongTransactions: true to observe) → assert blob files are not unlinked under records that still exist.
  • Resume cursor not pinned (Blob replication wedges permanently when source blob is gone (ENOENT) on an expiration cache table — held resume cursor never recovers #403): inject repeated source-missing-blob ENOENT → assert the cursor advances past (loud) and transient failures hold-then-recover.
  • Deploy-under-backpressure (the JJill coupling): wedge/backpressure a data-DB blob copy, then deploy_component → assert it still replicates to peers (system-table replication isolated from data-DB blob backpressure).

Where it lives / reuse

  • harper-pro/integrationTests/cluster/ — multi-node harness + the existing blob fixtures.
  • Extend fixture-blob-fail-injector with the new injection modes (env-toggled, same pattern).
  • Engines: parameterize across RocksDB and LMDB where feasible (the orphan paths differ subtly by engine).

Acceptance criteria

Related: HarperFast/harper#1364, HarperFast/harper#1353, HarperFast/harper#1369, harper-pro#409, harper-pro#403. Chaos/stress tier: harper-pro#412.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Fields

    Priority

    P2

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions