Skip to content

phase 6a - replay functionality [source cache-scoped] - #1045

Open
kans wants to merge 32 commits into
mainfrom
kans/phase-6a
Open

phase 6a - replay functionality [source cache-scoped]#1045
kans wants to merge 32 commits into
mainfrom
kans/phase-6a

Conversation

@kans

@kans kans commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Summary

Implements Sync Replay Phase 6a for the Pebble-backed dotc1z store, and secondarily, runs the experiment of using a semi-formal verification plan, created independently of the implementation.

This adds:

  • Source-scope stamping for connector-written resources, entitlements, and grants
  • Atomic maintenance of source-scope indexes through typed rawdb mutations
  • Durable source-cache manifests
  • Scope- and row-kind-isolated replay from a previous artifact
  • Retry-safe, bounded-batch replay and scoped tombstones
  • Pure-replay replacement semantics
  • Canonical and principal tombstones
  • Reset, cleanup, clone, reopen, and capability integration
  • Forward cacheability so replay output can serve as a subsequent replay source

Verification record

This PR was developed and tested against an implementation-blind verification plan.

The plan defines C01–C43, their oracles, coverage levels, failure models, and closure requirements. The evidence record states exactly which criteria are verified, sampled, incomplete, excluded, or deferred.

Verification-driven fixes

The verification work found and corrected defects involving:

  • Missing, wrong-kind, empty-validator, and invalidated manifest handling
  • Source primary/index corruption preflight
  • Occupied-destination replacement
  • Partial tombstone application
  • Malformed-row obligation cleanup
  • Replay from the same handle, path, or filesystem alias
  • Retry, cancellation, and interrupted-commit behavior
  • Timestamp and source-artifact preservation
  • Forward replay cacheability
  • Typed mutation-path atomicity
  • Unbounded scoped principal/resource tombstone batches

Scoped tombstone operations now commit in bounded chunks. If a later chunk fails, they report only committed progress, preserve primary/index agreement, mark the store dirty, and converge on retry.

Change orders

The committed plan records four post-freeze change orders:

  1. Clarified enforcement ownership between the public capability, engine replay, and deferred syncer orchestration.
  2. Documented symmetry and representative reductions instead of claiming a literal P1–P10 Cartesian expansion.
  3. Extended bounded-memory enforcement to scoped tombstone scans.
  4. Assigned manifest row-count optimization to a separate stacked follow-up PR.

Deliberately deferred

This PR does not implement:

  • Syncer/checkpoint orchestration
  • Compatibility matching or gating
  • Connector continuation/RPC behavior
  • Manifest invalidation policy
  • Compacted/non-FULL source eligibility
  • Compactor integration
  • Post-replay ingest-invariant evaluation
  • Scope-count replay optimization

Executable exclusions identify API-boundary cells that Phase 6a cannot represent. An exclusion is not counted as a behavioral pass.

Evidence

Passing gates:

make lint
go test ./pkg/sourcecache ./pkg/dotc1z/engine/pebble ./pkg/dotc1z
go test -race ./pkg/dotc1z ./pkg/dotc1z/engine/pebble -run '^TestVerification' -count=1

