Conversation
Merge pipeline products built over disjoint local-day ranges into one new product. Refuse before writing unless every shard shares schema, product identity, source layout and dataset metadata, completed days are disjoint, and every day-owned row lies inside its shard's completed days. Copy the completion markers so resuming the merged product is a no-op.
…re full marker coverage Drop the single merge transaction and its 11-shard limit: the private temporary file and rename already make the merge atomic. Refuse completion markers without a five-minute coverage row for every local bucket, which catches shards built without an explicit end date.
Remote shards survive ssh drops and are reattached on rerun. Shard databases are named by dataset and day range, a changed layout is refused, the local merge binary is checked before launch, Ctrl-C stops local jobs, and local shard copies are deleted after verify unless --keep-shards.
merge-shards --consume validates every shard first, moves the first shard into the temporary output, and deletes each other shard once its rows are durably committed. A failure keeps the partial product and names the consumed shards and the resume command.
…ng merges Record a shard as consumed as soon as its data is inside the temporary output, keep the partial on cleanup or sync failures, report post-publication sync failures as a published product, and recompute inferred default start dates from merged traffic inside each shard transaction.
Split the range into min(days, slots) shards whose lengths differ by at most one day, and order slots round-robin across hosts so lighter hosts still receive work.
Shards now own address_maad_stats rows by day and share maad_q_grid, which must match exactly across shards (including empty grids with MAAD disabled) before the first shard's copy is kept. The merge validates against storage::product_schema() instead of a duplicate definition.
flamboh
force-pushed
the
feat/cluster-shard-merge
branch
from
September 27, 2026 05:17
83cd61b to
fd760d7
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.
Note
🤖 Claude Opus 5.5 on behalf of Oliver
ELI5
A full reprocess takes days on one machine. This lets several machines each build a slice of days and then stitches the slices into one database, checking that the result matches a single-machine run.
Why
pipelineis one process with one SQLite writer. A production day takes ~16 min and uses only 4–5 of 24 cores, so a year of data is ~100 h single-process. Local days are independent: rollups max out at1d, and every rollup bucket is aligned to local midnight. Day ranges can therefore be built separately and merged.What changes
netflow-db merge-shards --output OUT SHARD...merges day-sharded products built with identical flags and an explicit--end-dateinto a new product. It copies completion markers, so runningpipelineon the merged product is a no-op for the merged days.--consumekeeps peak disk near the output size plus one shard. It moves the first shard into the temporary output and deletes each other shard right after its rows commit.scripts/netflow-db-cluster.shsplits a date range acrosshost:Nslots. It runs each shard detached over plain SSH, copies snapshots back, merges with--consumeby default and runsverify. All hosts must use the same--remote-dirpath.address_maad_statsrows are day-owned and merged like other stats.maad_q_gridis shared: every shard must hold identical(ip_version, q_min, q_step, q_count)rows before the merge keeps the first shard's copy. With MAAD disabled, all shards hold an empty grid. The merge checks the table contract withstorage::product_schema()and no longer keeps its own copy. Any table that is neither day-owned nor shared is refused.docs/user/setup-pipeline.md, and "Day-sharded products" indocs/code/pipeline-contract.md.Review path
Setup: any nfcapd tree with at least three days and a dataset registry entry, e.g.
datasets.json.example. Build each shard with identical flags and an explicit--start-date/--end-date.a.sqlite(day 1) andb.sqlite(days 2–3), then runnetflow-db merge-shards --output m.sqlite b.sqlite a.sqlite.netflow-db verify m.sqlite --dataset-id <id> --require-data --require-maad-data --require-processed --require-rollup-parity --require-no-raw-ipprintsOK.pipelineover days 1–3 onm.sqlitereportsPublished five-minute buckets: 0.maad_q_gridwas edited (for exampleUPDATE maad_q_grid SET q_step = q_step * 2).BEFORE INSERTtrigger ontraffic_stats. Runmerge-shards --consumewith the shards in the order day 2, day 1, day 3..merge.tmpproduct and prints a resume command.datasets.default_start_dateis day 1.--hosts nodeA:4,nodeBover 6 days, using real hosts or the fakessh/scpintools/netflow-db/tests/cluster_launcher.rs.Decisions and edge cases
Launcher split. The range splits into
min(days, slots)contiguous shards whose lengths differ by at most one day. Slots are ordered round-robin across hosts, so every listed host gets work whenever the range has at least as many days as there are hosts.host:Ngives a host N slots, so leaving:Noff a busier host gives it a lighter share instead of skipping it.default_start_datepolicy. For each dataset:What counts as consumed. A shard counts as consumed the moment its data is inside the temporary output: after the first shard's rename (or cross-filesystem copy and sync), or after a later shard's commit. Any later failure keeps the partial and lists that shard as consumed, so the resume command never re-includes it. This covers a directory sync,
DETACH, or deleting the shard's files. A failed file deletion is reported as its own cleanup error that names the leftover files, which are safe to delete.Failure after publication. If the directory sync fails after the output is renamed into place, the error says the product is published and asks you to run
verify. It does not report the temporary path, which no longer exists.Recovery guarantees, as tested. Tests cover:
In each case no data is lost, the partial holds exactly the consumed shards, and resuming produces a product identical to a single run. Durability across a crash or power loss rests on SQLite's rollback journal with
synchronous=FULLand explicit file and directory syncs, and is not tested. The post-publication sync failure is not covered by a test.The nfdump path is part of product identity, so every host installs it under the same absolute
--remote-dir.processed_inputs.file_devicediffers per node on network filesystems, and resume decisions ignore it.A launcher rerun re-copies every shard from the hosts and deletes a stale partial merge first. Remote shards survive an SSH drop or a launcher interrupt, and a rerun reattaches to shards that are still running.
Verification
bun run format,bun run lint,bun run typecheck,bun run test:db, andcargo fmt --all --check.cargo clippy --workspace --all-targets --all-features --locked -- -D warnings.addresses/packets/bytesrow stores blob curves and 2 grid rows, plusverifyand resume as a no-op;maad_q_gridq_min,q_step, orq_countdiffers or a row is missing, and a MAAD-disabled merge with empty grid and MAAD tables;ssh/scp:--consumeby default and off with--keep-shards, plus shard allocation fornodeA:4,nodeBover 6 and 3 days andnodeA,nodeBover 7 days.file_device. A 2-node launcher run built, merged and verifiedOK, and survived a mid-run TERM and rerun.Made by Claude Opus 5.5 via Claude Code.