feat(netflow-db): make internal-side MAAD optional - #127
Merged
Merged
Conversation
Skip MAAD for internal-side address sets by default and add a maad_internal_side dataset/config setting to opt in. Skipping products record the setting in their identity; verify, compare, and merge-shards expect skipped rows to be absent. The dashboard reads the setting from datasets metadata and explains uncomputed internal sides. Closes #121
Compare the datasets table structurally in merge-shards so a table upgraded with ALTER TABLE matches a freshly created one, and default maad_internal_side to 1 everywhere. Move column-mirror and config checks into unit tests and trim the pipeline CLI test.
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
Closes #121.
Problem
MAAD on internal-side address sets isn't well defined, and computing it costs time and storage. Internal-side sets are the source addresses of internal-source traffic and the destination addresses of internal-destination traffic. The decision on #121: make these sets optional everywhere, and skip them by default.
What changes
Pipeline (
tools/netflow-db)maad_internal_side, defaultfalse. Set it on adatasets.jsonentry, or at the top level of a pipeline config. A config's owndatasets[]entries can't set it, which matches howlocalityworks.publish::write_bucketstakes aMaadScopesvalue (None/ExceptInternalSide/All) and filters address sets before the MAAD worker pool runs. This applies to all three measures and to rollups. Address-count rows are unchanged, andall/allscopes are still computed."internal_side": falseinside themaadresult config. A product that computes every set keeps exactly the identity it had before. So:--datasetruns must agree on the setting.maad_internal_side: true.datasets.maad_internal_sidecolumn: mirrors the setting for the dashboard, which can't readpipeline_producton D1. The pipeline adds the column (default1) when it opens a pre-existing database.verifyreads the setting from product identity. It fails when the column disagrees or when a skipping product stores internal-side rows. With--require-maad-data, it now requires a MAAD row for every address set the product computes, so internal-side rows are required when opted in.comparereports reference rows for skipped sets asskipped_reference_rowsrather than reference-only rows. Candidate rows for skipped sets count as unexpected.merge-shardsrefuses a skipping shard that stores internal-side rows. Mixed settings were already refused through product identity.Scopes computed in default mode (per IP version and bucket)
all→allsource + destination,external→externalsource + destination,external→internalsource,internal→externaldestination (6 of 10)daily_active_sources(selection fixessrc_locality = internal)internal→internalorinternal→external. Useful MAAD:all→allsource + destination, andinternal→externaldestination (external peers). Theexternal→*scopes are still computed but are always empty for this selection.Dashboard (
apps/web)20261001083148_maad_internal_sideaddsdatasets.maad_internal_side(DEFAULT true, so existing D1 rows read as "computed").1./api/netflow/maad-statusnow returns{ computed, internalSide }. The dataset page and the file page load it.Screenshots of both states were taken from the Playwright fixture: ingress with Destination selected, and lateral. They aren't embedded because the evidence upload tool wasn't available in this environment.
tests/e2e/maad-internal-side.spec.tsasserts the exact copy for both states.Review guide
Flows to exercise
maad_internal_side. Then runverify --require-data --require-maad-data. It should pass, andaddress_maad_statsshould have nointernalsource-side or destination-side rows."maad_internal_side": trueand rebuild to a new path. You should get all 10 scope/side combinations, andverifyshould pass.direction=egress.Setup / test data: everything uses fixtures. The e2e fixture adds a second dataset row,
playwright-external-maad, withmaad_internal_side = 0in the same Playwright database.Decisions needing review
internal_side. This keeps currently deployed databases, which computed every set, extendable by settingmaad_internal_side: trueon their registry entries. If you'd rather force a fresh product for every database, add the key unconditionally.maad_internal_side: truewill fail its next run on an identity conflict rather than mixing results. Add"maad_internal_side": trueto keep extending an existing database.datasetscolumn is a mirror for the web, andverifychecks the two agree. The column defaults to1everywhere (Rust DDL, legacy upgrade, Drizzle/D1), and the pipeline always writes it explicitly.merge-shardscompares thedatasetstable by its columns rather than its rawCREATE TABLEtext, so a shard upgraded in place merges with a fresh one.--require-maad-datais stricter. It now runs an anti-join fromaddress_count_statstoaddress_maad_stats(indexed lookups), so it costs more on large databases.all/allscopes stay computed in both modes. Fordaily_active_sources, theall→allsource side is effectively all internal addresses. Removing it would be a separate decision.Verification
Automated:
bun run format,bun run lint,bun run typecheck: passbun run test:db: pass (219 tests)bun run test:web: pass (212 tests)bun run test:e2e: pass (26 tests)New tests:
--no-maad, identity refusal, column mismatch, stray internal rows)Manual checks remaining:
Made with Claude Opus 5.5 in Claude Code.