Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
90 commits
Select commit Hold shift + click to select a range
3022b92
feat(config): choose each layer's implementation at boot
EricAndrechek Sep 25, 2026
fde17ba
docs(config): say coord.backend is reserved; sync the boot-config lists
EricAndrechek Sep 25, 2026
13422da
feat(coord): leases, in-process implementation
EricAndrechek Sep 25, 2026
a82f74b
docs(coord): state what an unfenced sweeper overlap can cost
EricAndrechek Sep 25, 2026
f129d57
docs(config): no backend has a sub-block yet; index backends.go in AG…
EricAndrechek Sep 25, 2026
bb027da
test(mq): one conformance suite for every Broker
EricAndrechek Sep 25, 2026
d913a18
docs(coord): name ErrClosed as RunElected's other exit; changelog files
EricAndrechek Sep 25, 2026
1adc286
test(mq): run the embedded conformance in a test binary of its own
EricAndrechek Sep 25, 2026
80d6c22
docs(mq): keep the per-tenant no-queue case in ErrQueueFull's contract
EricAndrechek Sep 25, 2026
bf11ecc
test(mq): pin the exactly-once failed report through durable deletion
EricAndrechek Sep 25, 2026
ef4153c
docs(api): list the unavailable broker among the request aborts
EricAndrechek Sep 25, 2026
8792d89
test(mq): end delivery only once the pulls are live
EricAndrechek Sep 25, 2026
5a9d1f1
Merge origin/feat/boot-backends into feat/coord-leases
EricAndrechek Sep 25, 2026
e0705f2
feat(coord): coord.backend selects the coordinator
EricAndrechek Sep 25, 2026
3425243
Merge remote-tracking branch 'origin/mq-tenant-streams' into feat/mq-…
EricAndrechek Sep 25, 2026
beab0fd
feat(app): process roles
EricAndrechek Sep 25, 2026
e2434d6
fix(mq): a replay whose connection closed is not caught up
EricAndrechek Sep 25, 2026
5de4fd0
docs(app): scope instance_id and sweeper claims to what ships today
EricAndrechek Sep 25, 2026
aa9a655
Merge remote-tracking branch 'origin/feat/mq-conformance' into feat/m…
EricAndrechek Sep 25, 2026
0aaeddb
feat(mq): a Broker over an external NATS cluster
EricAndrechek Sep 25, 2026
c452cb6
fix(mq): count a replay down, and size the duplicate window to retries
EricAndrechek Sep 25, 2026
c8ad3f0
fix(mq): keep a replay consumer through a slow client's batch
EricAndrechek Sep 25, 2026
2757e64
fix(test): retry removing an embedded broker store in testutil
EricAndrechek Sep 25, 2026
dbd4661
Merge remote-tracking branch 'origin/feat/process-roles' into feat/mq…
EricAndrechek Sep 25, 2026
83ef8d0
feat(app): mq.backend selects embedded or external NATS
EricAndrechek Sep 25, 2026
2bfc9ee
fix(config): refuse credentials in mq.nats.urls; review fixes to docs
EricAndrechek Sep 25, 2026
18d47de
fix(mq): drain partitions a lower N leaves; quiet close
EricAndrechek Sep 25, 2026
6d42f5a
fix(mq): cover a publishing old-N process; name the stream delete
EricAndrechek Sep 25, 2026
d4f7450
feat(mq): coord leases on a NATS KV bucket
EricAndrechek Sep 25, 2026
71fdc07
fix(mq): time lease step-down from the renewal sent; review fixes
EricAndrechek Sep 25, 2026
b29929e
fix(mq): a per-term id in the lease value; lease tests off the unit b…
EricAndrechek Sep 25, 2026
8a9af4b
test(mq): the unit verifier cases skip the lease bucket they do not c…
EricAndrechek Sep 25, 2026
2a2b886
feat(mq): give every tenant a queue of its own (#612)
taitelee Sep 25, 2026
8e8cc73
Merge remote-tracking branch 'origin/main' into feat/mq-nats-topology
EricAndrechek Sep 25, 2026
5570296
Merge remote-tracking branch 'origin/main' into feat/mq-conformance
EricAndrechek Sep 25, 2026
6f2ddb6
Merge remote-tracking branch 'origin/main' into feat/boot-backends
EricAndrechek Sep 25, 2026
184864e
fix(mq): refuse an embedded store it cannot create at once
EricAndrechek Sep 25, 2026
a3381d2
test(mq): run the embedded broker's tests in parallel, without fsync
EricAndrechek Sep 25, 2026
c566f55
test(app): boot without the embedded broker's fsync per write
EricAndrechek Sep 25, 2026
a48ce1f
test(app): a 1ms topology_wait where the outcome cannot change
EricAndrechek Sep 25, 2026
61b643f
fix(mq): create the embedded store at 0700; changelog the fail-fast
EricAndrechek Sep 25, 2026
0e04e3e
docs(changelog): scope the fail-fast store entry to what mkdir catches
EricAndrechek Sep 25, 2026
5ba2bc1
test(mq): keep the streams directory occupied through a failed open
taitelee Sep 25, 2026
5de3e98
docs(mq): an external NATS ack rests on replicas, not an fsync
EricAndrechek Sep 25, 2026
9ed7119
fix(mq): refuse async persist mode on an ingest partition
EricAndrechek Sep 25, 2026
f00b0f4
feat(mq): generate a 15-minute history stream
EricAndrechek Sep 25, 2026
85d5a17
fix(mq): do not warn on a gap window equal to the history's max_age
EricAndrechek Sep 25, 2026
d563fa9
docs(changelog): an equal gap window read as longer, not short
EricAndrechek Sep 25, 2026
b4bd984
Merge origin/feat/boot-backends into feat/coord-leases
EricAndrechek Sep 25, 2026
eaeb52f
Merge origin/feat/coord-leases into feat/process-roles
EricAndrechek Sep 25, 2026
0cbb386
test(mq,app): fit the unit budget; fail fast on an uncreatable store …
EricAndrechek Sep 25, 2026
cb78f78
fix(config): keep an explicit false/0/"" from config.yaml (#632)
EricAndrechek Sep 25, 2026
73c75ee
fix(discovery): jitter the refresh retry backoff (#616)
EricAndrechek Sep 25, 2026
7847c14
fix(mq): count dead letters per table on both brokers
EricAndrechek Sep 25, 2026
50a1170
docs(changelog): list deadletter.go in the external-broker entry
EricAndrechek Sep 26, 2026
77cf4a7
refactor(keyenc): one key escaping, keep '-', DLQ counts per table (#…
EricAndrechek Sep 26, 2026
22d30b0
Merge remote-tracking branch 'origin/main' into feat/mq-conformance
EricAndrechek Sep 26, 2026
6ebfc6f
test(mq): nothing parked is an empty Tables map, never nil
EricAndrechek Sep 26, 2026
9aacb9c
fix(ingest): retry ClickHouse outages instead of dead-lettering (#619)
EricAndrechek Sep 26, 2026
b20ae6e
feat(config): choose each layer's implementation at boot (#618)
EricAndrechek Sep 26, 2026
5329eb1
Merge origin/main into feat/coord-leases
EricAndrechek Sep 26, 2026
b643ba1
Merge feat/coord-leases (synced with main) into feat/process-roles
EricAndrechek Sep 26, 2026
01cd262
docs(app): a bearer token on a worker's ops listener gets 401
EricAndrechek Sep 26, 2026
a1774fb
feat(coord): leases, in-process implementation (#615)
EricAndrechek Sep 26, 2026
1c26d11
Merge origin/main into feat/process-roles
EricAndrechek Sep 26, 2026
d7420f8
fix(api): map ClickHouse query failures by class, not HTTP status (#627)
EricAndrechek Sep 26, 2026
dafea4c
fix(app): ops-auth warning names the nested case; own store in test
EricAndrechek Sep 26, 2026
23ac730
Merge origin/main into feat/process-roles
EricAndrechek Sep 26, 2026
ada4bdd
test(integration): name the roles in TestQueryErrors_ClickHouseDown
EricAndrechek Sep 26, 2026
5b6efe0
feat(app): process roles (#622)
EricAndrechek Sep 26, 2026
ff67ab5
Merge remote-tracking branch 'origin/main' into feat/mq-conformance
EricAndrechek Sep 26, 2026
5004cd2
test(mq): one conformance suite for every Broker (#623)
EricAndrechek Sep 26, 2026
9db8185
feat(cache): shared redis cache, snapshot keys, uncached write pipes …
EricAndrechek Sep 26, 2026
4ff5074
fix(dedupe)!: reserve/commit ids, windowed ingest, retention, dynamod…
EricAndrechek Sep 26, 2026
0de28ad
fix(mq): keep one tenant's failed queue join from ending ingest for a…
taitelee Sep 29, 2026
704176f
Merge feat/coord-nats-kv and fix/mq-external-close-and-shrink into fe…
EricAndrechek Sep 29, 2026
f91ab6f
Merge feat/mq-external-broker into feat/mq-nats-wiring
EricAndrechek Sep 29, 2026
bd1113f
Merge feat/mq-external-broker into feat/mq-nats-topology
EricAndrechek Sep 29, 2026
eab1830
Merge the final head of #622 (feat/process-roles) into feat/mq-nats-t…
EricAndrechek Sep 29, 2026
e395743
Merge the final head of #623 (feat/mq-conformance) into feat/mq-nats-…
EricAndrechek Sep 29, 2026
873c39f
Merge origin/main into feat/mq-nats-topology
EricAndrechek Sep 29, 2026
d896450
fix(config): give the mq.nats defaults to defaults()
EricAndrechek Sep 29, 2026
492fde8
fix(mq): ExternalNATS honours the idempotency key
EricAndrechek Sep 29, 2026
cbd7464
feat(mq): nats duplicate_window covers dedupe.lease; warn on short re…
EricAndrechek Sep 29, 2026
9229f15
docs: the combined backends' claims main's merge left stale
EricAndrechek Sep 29, 2026
5bb6f2d
fix(mq): manifests cover dedupe.lease; review fixes to docs
EricAndrechek Sep 29, 2026
c7311e5
docs(changelog): list the manifests files in the mq.backend: nats entry
EricAndrechek Sep 29, 2026
a058c6c
test(integration): give the NATS tests' hand-built configs a dedupe l…
EricAndrechek Sep 29, 2026
9fefe7c
refactor(config): move the nats blocks and rules into mq_nats.go
EricAndrechek Sep 29, 2026
7fa5e46
refactor(app): move the ops-only listener's wiring into wire_ops.go
EricAndrechek Sep 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/labeler.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@
- any-glob-to-any-file:
- "internal/cache/**"

"area/coord":
- changed-files:
- any-glob-to-any-file:
- "internal/coord/**"

"area/dedupe":
- changed-files:
- any-glob-to-any-file:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ Break one of these knowingly or not at all.

2. **A dedicated `coverage` job applies the consolidated gate, polling — not `needs`-ing — the suites.** Each suite (`unit`, `integration`, `e2e`) runs with `COV_DEFER=1` and uploads a `coverage-<suite>` fragment; the `coverage` job runs `make cov` (merge + every threshold gate) over all three — exactly like local `make ci`'s final step. Keeping it a separate job (not folded into e2e's tail) decouples the gate result from the e2e suite's pass/fail. Crucially it is `needs: changes` **only, not the suites**: a `needs` edge is a *scheduling* barrier — GitHub won't pick up a runner, check out, restore caches, or `pnpm install` until the needed jobs finish — so needing the suites would serialize this job's ~50s of setup onto the critical path after the last suite, for nothing (the setup doesn't depend on their results). Instead it starts at run creation, runs its setup in parallel with the suites, and blocks only at the merge by polling for the three fragments with [`scripts/ci/wait-artifact.sh`](../../scripts/ci/wait-artifact.sh) (fails fast if a producer concluded without producing). Tail on the critical path: ~10s, not ~50s. **The aggregator and `docs-deploy` must keep `coverage` *and* every suite in their `needs`** — the suites directly (a suite failure must red the gate even though `coverage` no longer needs them), and `coverage` (else a coverage-gate failure wouldn't block merge or a prod deploy).

3. **e2e builds its own inputs and mirrors local `make test-e2e`.** It compiles the SDK dist + cover binary itself (`make -j test-e2e`, warm per-suffix cache) rather than waiting on a builder job, and runs the suite exactly as a developer does — one orchestrator, one ClickHouse testcontainer, sequential files. The ClickHouse image pulls in the background while caches restore (also in the integration job).
3. **e2e builds its own inputs and mirrors local `make test-e2e`.** It compiles the SDK dist + cover binary itself (`make -j test-e2e`, warm per-suffix cache) rather than waiting on a builder job, and runs the suite exactly as a developer does — one orchestrator, one ClickHouse and one Redis testcontainer, sequential files. The ClickHouse image pulls in the background while caches restore (also in the integration job).

4. **One change classifier, split into a pure core + a CI wrapper.** The pure allowlist — file list on stdin ⇒ `code`/`docs` — lives in [`scripts/classify-paths.sh`](../../scripts/classify-paths.sh), dependency-free and unit-tested by [`scripts/classify-paths.test.sh`](../../scripts/classify-paths.test.sh) (`make test-classify-paths`, a `verify` leaf) so the allowlists can't silently regress. The `changes` job runs the thin wrapper [`scripts/ci/classify-changes.sh`](../../scripts/ci/classify-changes.sh), which adds the CI-only policy (API file-list fetch + fail-closed: pushes, dispatches, API hiccups ⇒ `code=true`) on top. Keeping the core pure means the local git hooks can share it (`git diff --name-only | scripts/classify-paths.sh`). The `code`/`docs` outputs gate the suites and docs jobs — gate on these, never on workflow-level `paths:` filters, which would orphan the required check (invariant 1).

Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -280,8 +280,8 @@ jobs:
with:
go-cache-suffix: "-e2e-cov"
# `-j` builds the prereqs (build-ts ∥ build-cover) concurrently,
# then runs the orchestrator: ClickHouse testcontainer + the cover
# binary + the SDK vitest suite.
# then runs the orchestrator: ClickHouse and Redis testcontainers +
# the cover binary + the SDK vitest suite.
- name: Build SDK dist + cover binary, run E2E suite
run: make -j "$(nproc)" test-e2e COV_DEFER=1
- name: Upload coverage fragment
Expand Down
52 changes: 51 additions & 1 deletion .testcoverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,15 @@ exclude:
# HTTP assertions). It is imported only from *_test.go files, never from
# production code, so there's nothing meaningful to cover.
- ^internal/testutil/
# The coord conformance suite: test helpers every Coordinator's tests
# run, imported only from *_test.go like testutil.
- ^internal/coord/coordtest/
# internal/mq/mqtest/ is the Broker conformance suite: test code that
# lives outside *_test.go only so each backend's tests can import it.
- ^internal/mq/mqtest/
# internal/mq/natstest/ stands up NATS as an operator deploys it, for
# tests outside internal/mq; test code, like mqtest.
- ^internal/mq/natstest/
- ^tests/
# scripts/ holds Go helpers (cov, orchestrator) that drive the build but
# aren't part of the shipped binary; they show up in `-coverpkg=./...`
Expand All @@ -73,11 +82,52 @@ exclude:
- ^internal/settings/
- ^cmd/wavehouse/validate\.go$
- ^cmd/wavehouse/bootstrap\.go$
# The DynamoDB dedupe backend: the e2e binary runs Pebble dedupe, so
# this file measured 0% there and pulled e2e to 58.6%. The unit
# (fake API) and integration (dynamodb-local) suites cover it, and the
# merged total still counts it.
- ^internal/dedupe/dynamodb\.go$
# wireDynamoDedupe and its retry component (internal/app/wire_dynamodb.go):
# same reason as dynamodb.go above — the e2e binary never selects
# dedupe.backend: dynamodb, so this file measured 0% there and pulled
# e2e to 59.7%. The unit and integration suites cover it, and the
# merged total still counts it.
- ^internal/app/wire_dynamodb\.go$
# The in-process cache backend: the e2e stack runs cache.backend=redis
# (#613), so the binary carries LocalCache and its version index but e2e
# never reaches them. The unit suite and the integration suite's main
# app (cache.backend=local) cover them; the merged total still counts them.
- ^internal/cache/(local|version_manager)\.go$
# What e2e's Redis never makes it run: the cache.redis block's rejection
# paths (boot adopts a valid fixture, as with internal/settings above)
# and the retry of invalidations the server did not take, which needs an
# outage. The unit and integration suites cover both.
- ^internal/config/cache_redis\.go$
- ^internal/cache/pending\.go$
# The external-NATS topology spec, verifier and manifest generator
# (and the `mq manifests` CLI) run against an operator's NATS, which
# the e2e stack (embedded broker) never has: unit territory, covered
# there by a fixture server. Excluding them keeps e2e at ~61%.
# there by a fixture server. Excluding them keeps e2e at ~61%. The
# external broker is the same, covered by the integration suite.
- ^internal/mq/nats_topology\.go$
- ^internal/mq/nats_manifests\.go$
- ^internal/mq/subject_nats\.go$
- ^internal/mq/external\.go$
- ^internal/mq/lease\.go$
- ^cmd/wavehouse/mq\.go$
# Their wiring, apart from wire.go for this: the NATS queue and lease
# wiring and the retention warning under it.
- ^internal/app/wire_nats\.go$
# The ops-only listener of a process without the api role, moved
# out of wire.go the same way: the e2e binary runs every role.
- ^internal/app/wire_ops\.go$
# The mq.nats block's checks, as cache_redis.go above: boot adopts the
# embedded fixture, so e2e never reads it.
- ^internal/config/mq_nats\.go$
unit:
# The external NATS broker's tests start a server per case, which the
# unit suite's 15s per package cannot hold: they are integration-tagged
# (make test-integration), and the merged total counts them.
- ^internal/mq/external\.go$
# The NATS KV leases, the same way.
- ^internal/mq/lease\.go$
Loading
Loading