@kans
kans requested a review from a team July 29, 2026 04:16
@kans kans changed the title phase 6a - replay functionality [WIP] phase 6a - replay functionality Jul 29, 2026
Comment on lines +508 to +536
var scanned int
for primaries.First(); primaries.Valid(); primaries.Next() {
scanned++
if scanned&0x3FF == 0 {
if err := ctx.Err(); err != nil {
primaries.Close()
return err
}
}
stamp, err := rawdb.ScanSourceScopeKeyRaw(primaries.Value(), scopeField)
if err != nil {
primaries.Close()
return fmt.Errorf("source cache replay: preflight %s primary %x: %w", rowKind, primaries.Key(), err)
}
if stamp != scopeKey {
continue
}
indexKey, ok := rawdb.AppendBySourceScopeKeyFromPrimary(nil, primaries.Key(), scopeKey)
if !ok {
primaries.Close()
return fmt.Errorf("source cache replay: preflight %s primary %x cannot derive source index", rowKind, primaries.Key())
}
_, closer, err := prev.db.Get(indexKey)
if err != nil {
primaries.Close()
return fmt.Errorf("source cache replay: preflight %s primary %x missing source index: %w", rowKind, primaries.Key(), err)
}
closer.Close()
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Suggestion: This preflight iterates the entire primary keyspace of the row kind in prev on every replay call, skipping only the per-primary Get for non-matching stamps. Since validateReplaySourceScope runs once per scope (via each ReplaySourceCache*), a sync that replays S scopes against a previous file of N rows is O(S·N) — a quadratic cliff for delta-query connectors with many small scopes and a large prior sync. The single-scope benchmark (source_cache_replay_bench_test.go puts all rows under one scope) scales linearly and won't detect this. Per the repo cost-contract criteria, consider a multi-scope benchmark that pins the cost curve, and/or bounding the primary-side pass (e.g. persisting a per-scope row count/digest in the manifest so the index-side scan alone suffices). The code comment already acknowledges this as a known limitation. (Confidence: high on the full-keyspace scan; medium on real-world impact — depends on connector scope granularity.)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will be done in follow up.

@github-actions

github-actions Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

General PR Review: [WIP] phase 6a - replay functionality

Blocking Issues: 0
Suggestions: 0
Threads Resolved: 0

Criteria status: loaded .claude/skills/ci-review.md from trusted base 8c92491eafc5.
Review mode: full
Review run: https://github.com/ConductorOne/baton-sdk/actions/runs/30559396883

Review Summary

Scanned the full PR diff for security and correctness issues, plus the baton-sdk repo-local criteria (exported Go API stability, proto/wire contracts, serialized-state round-tripping, default-behavior stability, and go.mod/go.sum).

  • Proto/wire: source_scope_key field numbers (Resource=12, Entitlement=11, Grant=10) are consistent between records.proto, generated records.pb.go, and the raw protowire scanners (ScanSourceScopeKeyRaw). No renumbering or wire-break.
  • Serialized state: New TypeSourceCache (0x0B) manifest family and by-source-scope secondary indexes (0x09/0x0A/0x0B) are additive; no collision with existing keyspace discriminators.
  • Fast-path proofs: freshGrantsEmpty / freshEntitlementsEmpty / freshResourcesEmpty are disarmed on the replay clear/commit boundaries as required by the derived-state-as-proof risk model.
  • Concurrency: beginSourceCacheMutation holds the store closeMu across the whole engine mutation, serializing against Close.

The one real correctness issue found (a Pebble value-handle leak in PutGrantRecordsIfNewer, pkg/dotc1z/engine/pebble/if_newer.go:77-80, where closer from e.db.Get stays open if marshalRecord errors) is already tracked in existing findings and is pre-existing/unchanged since the last reviewed SHA — not re-posted here. The sibling entitlement/resource IfNewer paths close on all paths.

No new blocking security or correctness issues were found. The replay machinery is extensively self-verified.

Security: None found.
Correctness: None found (see note above re: already-tracked pre-existing leak).

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blocking issues found.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blocking issues found.

Comment thread pkg/dotc1z/engine/pebble/source_cache.go Outdated

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blocking issues found.

Comment thread pkg/dotc1z/engine/pebble/source_cache.go

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blocking issues found.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blocking issues found.

@kans
kans requested a review from mindymo as a code owner July 29, 2026 20:08
Comment on lines +147 to +175
if err != nil {
if errors.Is(err, pebble.ErrNotFound) {
continue
}
return err
}
if _, ok := seen[id]; ok {
continue
}
seen[id] = struct{}{}
identities = append(identities, id)
}
if len(identities) == 0 {
return nil
}

