-
Notifications
You must be signed in to change notification settings - Fork 731
feat: split GO/NUGET dependent counts, incremental edge-diff + snapshot guard (CM-1281) #4273
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
41 commits
Select commit
Hold shift + click to select a range
f6249a2
fix: trying to prevent a deadlock
themarolt eafcb53
fix: using proper tables for nuget and go
themarolt 9b77640
fix: flag to update constraints
themarolt 9fd28f7
chore: improvements to monitoring
themarolt 584cf90
chore: more states
themarolt c4e31ac
fix: use the same env as in deployment
themarolt ba96498
fix: better deploy script and monitor fix
themarolt 954a751
fix: monitor column spacing fix
themarolt 1f3fda0
fix: dont be done prematurely
themarolt fd18287
fix: index creation improvements
themarolt 755a317
chore: script to fix unique index on packages_dependents
themarolt 73ad88b
fix: better index creation
themarolt 078ccae
Merge branch 'main' into fix/bq-depsdev-deadlock
themarolt aac2ccd
Merge remote-tracking branch 'origin/main' into fix/bq-depsdev-deadlock
themarolt 0f33398
fix: comments
themarolt 719ce84
Merge remote-tracking branch 'origin/main' into fix/bq-depsdev-deadlock
themarolt e2ca951
fix: comments
themarolt 826b42e
Merge remote-tracking branch 'origin/main' into fix/bq-depsdev-deadlock
themarolt d369171
fix: comments
themarolt 61b4b31
Merge remote-tracking branch 'origin/main' into fix/bq-depsdev-deadlock
themarolt f56818a
fix: comments
themarolt 26f0680
fix: comments
themarolt 8656ec6
fix: dedup deps.dev incremental edges, guard corrupt snapshots (CM-1281)
themarolt 7d32df6
Merge remote-tracking branch 'origin/main' into fix/incremental-depsd…
themarolt 1ca7b17
style: format deps-dev README (CM-1281)
themarolt 16f5577
Merge remote-tracking branch 'origin/main' into fix/incremental-depsd…
themarolt 07a2ec7
Merge remote-tracking branch 'origin/main' into fix/incremental-depsd…
themarolt a76d13e
refactor: clarify edge-snapshot guard threshold naming (CM-1281)
themarolt 1c70251
Merge remote-tracking branch 'origin/main' into fix/incremental-depsd…
themarolt 3b39fd3
fix: comments
themarolt 4768874
fix: small cleanup
themarolt dee16ec
fix: comments
themarolt 561766e
fix: comments
themarolt 053aefe
fix: comments
themarolt c7a6dd0
fix: comments
themarolt 0fdd301
fix: comments
themarolt c2ba05e
Merge remote-tracking branch 'origin/main' into fix/incremental-depsd…
themarolt 4a7e5ca
fix: comments
themarolt ac2de8a
Merge remote-tracking branch 'origin/main' into fix/incremental-depsd…
themarolt f8c4463
fix: comments
themarolt 7dd4205
Merge remote-tracking branch 'origin/main' into fix/incremental-depsd…
themarolt File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,100 @@ | ||
| # ADR-0004: Compute GO/NUGET transitive dependent counts via exact reverse closure (over HLL approximation) | ||
|
|
||
| **Date**: 2026-06-23 | ||
| **Status**: accepted | ||
| **Deciders**: Uroš Marolt | ||
|
|
||
| ## Context | ||
| deps.dev publishes its reverse-dependent index (`Dependents`) and its resolved/transitive | ||
| dependency graph (`Dependencies`, `DependencyGraphEdges`) for only four ecosystems — | ||
| NPM, MAVEN, PYPI, CARGO (verified via BQ 2026-06-23: each `GROUP BY System` returns exactly | ||
| those four). GO and NUGET have only raw direct manifests (`GoRequirementsLatest`, | ||
| `NuGetRequirementsLatest`). The `dependent_counts` ingest was therefore split three ways | ||
| (edges / go / nuget — see the dependent-counts split). Direct `dependent_count` for GO/NUGET | ||
| is a cheap manifest invert, but `transitive_dependent_count` (MinimumDepth>1) and the all-depth | ||
| `dependent_repos_count` require the reverse **transitive closure**, which deps.dev does not | ||
| provide for these two ecosystems — so we must compute it ourselves from the direct edges. | ||
|
|
||
| ## Decision | ||
| Compute the **exact** reverse transitive closure for GO/NUGET — a semi-naive fixpoint over | ||
| module-level edges (names hashed to INT64 via `FARM_FINGERPRINT`, persisted + clustered) iterated | ||
| to convergence — so GO/NUGET transitive counts are true integers matching the edge-system | ||
| methodology. We chose exact over an HLL-sketch approximation, accepting the higher cost for now. | ||
|
|
||
| ## Alternatives Considered | ||
|
|
||
| ### Alternative 1: HLL sketch propagation (approximate) | ||
| - **Pros**: ~cents/run, seconds, tiny tables (one HyperLogLog sketch per subject, ~188K rows for | ||
| GO, merged along edges to a fixpoint — never materializes the billions of pairs). Full-depth. | ||
| - **Cons**: probabilistic cardinality estimate with bounded relative error ≈ `1.04/sqrt(2^p)` | ||
| (~1.6% at precision 12, ~0.4% at 15). Methodology differs from the exact edge-system counts, so | ||
| GO/NUGET would be ~1% approximate while the other four ecosystems are exact. | ||
| - **Why not**: the decision is to have identical, exact integer methodology across all six | ||
| ecosystems. The measured exact cost (below) is acceptable for now. HLL remains the documented | ||
| fallback if weekly cost becomes prohibitive. | ||
|
|
||
| ### Alternative 2: Bounded-depth closure (cap at K hops) | ||
| - **Pros**: K deterministic joins in a single statement, dry-runnable, cost-bounded, no scripting. | ||
| - **Cons**: undercounts. The GO closure needs **31 hops** to converge; new pairs peak at hop 8 | ||
| (~507M) and only decay to zero by hop 31. Capping at a small K drops hundreds of millions of | ||
| genuine transitive pairs. | ||
| - **Why not**: a low cap is materially wrong; a cap high enough to be correct is no cheaper than | ||
| full convergence. | ||
|
|
||
| ## Consequences | ||
|
|
||
| ### Positive | ||
| - Exact integer counts, full depth, identical methodology to NPM/MAVEN/PYPI/CARGO. | ||
| - Deterministic and reproducible; no estimator error to explain to downstream consumers. | ||
| - Termination is guaranteed by construction: each iteration's frontier is anti-joined against the | ||
| accumulated `reach`, so only brand-new pairs survive and the finite pair space converges (proven: | ||
| GO converged at hop 31 with `new_pairs → 0`). | ||
|
|
||
| ### Negative | ||
| - This is by far the heaviest job in the packages pipeline. Measured exact runs (2026-06-23, | ||
| INT64-fingerprint-optimized; raw string names would be ~5×). The build-`reach` figures are from the | ||
| closure probe; the full-pipeline figures (incl. the all-depth repos aggregation + per-subject | ||
| counts → `_export_data`) are from the end-to-end validation run of the production script: | ||
| - **GO**: 5.81B closure pairs, 31 iterations. Build reach ≈ 1.86 TB; **full pipeline = 2.31 TB | ||
| billed (~$14.5 at $6.25/TiB), 99 slot-hours, 16 min wall, 132 child statements** (validated). | ||
| - **NUGET**: 114M closure pairs, 19 iterations, ~4 min wall, ~32 GB billed (~$0.19); full pipeline | ||
| validated → 191,762 subject rows. | ||
| - GO dominates (its graph is ~50× larger and deeper). Combined ≈ **~$15/run, ~20 min, weekly**. | ||
| For comparison the edge `dependent_counts` job scans ~310 GB (~$2). | ||
| - Requires a semi-naive BQ scripting job (not a single exportable SELECT), so it does not fit the | ||
| existing one-query→GCS pipeline shape as-is (integrated via an `isScript` mode — see below). | ||
|
|
||
| ### Risks | ||
| - Cost/runtime grow with the GO/NUGET graphs. Mitigations: INT64 fingerprints (~5× cheaper), | ||
| clustering on the join keys, and a documented escape hatch to switch GO/NUGET to HLL (Alternative | ||
| 1) if cost becomes prohibitive — counts in the hundred-thousands make ~1% error invisible for | ||
| popularity/ranking. | ||
| - `FARM_FINGERPRINT` collisions are negligible at ~1–2M nodes (~1e-6) but non-zero; acceptable for | ||
| these metrics. Switch to a dictionary-encoded INT id if exactness must be collision-proof. | ||
|
|
||
| ## Pipeline integration (decided 2026-06-23) | ||
| - **No persistent scratch dataset.** The closure runs as one multi-statement scripting job using | ||
| session-scoped `CREATE TEMP TABLE`s (edges → INT64-fingerprinted edges → `reach` fixpoint → | ||
| fingerprint→name lookup → `_export_data`). Temp tables are clustered on the join keys and are | ||
| auto-dropped when the script's session ends — on success *and* failure — so there is no lingering | ||
| storage to bill or clean up. (The interactive probe used regular tables in | ||
| `lfx-insights:scratch_closure_probe`; that is leftover, dropped separately — not the pipeline.) | ||
| - **Reuses the existing job path, not a new kind.** `bqExportToGcs` gains an `isScript` mode: when | ||
| set, `sql` is the full script (ending in `CREATE TEMP TABLE _export_data AS …`) and the activity | ||
| appends only the `EXPORT DATA … AS SELECT * FROM _export_data` statement rather than wrapping | ||
| `sql` in a subquery. Everything downstream (listParquetFiles → guard → chunked | ||
| gcsParquetToStaging → mergeStagingToTable) is unchanged, and the `dependent_counts_go` / | ||
| `dependent_counts_nuget` kinds, guard baselines, and monitor entries stay continuous. | ||
| - **Cost guard.** A dry-run cannot price a `WHILE` loop, so script mode skips it and enforces the | ||
| byte ceiling server-side via `maximumBytesBilled` (= the per-variant `maxBytesGb`). The script | ||
| also carries an iteration cap as the deterministic runaway guard (convergence already proven: | ||
| GO hop 31, NUGET hop 19, both `new_pairs → 0`). | ||
| - **Final output → counts.** `_export_data` emits `(purl, dependent_count, | ||
| transitive_dependent_count, dependent_repos_count)`: fingerprints in `reach` are mapped back to | ||
| names via a `names` temp table, joined to `purl_map` (name→purl). `transitive_dependent_count = | ||
| COUNT(DISTINCT dep over reach) − direct_count`; `dependent_repos_count` = distinct | ||
| `PackageVersionToProject` SOURCE_REPO_TYPE repos over the all-depth dependent set. | ||
| - **Weekly trigger.** No new schedule — the `go`/`nuget` variants are already child workflows of | ||
| `bootstrapOsspckgs`, which the existing `osspckgs-bootstrap-weekly` schedule runs incrementally. | ||
|
|
||
| Related: ADR-0003 (deps BQ table selection — same root cause: GO/NUGET absent from the resolved-graph tables). |
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
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,10 +1,8 @@ | ||
| import { scheduleOsspckgsBootstrap } from '../deps-dev/schedules/bootstrap' | ||
| import { scheduleOsspckgsCleanup } from '../schedules/cleanup' | ||
|
themarolt marked this conversation as resolved.
|
||
| import { svc } from '../service' | ||
|
|
||
| setImmediate(async () => { | ||
| await svc.init() | ||
| await scheduleOsspckgsBootstrap() | ||
| await scheduleOsspckgsCleanup() | ||
| await svc.start() | ||
| }) | ||
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
Oops, something went wrong.
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.