batch := e.db.NewRecordBatch()
defer func() { _ = batch.Close() }()
for _, id := range identities {
key := encodeGrantIdentityKey(id)
oldVal, closer, err := e.db.Get(key)
if errors.Is(err, pebble.ErrNotFound) {
continue
}
if err != nil {
return err
}
if err := batch.StageGrantDelete(key, oldVal); err != nil {
_ = closer.Close()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Suggestion (low confidence): DeleteGrantRecordsBounded (and the parallel DeleteEntitlementRecords) accumulate every resolved identity into an in-memory identities slice + seen map and stage all tombstones into a single RecordBatch committed once. Memory is O(len(externalIDs)), unlike the replay path which deliberately chunks at replayBatchRows (10k). The single atomic commit is a deliberate all-or-nothing tombstone choice and the delete set is asserted to be delta-sized (see DeleteSourceCacheRows doc), so this is likely fine — but it's exactly the "cleanup/tombstone scan builds O(scope size) batch" class this PR added to docs/BUG_CATCHING.md. Worth a bound or a note if a whole-scope tombstone can ever reach here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

follow up work

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blocking issues found.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blocking issues found.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blocking issues found.

Comment thread pkg/dotc1z/engine/pebble/if_newer.go Outdated
Comment thread pkg/dotc1z/engine/pebble/grants.go Outdated
@github-actions

github-actions Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

General PR Review: [WIP] phase 6a - replay functionality

Blocking Issues: 0 | Suggestions: 3 | Threads Resolved: 0
Criteria: Criteria status: loaded .claude/skills/ci-review.md from trusted base 10a6da053799.
Review mode: full
View review run

Review Summary

Full PR diff scanned for security and correctness. This is a HIGH-risk change per the repo triage model: it introduces new durable storage families (by_source_scope indexes, the source-cache manifest keyspace TypeSourceCache=0x0B), a new wire-stamped record field (source_scope_key), and cross-file replay that copies raw values between SDK versions — silent + durable + version-pair-dependent, so failures escape local testing and cost a fleet-wide re-sync. The concrete correctness findings below were already identified in the prior review and remain present in the current tree; no new bugs were found, and no security issues were found. Recommend the durable-format and replay paths get the escalated instrument coverage the trusted criteria calls for (a two-artifact cross-version replay harness plus a golden-artifact corpus for the new index/manifest families), which a single-shot CI review only samples.

Security Issues

None found.

Correctness Issues

None found.

Suggestions

  • pkg/dotc1z/engine/pebble/if_newer.go:77-80 (PutGrantRecordsIfNewer): when getErr == nil, the pebble Get closer leaks if marshalRecord(r) returns an error, since the case-local closer.Close() was removed and only re-added around StageGrantPutInline. Rare error path; PutResourceRecordsIfNewer/PutEntitlementRecordsIfNewer handle it correctly. (previously identified; still present)
  • pkg/dotc1z/engine/pebble/grants.go:277-280 (PutExpandedGrantRecords): same closer-leak shape — marshalRecordAppend error returns while the getErr == nil closer is still held. (previously identified; still present)
  • pkg/dotc1z/engine/pebble/source_cache.go:632-687 (validateReplaySourceScope): the preflight scans the entire previous-file primary family for the row kind on every per-scope replay, so a many-scope delta sync is O(scopes × rows). Acknowledged in-code and deferred to a manifest row-count/digest follow-up; noted for tracking, not blocking. (previously identified)
Prompt for AI agents
Verify each finding against the current code and only fix it if needed.

## Suggestions

In `pkg/dotc1z/engine/pebble/if_newer.go`:
- Around line 77-80 (PutGrantRecordsIfNewer): when the prior Get succeeded
  (getErr == nil), closer is open. If marshalRecord(r) returns an error the
  function returns without calling closer.Close(), leaking the pebble read
  handle. Close closer on the marshal-error return (mirror
  PutResourceRecordsIfNewer, which closes before returning the marshal error).

In `pkg/dotc1z/engine/pebble/grants.go`:
- Around line 277-280 (PutExpandedGrantRecords): same shape — when getErr == nil
  the Get closer is held while marshalRecordAppend(valScratch[:0], r) runs; the
  error return does not close it. Close closer (guard if closer != nil) before
  returning the marshal error.

In `pkg/dotc1z/engine/pebble/source_cache.go`:
- Around line 632-687 (validateReplaySourceScope): the primary-family pass is
  O(all rows of the kind) per scope, making a many-scope delta sync
  O(scopes x rows). Consider persisting a per-scope row count or digest in the
  manifest so the preflight can be bounded by scope size instead of the whole
  primary family. Tracking only; the code comment already defers this.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blocking issues found.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blocking issues found.

Comment thread pkg/dotc1z/engine/pebble/internal/rawdb/records.go
@github-actions

github-actions Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

General PR Review: phase 6a - replay functionality [source cache-scoped]

Blocking Issues: 0 | Suggestions: 10 | Threads Resolved: 0
Criteria: Criteria status: loaded .claude/skills/ci-review.md from trusted base 1cdfd281f9c3.
Review mode: incremental since 38aa990
View review run

Review Summary

The new commit is a test-tiering refactor only: a new internal/testtier helper plus BATON_TEST_EXTRA / BATON_TEST_NIGHTLY guards on ~20 long-running tests, matching Makefile/docs/TESTING.md changes so CI stops timing out. I verified the Make wiring — test-nightly's exported BATON_TEST_NIGHTLY=1 does reach race-check's ./... through the sub-make, so the nightly-gated chaos/soak corpora still run, and none of chaos-check's curated -run list is newly gated into a silent no-op. The full PR diff was also re-scanned for security and correctness: no security findings, no new blocking correctness findings; key encoding, proto/wire compatibility (compacted = 7 is appended, generated .pb.go matches), and old-artifact backward compatibility all check out. The three previously reported findings are still present and unaddressed.

Security Issues

None found.

Correctness Issues

None blocking.

Suggestions

  • pkg/dotc1z/engine/pebble/if_newer.go:77-80 (PutGrantRecordsIfNewer): when getErr == nil, the pebble Get closer leaks if marshalRecord(r) returns an error. (previously identified; still present)
  • pkg/dotc1z/engine/pebble/grants.go:277-280 (PutExpandedGrantRecords): same closer-leak shape — marshalRecordAppend error returns while the getErr == nil closer is still held. (previously identified; still present)
  • pkg/dotc1z/engine/pebble/source_cache.go:632-687 (validateReplaySourceScope): per-scope preflight scans the whole previous-file primary family, so a many-scope delta sync is O(scopes × rows). Deferred to a manifest row-count follow-up. (previously identified)
  • pkg/sync/syncer.go:3796-3798: the WithPreviousSyncC1ZPath doc still promises "the previous-sync c1z may use either engine", but syncer.go:4028 now rejects non-Pebble stores and degrades with a warning.
  • pkg/sync/syncer.go:4044-4059: new hard NewSyncer failure on a previous-sync metadata read error, asymmetric with the adjacent eligibility branch that only warns for the same non-optional caller.
  • pkg/synccompactor/compactor_pebble.go:1136-1144 and :561-570: close errors on already-consumed read-only source stores now abort compaction instead of being logged.
  • pkg/synccompactor/compactor.go:621-624: the syncer.Sync error path returns without syncer.Close(ctx), unlike the three branches added directly below it.
  • pkg/dotc1z/engine/pebble/internal/rawdb/records.go:479-489: stageSourceScopeChange hard-fails on an unparseable prior value while its delete twin at :452-455 degrades and self-heals.
  • pkg/dotc1z/engine/pebble/keys.go:281-284: encodeGrantBySourceScopeIndexKey discards the ok return and can hand back a nil index key.
  • internal/testtier/testtier.go:16-28: the tier gate is != "", so BATON_TEST_EXTRA=0 enables the extra tier.
Prompt for AI agents
Verify each finding against the current code and only fix it if needed.

## Correctness Issues

In `pkg/dotc1z/engine/pebble/if_newer.go`:
- Around line 77-80 (`PutGrantRecordsIfNewer`): on the `getErr == nil` branch the pebble
  Get `closer` is still held when `marshalRecord(r)` returns an error, so the error return
  leaks it. Close the closer before returning the marshal error, matching how
  `PutResourceRecordsIfNewer` and `PutEntitlementRecordsIfNewer` handle the same shape.

In `pkg/dotc1z/engine/pebble/grants.go`:
- Around line 277-280 (`PutExpandedGrantRecords`): same shape — `marshalRecordAppend`
  returns an error while the `getErr == nil` closer is still open. Close it on that error
  path.

## Suggestions

In `pkg/sync/syncer.go`:
- Around line 3796-3798: the `WithPreviousSyncC1ZPath` doc comment says the previous-sync
  c1z "may use either engine", but the new gate at line 4028 requires a Pebble engine and
  warns-and-degrades otherwise. Update the doc to state Pebble-only plus the
  full/uncompacted `UsableAsReplaySource` eligibility requirement.
- Around line 4044-4059: a metadata read error on the previous-sync c1z now hard-fails
  `NewSyncer` for non-optional `WithPreviousSyncC1ZPath` callers, where previously no
  metadata was read and the sync proceeded. The adjacent `run == nil ||
  !UsableAsReplaySource()` branch only warns for the same caller. Make the metadata-error
  branch warn-and-degrade too, reserving hard failure for open failures.

In `pkg/synccompactor/compactor_pebble.go`:
- Around lines 561-570 and 1136-1144: `joinSourceStoreCloseError` now folds `Close` errors
  from read-only, already-consumed source stores into the returned error, turning what was
  a logged warning into a failed compaction. Keep logging these and reserve the join for
  destination-side close errors.

In `pkg/synccompactor/compactor.go`:
- Around line 621-624: `if err := syncer.Sync(ctx); err != nil { return err }` returns
  without closing the syncer, leaking it, while the three branches added at lines 625-639
  all use `errors.Join(err, syncer.Close(ctx))`. Change it to
  `return errors.Join(err, syncer.Close(ctx))`.

In `pkg/dotc1z/engine/pebble/internal/rawdb/records.go`:
- Around line 479-489: `stageSourceScopeChange` returns the
  `ScanSourceScopeKeyRaw(oldVal, field)` error, permanently failing any overwrite of a
  record whose stored prior value is malformed. Its delete twin `stageSourceScopeCleanup`
  (lines 452-455) instead falls back to `deleteAllSourceScopeKeysForPrimary`. Mirror that:
  on scan error call `deleteAllSourceScopeKeysForPrimary(key)` and continue to the
  new-scope `Set` so the row self-heals.

In `pkg/dotc1z/engine/pebble/keys.go`:
- Around line 281-284: `encodeGrantBySourceScopeIndexKey` discards the `ok` result of
  `AppendBySourceScopeKeyFromPrimary` and returns a `nil` key when the splice fails. Either
  return the bool so callers can error, or panic on `!ok`, instead of relying on a
  downstream `len(k) < 3` check.

In `internal/testtier/testtier.go`:
- Around lines 16-28: `RequireExtra` and `RequireNightly` gate on `os.Getenv(...) != ""`,
  so `BATON_TEST_EXTRA=0` or `BATON_TEST_NIGHTLY=false` enables the tier rather than
  disabling it, contradicting the `=1` the skip message and docs/TESTING.md prescribe.
  Compare against `"1"` or parse with `strconv.ParseBool`.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blocking issues found.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blocking issues found.

@kans kans changed the title [WIP] phase 6a - replay functionality phase 6a - replay functionality [source cache-scoped] Jul 31, 2026
Comment on lines +89 to +91
func (r SyncRun) UsableAsReplaySource() bool {
return r.Type == connectorstore.SyncTypeFull && !r.Compacted
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Suggestion: UsableAsReplaySource() gates on !r.Compacted, but the SQLite adapter (getFinishedSync in pkg/dotc1z/sync_runs.go:306-352) never selects or sets Compacted, so it is always false for a SQLite prior run — a compacted SQLite full sync would pass this gate. Impact is currently bounded (the pebble replay path independently re-checks via validateReplaySourceEligible at source_cache.go:129, and replay is pebble-only), but the syncer comment at syncer.go:3735 says a SQLite prior run should work as a replay source. Consider documenting that Compacted is only meaningful for engines that track it, or having the SQLite adapter conservatively derive compaction provenance. (medium confidence)

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blocking issues found.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blocking issues found.

kans and others added 11 commits August 4, 2026 09:53
Pin the corrected implementation SHA and preserve its final lint, race, soak, coverage, and repository-wide gate evidence for independent re-review.

Co-authored-by: Cursor <cursoragent@cursor.com>
Preserve run-file ownership when source cleanup fails and make the remaining mutation, coverage, and API-boundary limitations explicit.

Co-authored-by: Cursor <cursoragent@cursor.com>
Pin CO-012a and preserve the repeated repository, race, soak, and structural-profile evidence before the final independent review.

Co-authored-by: Cursor <cursoragent@cursor.com>
Record both independent acceptance verdicts and retain an executable removal-error branch test for the final ownership obligation.

Co-authored-by: Cursor <cursoragent@cursor.com>
…ing a compacted sync. ensure fold doesnt expolode
Pin the reviewed remediation and preserve the explicit verification limitations so the closure claim is auditable.

Co-authored-by: Cursor <cursoragent@cursor.com>
Keep the mechanical registry portable so Windows does not report registered commit sites as missing.

Co-authored-by: Cursor <cursoragent@cursor.com>

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blocking issues found.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blocking issues found.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blocking issues found.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blocking issues found.

Comment thread Makefile
test-extra: race-check compat-check interrupt-check fuzz-smoke differential-check bench-smoke ## Run bounded confidence checks omitted from CI.

.PHONY: test-nightly
test-nightly: export BATON_TEST_NIGHTLY=1

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Suggestion: neither new tier has an automated runner. .github/workflows/ci.yaml:39 and main.yaml:56 invoke bare go test ./... with neither BATON_TEST_EXTRA nor BATON_TEST_NIGHTLY set, and there is no schedule:/workflow_dispatch: workflow that calls make test-extra or make test-nightly. As a result the 21 newly gated tests — the full deterministic chaos corpora, scheduler soak, WAL-checkpoint race, C1Z integrity, the randomized source-cache lifecycle model, checkpoint-cut enumeration, exhaustive parallel-queue interleavings, and both differential suites — now run only when a human types a Make target, which is coverage removal rather than relocation. Consider adding a nightly scheduled job that runs make test-nightly.

Comment on lines +15 to +29
func RequireExtra(t testing.TB) {
t.Helper()
if os.Getenv(ExtraEnv) == "" && os.Getenv(NightlyEnv) == "" {
t.Skipf("set %s=1 or run the corresponding Make target", ExtraEnv)
}
}

// RequireNightly skips a randomized, repeated, or full-corpus test unless the
// nightly confidence suite explicitly enabled it.
func RequireNightly(t testing.TB) {
t.Helper()
if os.Getenv(NightlyEnv) == "" {
t.Skipf("set %s=1 or run make test-nightly", NightlyEnv)
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Suggestion: both gates test os.Getenv(...) == "", so BATON_TEST_EXTRA=0 (or BATON_TEST_NIGHTLY=false) enables the tier. docs/TESTING.md documents the contract as =1, so a CI matrix or shell profile that sets the variable to 0 in order to opt out would silently opt in instead. Consider accepting only truthy values (an explicit == "1", or strconv.ParseBool on a non-empty value).

// C10/C12: the replay commit seam supplies deterministic evidence that live
// batch cardinality is fixed, and lets retry be cut after one landed chunk.
func TestVerificationReplayBatchBoundAndInterruptedRetry(t *testing.T) {
testtier.RequireExtra(t)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Suggestion: require.Equal(t, replayBatchRows, highWater) on line 57 is the only assertion anywhere that the live replay path actually honors the production replayBatchRows = 10_000 bound — TestVerificationReplayCommittedPrefixRetryAllKinds lowers the seam to 2, and source_scope_verification_test.go:1113 only exercises validateBatchHighWater in isolation. Gating this test to the extra tier therefore removes CI's only guard against a regression that drops or changes the production batch bound on the new durable replay path. Consider splitting the cheap bound assertion into a CI-tier test and leaving only the 10k-row fixture behind RequireExtra.

Comment thread pkg/sync/syncer.go
Comment on lines +4044 to +4059
run, metaErr := previousSyncStore.SyncMeta().LatestFinishedSyncOfAnyType(ctx)
if metaErr != nil {
closeErr := previousSyncStore.Close(ctx)
if s.previousSyncC1ZPathOptional {
ctxzap.Extract(ctx).Warn("previous-sync c1z metadata unusable; syncing without source-cache replay",
zap.String("previous_sync_c1z_path", s.previousSyncC1ZPath),
zap.Error(errors.Join(metaErr, closeErr)),
)
break
}
return nil, fmt.Errorf(
"error reading previous-sync c1z %q metadata: %w",
s.previousSyncC1ZPath,
errors.Join(metaErr, closeErr),
)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Suggestion: this makes NewSyncer hard-fail for WithPreviousSyncC1ZPath callers on a metadata read error, where before this PR no metadata was read at all and the sync proceeded. Note the asymmetry with the branch immediately below — run == nil || !UsableAsReplaySource() only warns and degrades for the same non-optional caller. Since previousSyncReader is currently unconsumed scaffolding (the ETag read path is still t.Skipped in pebble_etag_replay_test.go), this only adds a new way for construction to fail; consider warn-and-degrade here too, reserving hard failure for open failures as the option doc describes. (confidence: medium)

Comment on lines +479 to +489
if mayExist {
oldScope, err := ScanSourceScopeKeyRaw(oldVal, field)
if err != nil {
return err
}
if oldScope != "" && oldScope != newScope {
if err := rb.deleteSourceScopeKey(key, oldScope); err != nil {
return err
}
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Suggestion: this returns the ScanSourceScopeKeyRaw(oldVal, …) error and fails the whole put, while the delete twin stageSourceScopeCleanup (records.go:452-455) deliberately degrades to deleteAllSourceScopeKeysForPrimary. Net effect: a record whose stored prior value is malformed can never be overwritten (permanent failure) but can be deleted (self-heals) — and before this PR the put path did not parse oldVal at all for grants/entitlements, so overwriting a bad row succeeded. Consider mirroring the cleanup path: on scan error fall back to deleteAllSourceScopeKeysForPrimary(key) and continue to the new-scope Set. (confidence: medium)

Comment on lines +281 to +284
func encodeGrantBySourceScopeIndexKey(scopeKey string, id grantIdentity) []byte {
key, _ := rawdb.AppendBySourceScopeKeyFromPrimary(nil, encodeGrantIdentityKey(id), scopeKey)
return key
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Suggestion: the ok return from AppendBySourceScopeKeyFromPrimary is discarded, so a grant identity that fails to splice yields a nil key that flows on as a real index key. Every other splice site in this PR (setByPrincipalKey, deleteSourceScopeKey, stageSourceScopeChange) turns !ok into an error. Either propagate the bool or panic on !ok, rather than relying on downstream len(k) < 3 checks to catch it. (confidence: medium)

Comment on lines +1139 to 1144
closeErr := w.Close(ctx)
if closeErr != nil {
l.Error("compactPebble: error closing source store", zap.Error(closeErr), zap.String("file", sourcePath))
}
if err := joinSourceStoreCloseError(selectErr, closeErr, sourcePath); err != nil {
return err

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Suggestion: source stores here are read-only inputs that have already been fully consumed (SourceFile carries only Path/SyncID/Stats). Joining their Close error into the return — same at compactor_pebble.go:561-570 — changes previously log-only behavior into a hard compaction failure, so a transient temp-dir/unlink hiccup can now fail an otherwise correct compaction. Consider keeping these logged and reserving the join for destination-side close errors. (confidence: high that the behavior changed, medium that it matters in practice)

Comment on lines +16 to +21
t.Helper()
if os.Getenv(ExtraEnv) == "" && os.Getenv(NightlyEnv) == "" {
t.Skipf("set %s=1 or run the corresponding Make target", ExtraEnv)
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Suggestion: the gate is != "", so BATON_TEST_EXTRA=0 (or =false) enables the extra tier — the opposite of what the value suggests, and the skip message tells the reader to set =1. Same shape in RequireNightly. Consider comparing against "1" (or parsing with strconv.ParseBool) so an explicit off value turns the tier off. (confidence: high, low impact)

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blocking issues found.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant