Data usage and provenance¶
This document describes every table, view and column in the aveloxis_cve schema: what one row is, where each value comes from, which command writes it, and when it changes or is deleted. It also covers what aveloxis-cve reads from Aveloxis, the personal data it stores, and how to remove that data.
It describes the schema as of migrations 0001-0019, drawn in schema.md. Tested queries over it are in analysis/. TestDataDocCoversEverySchemaColumn (internal/db) fails when a table, view or column of the migrated schema is missing here, so a migration that adds one must also document it. Code is cited by file and function, not line number. Unless a section says otherwise, paths are relative to the repository root.
Where the data comes from¶
aveloxis-cve has five sources. It makes no forge API calls.
Source |
What is taken |
Through |
|---|---|---|
Its own git clones |
Commits (hashes, parents, author and committer names, emails and times, messages), per-file line counts, refs (branches), trees and blobs |
|
The Aveloxis database, read-only |
Which repositories exist and are collected; the contributor Aveloxis attached to a commit; contributor emails, aliases and identities |
|
|
Per-file language, line counts and complexity |
|
The operator |
Which repositories to track, enable or disable; recompute and retry requests; configuration (window size and step, scc options) |
CLI commands ( |
The program and the database server |
Timestamps ( |
Constants and the database clock |
Aveloxis objects read¶
These are exactly the objects and columns in source.RequiredColumns (internal/source/source.go); doctor fails when one is missing or unreadable. repos.repo_name and repos.repo_owner are selected but not used.
Aveloxis object |
Columns |
Used for |
|---|---|---|
|
|
|
|
|
Readiness: a repository is analysed only after Aveloxis has completed a collection of it |
|
|
|
|
|
Checked by |
|
|
Readiness (has commits); identity step 1 (the contributor Aveloxis attached to the same commit) |
|
|
Identity step 2, and the not-merged filter of every identity step |
|
|
Identity step 3 |
|
|
Identity step 4 |
doctor warns when Aveloxis lacks the requested index on lower(cntrb_email), lower(alias_email) or lower(email) (source.MissingEmailIndexes; a failed check is a warning, never read as missing): without them, every author resolution reads those tables whole. Only Aveloxis creates indexes in its schemas: Aveloxis 0.29.141 and later create all three (its migrate); never create them by hand or from aveloxis-cve. doctor then checks the role's privileges. It warns if the role can modify any Aveloxis table (aveloxis-cve never does, but it should run as the read-only role), and then skips the secret-table check. Only when the role can modify no Aveloxis table does doctor check secret tables: it fails if the role can read one (aveloxis_data.chaoss_user, or any aveloxis_ops table other than the two above).
Nothing in aveloxis_cve refers to an Aveloxis object. There are no foreign keys, views, functions or triggers across schemas (TestMigrationsNeverReferenceAveloxisSchemas). Aveloxis ids (repo_id, cntrb_id) are copied by value. They can therefore point at an Aveloxis row that has since changed or been deleted.
What writes what¶
Command |
Writes |
|---|---|
|
Creates the schema; |
|
|
|
|
|
|
|
Identity columns of |
|
|
|
|
|
|
|
Nothing (read only) |
Writes made under a repository lock or the labor gate are fenced: each proves, in its own transaction, that the lock is still held (internal/store/fence.go). The exception is the recompute request, which --retry-uncounted records unfenced while holding the gate. stop takes no lock and writes unfenced. A command writes nothing more under a lock it has lost. A labor run that lost only the gate still records its outcome, which is fenced by the repository lock alone.
Personal data¶
aveloxis-cve stores personal data taken from git and linked to Aveloxis's contributor records:
commits.author_name,author_email,committer_name,committer_email: copied as git records them (no.mailmap, not lowercased).commits.messageandsubject: free text, which can name people or contain email addresses.branch_repos.ref_nameandbranch_name: branch names, which often carry user names (jdoe/fix).commits.cntrb_id: a link to an Aveloxis contributor, which joins these rows to whatever Aveloxis holds about that person.repo_labor_rolling_changed.distinct_contributors: a count, not identities.File paths and names (
commit_files.file_path,commit_facade_files.file_name,repo_labor_rolling.file_path,repo_labor_rolling_changed.file_path,scc_blob_cache.file_name), which occasionally contain a person's name.Error text (
tracked_repos.last_sync_error,last_labor_error), which can quote branch names and paths.tracked_repos.repo_git: the repository URL as Aveloxis has it. If it carries credentials, they are stored here too. Error text (last_sync_error) has URL credentials redacted (procx.Redact,procx.RedactURL).
The on-disk clones under clone_dir hold the full history of each repository, including the same names, emails and messages.
Retention and removal¶
aveloxis-cve never deletes a commit, branch membership of an archived branch, or a tracked repository. repos disable drops a repository from the default selection and keeps its data. A disabled repository named with --repo-id is still processed. Branches deleted or force-pushed upstream are kept, as archive refs and archived branch_repos rows, because CVE history on end-of-life branches is the point. Rows are removed only as each section below describes (for example, a live branch row whose ref is gone, or windows a recompute replaces).
To remove everything:
DROP SCHEMA aveloxis_cve CASCADE; -- touches no Aveloxis object
Then delete clone_dir.
To remove one repository, run the following with no aveloxis-cve command running, with :repo_id set to the repository's id (psql -v repo_id=123 -f remove.sql, or the script on stdin; psql does not substitute variables in -c). Foreign keys cascade to the child tables. Then delete <clone_dir>/repo_<id>.git, its lock file <clone_dir>/repo_<id>.git.lock, and any <clone_dir>/repo_<id>.git.tmp-* a killed first clone left.
BEGIN;
DELETE FROM aveloxis_cve.branch_repos WHERE repo_id = :repo_id; -- branch_commits
DELETE FROM aveloxis_cve.commits WHERE repo_id = :repo_id; -- commit_files, commit_facade_files, links, undoes
DELETE FROM aveloxis_cve.repo_labor_windows WHERE repo_id = :repo_id; -- repo_labor_rolling, repo_labor_rolling_changed
DELETE FROM aveloxis_cve.labor_recompute_requests WHERE repo_id = :repo_id;
DELETE FROM aveloxis_cve.tracked_repos WHERE repo_id = :repo_id;
COMMIT;
scc_blob_cache is shared by all repositories and keyed by blob hash, file base name and scc cache version. It holds measurements only, never file contents or paths. The script above leaves a removed repository's cache rows (blob ids and base names), because other repositories may share them.
Contents¶
Object |
One row is |
Section |
|---|---|---|
|
a repository the operator tracks |
|
|
an applied migration |
same |
|
a commit of a repository |
|
|
a file a commit changed |
same |
|
a file row as Aveloxis's facade would record it |
same |
|
a live branch or archived tip |
|
|
a commit's membership of a branch |
same |
|
a resolved cherry-pick trailer |
same |
|
a resolved revert reference |
same |
|
a CVE whose fix a commit undoes |
same |
|
a file changed by a commit on a branch |
same |
|
a group of commits with the same patch-id |
same |
|
a commit that fixes a CVE, and how it was matched |
same |
|
when a CVE's fix first landed on a branch |
same |
|
a recompute request for one window configuration |
|
|
a stop request ( |
same |
|
a rolling window of a repository's default branch |
same |
|
a file in a window's end snapshot |
same |
|
a file changed within a window |
same |
|
an scc measurement of a file content under a name |
same |
Column tables give: column, type, nullability, source, and how the value is derived and written.
Branches, commit links and CVE views¶
Conventions used below. Paths are relative to the repository root. "sync" means the sync command and the sync step of run. Both go through Syncer.SyncRepo (internal/pipeline/sync.go), which takes the per-repository advisory lock and makes every write fenced (sync.go, internal/store/fence.go). "resolve" means the resolve command and the resolve step of run (Syncer.Resolve, sync.go). "labor" means the labor command and the labor step of run. No code path in the program deletes a commits row, so the ON DELETE CASCADE foreign keys on commits only fire if someone deletes rows by hand. repo_id is Aveloxis's repos.repo_id, copied by value with no foreign key.
The order of the steps inside one sync (sync.go) matters for several columns:
Readiness check against Aveloxis.
Clone or fetch, then ref reconciliation and archiving (
gitx.Runner.Sync).Re-attach archived tips (
restoreArchives).List refs.
Ingest commits (
ingest).ResolveCherryPicks.ResolveReverts.RefreshUndoes.Branch rows, membership and stats (
updateBranches).Identity resolution of unchecked commits.
Patch-ids.
Facade files.
Each store call runs in its own transaction. If a sync fails part-way, the steps already done stay committed, and the next sync finishes the rest.
branch_repos¶
One row per live branch (refs/heads/<name>) or archived tip (refs/cve-archive/<deleted|forced>/<UTC stamp>[-N]/<name>) in aveloxis-cve's own bare clone of a repository. The primary key is branch_id, an identity column. The natural key is UNIQUE (repo_id, ref_name). Two CHECK constraints apply: archive_reason IN ('deleted','forced'), and (archive_reason IS NULL) = (archived_at IS NULL). Sync creates, updates and deletes the row, through Syncer.updateBranches (sync.go); resolve and labor write only labor_dirty_since (below). It inserts or updates the row for every ref present in the clone (Store.UpsertBranch, internal/store/branches.go), renames a live row into an archive in place (ArchiveBranch, branches.go), and deletes live rows whose ref is gone (DeleteBranchesNotIn, branches.go). Membership columns are set by WriteMembership (branches.go) and stats by SetBranchStats (branches.go). labor_dirty_since is also set by identity resolution (sync and resolve; ApplyResolutions, branches.go) and cleared by labor (InvalidateWindows, internal/store/labor.go).
Lifecycle:
A live row is deleted, with its
branch_commitsby cascade, when its ref is no longer in the clone and it was not renamed into an archive.Archived rows are never deleted. The database, not the clone, is the record of archived history.
An archived row whose ref cannot be restored into a re-created clone (its objects are gone) is kept as it was. It is no longer upserted, so its
last_seen_atstops advancing.
Readers:
sync:
Store.Branches, branches.go. It lists previous tips for the ingest exclusion and supplies the membership and stats state.labor:
DefaultBranchState, labor.go. This is the default, live row withmembership_head_sha = head_shaandmembership_schemecurrent, pluslabor_dirty_since.status: internal/store/status.go. It counts live and archived rows and shows the default branch name.The views
branch_commit_files(for analysts; the program does not read it) andcve_fix_propagation(read bycves).
Column |
Type |
Null |
Source |
How it is derived / written |
|---|---|---|---|---|
|
integer |
NO |
Identity sequence |
|
|
bigint |
NO |
Aveloxis |
Set on insert by |
|
text |
NO |
git ref name, from |
Full ref name, passed through |
|
text |
NO |
Parsed from the ref name |
Live branch: the name after |
|
text |
NO |
git ref tip (objectname) |
Updated on every sync from the ref's current tip (branches.go). |
|
timestamptz |
YES |
Committer timestamp of the tip, from |
The raw committer time of the tip as UTC seconds (sync.go). It is NULL when the time is outside years 1..9999 ( |
|
boolean |
NO (default false) |
The forge's default branch: |
TRUE when the ref is live and its branch name equals the HEAD branch (sync.go). Recomputed for every ref on every upsert (branches.go). |
|
text |
YES |
Ref reconciliation in |
NULL for a live branch. |
|
timestamptz |
YES |
Wall clock of the archiving sync, encoded in the archive ref name |
|
|
text |
YES |
|
For the default row, or any branch whose tip is the default tip, it is the tip itself (sync.go). NULL when the histories are unrelated (git exits 1). Written by |
|
integer |
YES |
Commit graph |
Number of commits reachable from the branch tip (all parents) and not from the default tip, computed in memory with bitsets ( |
|
integer |
YES |
Commit graph |
Commits reachable from the default tip and not from the branch tip (same function). Same write rule. |
|
text |
YES |
The tip that membership was computed for |
Set by |
|
integer |
YES |
|
|
|
timestamptz |
NO (default now()) |
Wall clock (DB) |
Set on insert only. A renamed archive keeps the live branch's value. |
|
timestamptz |
NO (default now()) |
Wall clock (DB) |
|
|
text |
NO |
Constant |
Overwritten on every upsert. Not touched by |
|
text |
YES |
Default tip SHA used for the stats |
Written by |
|
text |
YES |
Branch tip SHA used for the stats |
Same as above. |
|
timestamptz |
YES |
Derived from |
Lowered with |
|
integer |
NO (default 1) |
Constant |
Set by |
Notes:
Archive rename. When
Syncarchives a tip, the nextupdateBranchesfinds an archive ref with no row. If the live rowrefs/heads/<b>hasmembership_head_shaequal to the archived tip, that row is renamed in place (branches.go, sync.go). It keepsbranch_id,first_seen_atand everybranch_commits.first_observed_at. Otherwise the archive ref gets a new row, its membership is computed from scratch, andfirst_observed_atrestarts. After a force-push the live branch name usually gets a new row (when the old row is renamed into the archive in place), with a newbranch_id, and full membership, sofirst_observed_aton the live branch restarts for its whole history.Re-created clone.
restoreArchives(sync.go) re-creates each archive ref the database knows, withgit update-refviaRestoreRef(internal/gitx/tree.go), but only when the tip object is still present.Stats are left alone, not cleared, when the default branch's tip is not among the refs (sync.go).
branch_commits¶
One row per (branch row, commit reachable from its tip). This is full membership: the whole reachable history of every live and archived branch, not just the commits unique to it. The primary key is (branch_id, commit_id). Three deferrable foreign keys with ON DELETE CASCADE point to branch_repos(branch_id), commits(commit_id), and commits(commit_id) via landed_via. Indexes: (commit_id), (landed_via), (branch_id, landed_at) INCLUDE (commit_id). Only sync writes it, through Syncer.membership (sync.go) -> Store.WriteMembership (branches.go). Membership is recomputed when the branch has no membership_head_sha, its tip moved, or its membership_scheme is older than 3 (sync.go).
The write is incremental (full=false: rows are inserted or updated, none deleted) only when the previous membership tip is still in the graph and lies on the new tip's first-parent chain (OnFirstParentChain, membership.go). Otherwise it is a full recompute (full=true), which also deletes rows no longer reachable (branches.go). The full path covers a force-push onto the same row, a "foxtrot" merge, and a scheme upgrade. Rows are also deleted by cascade when a live branch_repos row is deleted.
Readers:
labor:
WindowActivity(labor.go) reads rows bylanded_at,(start, end], or[start, end]for the first window.ChainLandings(labor.go) reads first-parent landings.DefaultBranchState(labor.go) reads the first dated landing.sync and resolve, through
ApplyResolutions(dirty marks).The views
branch_commit_filesandcve_fix_propagation.
Column |
Type |
Null |
Source |
How it is derived / written |
|---|---|---|---|---|
|
integer |
NO |
|
The branch row being computed (sync.go). |
|
bigint |
NO |
|
Looked up by hash ( |
|
boolean |
NO |
Commit graph |
TRUE when the commit is on the tip's first-parent chain, that is, committed or merged directly onto the branch. FALSE when it was brought in through a merge's non-first parent (membership.go). Rewritten on change (branches.go). |
|
bigint |
NO |
Commit graph |
The first-parent-chain commit that first made this commit reachable. |
|
timestamptz |
NO |
Committer times of the first-parent chain, adjusted ( |
The landing time of |
|
timestamptz |
NO (default now()) |
Wall clock (DB) |
Set on insert only. The upsert updates only |
Notes:
Because the history is shared,
landed_aton a branch cut from another branch records when the change entered that branch's first-parent history. For commits before the fork point, that predates the branch's existence. It is not when the branch was created.The synthetic epoch landing (
landed_at = 'epoch') can appear for an implausible root and for the commits that inherit its time. Labor skips it when anchoring windows: an epoch landing counts only when nothing lands after the epoch, and then only if its landing commit was really committed at the epoch (seerepo_labor_windows.window_end).Size: every branch stores its full history, so the row count is roughly (branches x history length).
commits (CVE and link columns)¶
These columns live on the commits row (grain: one row per commit reachable from any live or archived ref in the clone; key commit_id; unique (repo_id, commit_hash)). Sync inserts every column below at ingest: Syncer.ingest (sync.go) streams git log --stdin -z --no-renames --root --numstat --format=... over all ref tips, excluding the tips of known branch_repos rows that still exist (Syncer.ingest, through gitx.Runner.Existing in internal/gitx/graph.go; the stream is StreamCommits, internal/gitx/log.go). Store.InsertCommits (branches.go) stores them with ON CONFLICT (repo_id, commit_hash) DO NOTHING, in batches of 2000. The message-derived columns are computed once, at insert, from the commit message by cve.Extract and cve.IsRevert (internal/cve/extract.go). They are never re-parsed later, except by the one-off migration backfills listed below. Only the *_checked flags and patch_id are updated afterwards, all by sync. Readers: the views below, RefreshUndoes, resolveLinks, and status (counts commits with cardinality(cve_ids) > 0, status.go).
Column |
Type |
Null |
Source |
How it is derived / written |
|---|---|---|---|---|
|
text |
YES |
|
Computed only for non-merge commits ( |
|
boolean |
NO (default false) |
Flag |
Set to TRUE by |
|
text[] |
NO (default '{}') |
Commit message, regex |
Every match anywhere in the raw message (not only in the trailer block), in message order, de-duplicated (extract.go). Case-sensitive: only lowercase hex, 7 to 64 characters, with |
|
boolean |
NO (default false) |
Flag |
At insert: |
|
text[] |
NO (default '{}') |
Commit message, regex |
Case-insensitive match with ASCII word boundaries. Canonicalised to |
|
text[] |
NO (default '{}') |
Commit message, regex |
Canonicalised to |
|
boolean |
NO (default false) |
Commit message ( |
The subject is the first line of the trimmed cleaned message, trimmed ( |
|
text[] |
NO (default '{}') |
Commit message, regex |
Every match in message order, de-duplicated (extract.go). Case-sensitive, lowercase hex. It is extracted whatever |
|
boolean |
NO (default false) |
Flag |
Same rule as |
Notes:
Migration 0009 set both
*_checkedflags TRUE for pre-existing rows naming no SHA.Because messages are parsed once, a later change to a regex does not reach commits already stored unless a migration backfills them.
The commits of a vanished live branch stay in
commits, because every removed tip is archived and the rows are never deleted.
commit_cherry_picks¶
One row per resolved cherry-pick link: the commit commit_id carries a (cherry picked from commit X) trailer, and X names exactly one commit picked_from_id in the same repository. The primary key is (commit_id, picked_from_id). Both columns are deferrable FKs to commits with ON DELETE CASCADE. Index on (picked_from_id). Only sync writes it: Store.ResolveCherryPicks -> resolveLinks (branches.go), called right after ingest (sync.go). Readers: cve_fix_commits (the cherry_pick match) and RefreshUndoes (the credit CTE).
Column |
Type |
Null |
Source |
How it is derived / written |
|---|---|---|---|---|
|
bigint |
NO |
|
|
|
bigint |
NO |
The one commit whose hash has that prefix |
Inserted only when exactly one commit matched and it is not the commit itself ( |
Notes:
Each sync deletes, for the commits being re-resolved, existing links that no longer resolve uniquely to the same target (branches.go). An abbreviation that later becomes ambiguous loses its link. A source ingested later is picked up.
Once a commit is checked (all SHAs full-length and resolved), its links are never revisited.
Links stay within one repository. A cherry-pick from another repository, or from a commit never ingested (for example on an unfetched ref), stays unresolved, and the commit is retried on every sync.
commit_reverts¶
One row per resolved revert link: the commit commit_id contains This reverts commit X, and X names exactly one commit reverted_id in the same repository. The primary key is (commit_id, reverted_id). Both columns are deferrable FKs to commits with ON DELETE CASCADE. Index on (reverted_id). Only sync writes it: Store.ResolveReverts -> resolveLinks (branches.go), called after ResolveCherryPicks (sync.go). The resolution, deletion and retry rules are exactly those of commit_cherry_picks, with reverts_commits / reverts_checked. The reader is RefreshUndoes (chain CTE). No view reads this table directly.
Column |
Type |
Null |
Source |
How it is derived / written |
|---|---|---|---|---|
|
bigint |
NO |
|
As for |
|
bigint |
NO |
The one commit whose hash has the named prefix |
Inserted only for a unique match that is not the commit itself (branches.go). |
commit_cve_undoes¶
One row per (commit, CVE) where the commit names the CVE in its own cve_ids and undoes that CVE's fix, so cve_fix_commits must not report it as a fix. The primary key is (commit_id, cve_id). commit_id is a deferrable FK to commits with ON DELETE CASCADE. cve_id is plain text taken from commits.cve_ids, with no FK. Only sync writes it, through Store.RefreshUndoes (branches.go), right after ResolveReverts (sync.go). Every sync deletes all rows of the repository (joined through commits.repo_id, branches.go) and rebuilds them in the same transaction. Migrations 0012, 0013, 0015 and 0016 rebuilt it across all repositories. 0016 is the current rule and must stay identical to RefreshUndoes (TestRefreshUndoesAgreesWithMigration16). Reader: cve_fix_commits.
Column |
Type |
Null |
Source |
How it is derived / written |
|---|---|---|---|---|
|
bigint |
NO |
|
Recursive query (branches.go). (1) |
|
text |
NO |
|
The CVE whose chain parity is odd. |
Notes:
Revert parity, the "safe subset" (user decision 2026-11-07, migration 0016). Chains follow only named commits and cherry-pick trailers, never patch-id matches. Patch-id links have no direction and made chains loop (0.4.14-0.4.15). A revert's target and a cherry-pick's source always existed before the commit naming them, so the kept edges are acyclic in real data. Known gap, pinned by a test: a revert that names a CVE, of an unnamed backport matched to the fix only by patch-id (no trailer), is not an undo, so it is reported as a fix. A further revert of it is in turn counted as an undo.
A revert that does not name the CVE in its own message is never an undo here. It is also never a fix candidate, since
cve_fix_commitsstarts fromcve_ids. The exception: if its diff equals a fix's patch-id, it can be matched aspatch_idin that view.A revert of a commit that does not name the CVE and is not a trailer cherry-pick of one that does (for example, a fix made by reverting the vulnerable commit) has no chain and stays a fix.
If a sync fails between
ResolveRevertsandRefreshUndoes, the undoes reflect the previous link state until the next sync. Disabled repositories keep their last rows.
branch_commit_files (view)¶
Commits by branch and file: branch_commits joined, all inner joins, to branch_repos, commits and commit_files. The grain is one row per (branch row, commit, commit_files.file_path). It was defined in migration 0002 and never changed. The program does not read it; it is for analysts.
Column |
Type |
Null |
Source |
How it is derived / written |
|---|---|---|---|---|
|
bigint |
NO |
|
Join |
|
integer |
NO |
|
|
|
text |
NO |
|
|
|
text |
NO |
|
|
|
text |
YES |
|
NULL for live branches. Filter on it to exclude archived tips. |
|
bigint |
NO |
|
|
|
text |
NO |
|
|
|
timestamptz |
NO |
|
The raw committer time, not the landing time. |
|
timestamptz |
NO |
|
When the change reached this branch (see |
|
boolean |
NO |
|
|
|
text |
NO |
|
Per-path |
|
integer |
YES |
|
NULL for a binary file. |
|
integer |
YES |
|
NULL for a binary file. |
Notes:
Inner join to
commit_files: merge commits (git log prints no numstat for merges) and empty commits have nocommit_filesrows, so they do not appear.These counts are not Aveloxis-parity. Use
branch_commitsjoined tocommit_facade_filesfor counts identical toaveloxis_data.commits.
commit_equivalence (view)¶
Groups of commits that are the same change by git patch-id --stable. The grain is one row per (repo_id, patch_id) shared by more than one commit (HAVING count(*) > 1), over every commit of the repository on any branch, live or archived. It was defined in migration 0004 and never changed. The program does not read it.
Column |
Type |
Null |
Source |
How it is derived / written |
|---|---|---|---|---|
|
bigint |
NO |
|
|
|
text |
NO |
|
Only rows with |
|
integer |
NO |
|
Number of commits sharing the patch-id. Always 2 or more. |
|
text[] |
NO |
|
Ordered by raw committer time, then hash. |
|
timestamptz |
NO |
|
Raw committer time. |
|
timestamptz |
NO |
|
Raw committer time. |
Notes:
Merges and empty-diff commits have no patch-id and never appear. Commits whose patch-id has not been computed yet (the step runs late in sync) are missing until it has.
Equal patch-ids have no direction. A reapply has the same patch-id as the original change, and a cherry-pick has the same patch-id as its source, so a group can mix fixes, reapplies and backports.
cve_fix_commits (view)¶
Commits that fix a CVE, with how each was identified. The grain is one row per (repo_id, cve_id, commit_id). The current definition comes from migration 0011. Logic:
named: every commit times each element of itscve_ids.tagged:namedminus the (commit, cve) pairs incommit_cve_undoes.matches: three ranked arms.message: the tagged commit itself.cherry_pick: every commit with acommit_cherry_picksrow whosepicked_from_idis a tagged commit. One hop, resolved trailers only.patch_id: every commit of the same repository with the same non-NULLpatch_idas a tagged commit. This includes the tagged commit itself.
The result drops any (commit, cve) present in
commit_cve_undoes, andDISTINCT ON (repo_id, cve_id, commit_id) ... ORDER BY rankkeeps the best arm: message, then cherry_pick, then patch_id.
All CTEs are NOT MATERIALIZED, so a repo_id or cve_id filter reaches the commits scan. Reader: cve_fix_propagation, and through it the cves command.
Column |
Type |
Null |
Source |
How it is derived / written |
|---|---|---|---|---|
|
bigint |
NO |
|
Matches stay within one repository. |
|
text |
NO |
Element of the tagged commit's |
Canonical upper-case |
|
bigint |
NO |
The matched commit |
|
|
text |
NO |
|
|
|
text |
NO |
|
First line of the cleaned message. |
|
timestamptz |
NO |
|
Raw committer time, not a landing time. |
|
text |
YES |
|
NULL for merges, empty diffs, and commits not yet patch-id'd. |
|
text |
NO |
Constant per arm |
|
Notes:
Only
cve_idsis used. GHSA ids never produce fixes.A revert that names the CVE is a fix unless its revert-chain parity is odd (see
commit_cve_undoes).is_revertis not consulted.The
cherry_pickandpatch_idarms do not re-check undo status on the matched commit, except through the final filter, which only removes commits that themselves name the CVE and are undoes. In particular, a commit with the fix's patch-id is reported as apatch_idmatch whatever its history (even after it was reverted); this is also how manual backports are found. The known gap (seecommit_cve_undoes) is different: a revert naming the CVE of such an unnamed backport is reported as amessagefix.The
cherry_pickarm follows one trailer hop from a tagged commit. A chain of unnamed cherry-picks of cherry-picks is not followed. In practicecherry-pick -xcopies the message, so each copy usually names the CVE itself.Commits are listed whether or not they are on a live branch. Use
cve_fix_propagationfor per-branch landing.
cve_fix_propagation (view)¶
When each CVE's fix reached each branch. The grain is one row per (repo_id, cve_id, branch row) where the branch, live or archived, contains at least one commit from cve_fix_commits for that CVE. It inner-joins cve_fix_commits to branch_commits (by commit_id) and branch_repos, then groups by CVE and branch. It was defined in migration 0004 and never changed; it picks up the current cve_fix_commits. Reader: the cves --repo-id N [--cve ID] command (Store.CVEPropagations, internal/store/cves.go). That command filters by repo_id and an upper-cased, trimmed CVE id, and orders by cve, first_landed_at, branch name.
Column |
Type |
Null |
Source |
How it is derived / written |
|---|---|---|---|---|
|
bigint |
NO |
|
|
|
text |
NO |
|
|
|
integer |
NO |
|
|
|
text |
NO |
|
For an archived row this is the original branch name, so several rows can share a name. Use |
|
text |
NO |
|
|
|
text |
YES |
|
NULL for live. |
|
boolean |
NO |
|
As of the last sync. |
|
timestamptz |
NO |
|
Earliest landing of any fix commit (any |
|
interval |
NO |
|
0 on the branch or branches where the fix landed first. Archived rows take part in the minimum. |
|
text[] |
NO |
|
The distinct hashes of the fix commits present on the branch. The view does not specify an order. |
Notes:
Because membership is full, a branch created from another branch after the fix landed contains the fix with the landing time it had on the shared first-parent history. Its
lag_behind_firstis then 0 or close to it, which is not a measure of a backport delay. A real backport (a cherry-pick or a manual patch) lands at its landing time on that branch (branch_commits.landed_at: the running maximum of plausible committer times along the first-parent chain), or at the time of the merge that brought it in.A synthetic epoch
landed_at(an implausible root clock) can surface here if a fix commit landed through such a chain. That is unlikely for real fixes.Branches whose live row was deleted (the ref is gone and the row was not archived) are absent. Archived tips stay included, which is how fixes on end-of-life or rewritten branches remain visible.
Open questions: branches and links¶
What scheme 1 of
membership_schemecomputed is not in the current code, because it predates migration 0007. The description above ("running maximum of raw committer times", 0.4.2) comes from CHANGELOG entries for 0.4.2/0.4.3, not from code.resolveLinksuses a range prefix match (commit_hash >= sha AND commit_hash < sha || 'g'). The code comment says it relies on hex sorting below 'g'. Not verified: how it behaves under every database collation.PatchIDsruns git with the operator's global git configuration (the runner sets-c core.quotepath=off -c log.showSignature=false -c color.ui=never, and the environmentLC_ALL=C,GIT_TERMINAL_PROMPT=0andGIT_SSH_COMMAND=ssh -o BatchMode=yes; internal/gitx/runner.go). Not checked: whether any user diff setting could changediff-tree -poutput enough to change a--stablepatch-id.The order of
cve_fix_propagation.fix_commits: PostgreSQL'sarray_agg(DISTINCT ...)in practice returns sorted values, but the view does not guarantee an order.
Tracked repositories, migrations and rolling labor¶
Conventions used below:
Paths like
store/labor.goare relative tointernal/.now()is PostgreSQL's transaction start time, taken in the transaction that writes the row.clean(s)(store/branches.go) makes git text storable. Each run of invalid UTF-8 bytes becomes one U+FFFD, and each NUL becomes U+FFFD.store.StoredNameis the same function."Fenced" means the write runs in a transaction that first proves the caller still holds its advisory locks (
store/fence.go). If a lock was lost, the transaction writes nothing and returnsErrLockLost. Labor-run writes are fenced by the repository lock (aveloxis_cve.repo:<id>) and by the labor gate (aveloxis_cve.labor-gate; shared, or exclusive under--retry-uncounted) (pipeline/labor.go). The run's outcome stamp is fenced by the repository lock only (pipeline/labor.go). Sync writes are fenced by the repository lock (pipeline/sync.go).repos add/enable/disable, the--recomputerequest andstopwrite unfenced (store/fence.go).ToolVersionisdb.ToolVersion(db/version.go) of the binary that wrote the row.
tracked_repos¶
One row is one Aveloxis repository that the operator chose to track. The key is repo_id (PK tracked_repos_pkey). It holds Aveloxis's aveloxis_data.repos.repo_id by value only, with deliberately no foreign key (db/migrations/0001_init.sql). repos add inserts rows. Adding again re-enables the row and refreshes its URL and tool version. repos enable / repos disable flip enabled. Every sync (also inside run) stamps the sync outcome, and every labor run (also inside run) stamps the labor outcome. Nothing in the code ever deletes a row. Disabling a repository only drops it from the default selection, and its branches, commits and windows are kept. Readers:
repos listandstatus(store/store.go,store/status.go);doctor, enabled rows only (cli/cli.go);sync,resolve,laborandrun, throughselectTracked(cli/sync.go). With no--repo-id, these select every enabled row. With--repo-id, any tracked id is accepted, disabled or not.
Column |
Type |
Null |
Source |
How it is derived / written |
|---|---|---|---|---|
|
bigint |
NO |
|
Comes from the Aveloxis row found by |
|
text |
NO |
|
The value stored in Aveloxis, verbatim. For |
|
boolean |
NO (default true) |
operator (CLI) |
TRUE on insert (column default) and on re-add ( |
|
timestamptz |
NO (default now()) |
wall clock (DB) |
The column default, set once on the first insert. A re-add does not change it. |
|
timestamptz |
YES |
wall clock (DB) |
|
|
text |
YES |
error text |
Written by the same statement. On failure it is |
|
timestamptz |
YES |
wall clock (DB) |
|
|
text |
YES |
error text |
Written by the same statement: |
|
text |
NO |
|
The version of the binary that last ran |
Notes:
RecordSyncandRecordLaboruse the expressionCASE WHEN $2 IS NULL THEN now() ELSE last_*_at END. Solast_*_atmeans "last success", andlast_*_errordescribes the latest recorded attempt.runstamps both outcomes. Labor runs only for repositories whose sync succeeded (cli/sync.go).
schema_migrations¶
This is the migration ledger. One row is one embedded migration file internal/db/migrations/NNNN_name.sql applied to this database. The key is version (PK). Only init-db and migrate write it (cli/cli.go calls db.Migrate and db.Upgrade, db/migrate.go). Upgrade (migrate) first checks, under the lock below, that this table exists; without it, it returns db.ErrNotInitialized and creates nothing. Both refuse a ledger that holds a version newer than any this binary embeds (db.ErrSchemaNewer), applying nothing. Both wait for the session advisory lock aveloxis_cve.migrate by polling pg_try_advisory_lock every 250 ms, never with a blocking wait (db/migrate.go). Migrate alone creates the schema if absent, and creates this table with CREATE TABLE IF NOT EXISTS (db/migrate.go); Upgrade never runs either statement. The ledger table is not created by a migration file. Both then apply each migration whose version is not in the table, in version order. Each migration and its ledger row commit in one transaction (db/migrate.go). Rows are never updated or deleted. Readers:
doctorcompares the highest applied version with the highest embedded version (db.AppliedVersions,db/migrate.go;cli/cli.go);MigrateandUpgrade, to skip versions already applied and to refuse a ledger newer than the binary (db.ErrSchemaNewer,db/migrate.go).
Column |
Type |
Null |
Source |
How it is derived / written |
|---|---|---|---|---|
|
integer |
NO |
migration file name |
The |
|
text |
NO |
migration file name |
The part after |
|
timestamptz |
NO (default now()) |
wall clock (DB) |
The column default: the start time of the migration's transaction. |
|
text |
NO |
|
The version of the binary that applied that migration ( |
Notes:
The table records only versions 1 to 19 (0001 to 0019) at this tool version. It does not checksum the SQL, so an edited migration file is not detected.
labor_recompute_requests¶
One row is one pending or past request to recompute the labor windows of one repository for one window configuration. The key is (repo_id, window_days, step_months) (PK). It was added in migration 0018 (db/migrations/0018_needs_recompute.sql). The row is created or updated by labor --recompute, and by labor --retry-uncounted, which implies a recompute. These commands only record the request (requested goes up by 1; store.MarkRecompute, store/labor.go). --recompute takes no lock to record it. --retry-uncounted records it while holding the labor gate exclusively, after clearing the cache's uncounted rows. Neither marks a window. The request is recorded for every selected repository before any repository runs (cli/sync.go). Each later labor run (labor or run) applies a pending request, under the repository's lock, to the default branch it reads (store.ApplyRecompute, store/labor.go, called at pipeline/labor.go). That marks the configuration's windows needs_recompute and sets applied = requested, in one transaction. Rows are never deleted. Only labor runs read the table, through the UPDATE in ApplyRecompute.
Column |
Type |
Null |
Source |
How it is derived / written |
|---|---|---|---|---|
|
bigint |
NO |
CLI selection |
Each distinct id selected by |
|
integer |
NO |
config |
The window length configured when the request is made ( |
|
integer |
NO |
config |
As for |
|
bigint |
NO (default 0) |
request counter |
Set to 1 on the first request. Each later request runs |
|
bigint |
NO (default 0) |
request counter |
|
Notes:
A request is pending while
requested > applied. A request made before a run'sApplyRecomputeis applied by that run. One made after it (waiting on the row lock if it overlaps that transaction) counts pastappliedand stays pending for the next run (store/labor.go). That next run may be needed because the earlier run may have measured before the request.In a run,
InvalidateWindows(dirty history) andDeleteWindowsNotIn(plan and count scheme) run first.ApplyRecomputeruns next, thenDoneWindowsreads which windows are done (pipeline/labor.go). A run that fails before that point leaves the request pending. Example failures: not synced, no clone,scc --versionfailing, graph load, or lock checks.A request for a repository that never runs labor again, for example one disabled and not named with
--repo-id, stays pending indefinitely.
stop_requests¶
One row is one stop request, made by aveloxis-cve stop (store.RequestStop, store/stop.go; cli/sync.go). The key is request_id (PK). It was added in migration 0019 (db/migrations/0019_stop_requests.sql). The write takes no advisory lock and is unfenced. Requests are recorded one at a time: RequestStop locks the table IN SHARE ROW EXCLUSIVE MODE before its INSERT. That waits for any request being recorded, so ids commit in the order they are taken, and a request with a smaller id cannot commit after a command read a larger one as its marker. Markers and checks only read, and never wait. Rows are never deleted. They hold no personal data.
Who reads it. Every sync, labor, resolve and run of 0.4.32 or later.
Its stop marker. Once connected, each reads the highest
request_idrecorded so far (store.StopMarker).The check. Before each repository, it asks whether a later
request_idexists (store.StopRequestedAfter,cli/sync.go,stopCheck).Ids, not clock times. A step of the database server's clock cannot hide a request.
Where it runs. The check runs wherever the command starts a repository: the dispatch loops of
SyncIDsandRunIDs(pipeline/sync.go,pipeline/labor.go), and the resolve loops ofresolveandrun(cli/sync.go).
When there is a request:
The repositories in progress finish and record their outcomes. The command starts no other repository.
It reports the rest as
not started: stop requested(pipeline.ErrStopped) and exits 0 unless something failed.resolve --allfails instead, naming every repository it did not re-resolve, including those skipped as busy.
Requests that do not stop a command:
one recorded before the command read its marker, which includes a command still connecting;
any request, for a binary older than 0.4.32, which never checks.
Signals. A signal during a check is an interruption, whatever the check found.
When the check fails. A check that fails is not "no request". The command starts nothing more and fails, naming the check (pipeline.ErrStopCheck). run then starts neither resolve nor labor. When the table is missing, as on a schema before 0019, the error says to run migrate.
Column |
Type |
Null |
Source |
How it is derived / written |
|---|---|---|---|---|
|
bigint |
NO |
identity |
|
|
timestamptz |
NO (default |
database clock |
The default, |
|
text |
NO |
|
The version of the binary that ran |
repo_labor_windows¶
One row is one complete sliding window on one repository's default branch, for one window configuration. The row holds:
the window's bounds;
the snapshot commits at the window's start and end;
activity counts;
totals of the full scc snapshot at the end.
The surrogate key is window_id (identity PK). The natural key is repo_labor_windows_key UNIQUE (repo_id, branch_name, window_days, step_months, window_end). A check enforces window_start < window_end.
The row is written only by labor runs (labor, and the labor step of run) in store.WriteWindow (store/labor.go, called at pipeline/labor.go). One transaction writes the window, its repo_labor_rolling rows and its repo_labor_rolling_changed rows, so a window is complete or absent. Rows are never updated in place, except for the needs_recompute flag.
A stale window is marked needs_recompute = TRUE and kept until a later run replaces it. Replacement deletes the marked row and inserts a new one, with a new window_id and computed_at, in that write's transaction (store/labor.go).
Rows are deleted only in two cases:
replacement, as above;
DeleteWindowsNotIn(store/labor.go, called atpipeline/labor.go). It deletes windows of the run's branch and configuration whosewindow_endis not in the current plan, for example after a re-rooted history or a moved first landing.
Deletes cascade to repo_labor_rolling and repo_labor_rolling_changed (FK ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED).
Some windows are never touched by labor: windows of other configurations, except the dirty-history mark, which covers every configuration; windows of a former default-branch name; windows of untracked repositories; and windows of disabled repositories, unless one is named with --repo-id. Readers:
labor itself (
DoneWindows,store/labor.go): windows withNOT needs_recomputecount as done, mappingwindow_endtoend_sha;external consumers. No other command reads this table.
Column |
Type |
Null |
Source |
How it is derived / written |
|---|---|---|---|---|
|
bigint |
NO |
identity |
|
|
bigint |
NO |
|
The repository the labor run was for ( |
|
text |
NO |
|
|
|
integer |
NO |
config |
The length of every window in days ( |
|
integer |
NO |
config |
Consecutive window ends are this many calendar months apart. |
|
timestamptz |
NO |
derived |
|
|
timestamptz |
NO |
derived from |
Computed by |
|
text |
YES |
git first-parent chain + |
|
|
text |
NO |
same |
The same rule applied to |
|
boolean |
NO |
derived |
TRUE when |
|
integer |
NO |
|
|
|
integer |
NO |
derived |
|
|
integer |
NO |
derived |
|
|
bigint |
NO |
scc |
The sum of |
|
bigint |
NO |
scc |
Sum of the snapshot rows' |
|
bigint |
NO |
scc |
Sum of the snapshot rows' |
|
bigint |
NO |
scc |
Sum of the snapshot rows' |
|
bigint |
NO |
scc |
Sum of the snapshot rows' |
|
text |
NO |
|
The last whitespace field of the output (for example |
|
timestamptz |
NO (default now()) |
wall clock (DB) |
The column default: the start time of the |
|
text |
NO |
|
The binary that wrote the window ( |
|
text |
NO |
constant |
Names which files a snapshot counts ( |
|
boolean |
NO (default false) |
derived (stale mark) |
FALSE on insert. It is set TRUE, never cleared in place, by: (1) |
Notes:
Nothing here has a column-level counterpart in Aveloxis. The totals of the latest window correspond to the sum of
aveloxis_data.repo_laborfor the same commit. The code claims this match, and CHANGELOG 0.4.x reports one real repository at 163 files, 16,941 code lines and complexity 277 (CHANGELOG.md).repo_laborholds only Aveloxis's latest snapshot (older ones move toaveloxis_data.repo_labor_history, which aveloxis-cve does not read) and records no commit id. A comparison must therefore pick the window whoseend_shais the default-branch commit at Aveloxis's clone date.Changing
labor.window_daysorlabor.step_monthsstarts a new series. The old configuration's windows stay, never deleted or recomputed by labor. They can still be marked byInvalidateWindows, which ignores the configuration.If the default branch is renamed, the old name's windows stay as they were.
With
Replace, the stored row is deleted only when marked. If an unmarked row with the same key exists, the insert reportsErrWindowExistsand the run fails (store/labor.go).count_schemenames the walk rules but not the host OS.scc-walk3rows written on macOS and on Linux can differ for repositories with chains of 32 to 40 symlinks.
repo_labor_rolling¶
One row is one file of a window's full-repository snapshot at end_sha: a file that scc's checkout walk reaches and that scc counts, with scc's measurement. The key is (window_id, file_path) (PK). FK window_id references repo_labor_windows, ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED. The row is inserted by COPY in store.WriteWindow (store/labor.go), in the window's transaction. It is never updated, and it is deleted only by cascade when its window is replaced or deleted. No command reads it. It is output for consumers.
Which files a snapshot counts (pipeline/labor.go):
git ls-tree -r -z --full-tree <end_sha>lists regular blobs only (modes 100644 and 100755). Symlinks (120000) are listed separately and never measured. Submodule gitlinks and other modes are dropped (gitx/tree.go).labor.Skippedemulates the walk of scc 3.7.0 (gocodewalker, with itsgo-gitignorematcher at the version scc 3.7.0 pins) on a checkout of that tree (labor/walk.go, used bypipeline/labor.go):hidden files are included;
.gitignore,.ignoreand.sccignorerules of the file's directory and every ancestor apply, in that order, and the last match decides;.gitmodulespath =entries skip directories;directories named
.git,.hgor.svnare skipped;a skipped directory is not entered.
Control-file contents come from the clone's blobs, capped at 16 MiB and cut back to the last whole line (
pipeline/labor.go). A control file that is a symlink is read through the link (labor.Links), except.gitmodules(labor.FollowedLink,labor/walk.go).Each remaining file is looked up in, or measured into,
scc_blob_cacheby (blob, base name). A file scc does not count (counted = false) gets no row.Rows are deduplicated by stored path. If two raw paths sanitise to the same text, the first is kept (
pipeline/labor.go).
Column |
Type |
Null |
Source |
How it is derived / written |
|---|---|---|---|---|
|
bigint |
NO |
|
The id returned by the window's INSERT in the same transaction ( |
|
text |
NO |
git tree path at |
The full path from the repository root, with |
|
text |
NO |
derived |
The part of |
|
text |
NO |
git blob id |
The blob object id of the file in the |
|
text |
NO |
scc JSON |
From |
|
integer |
NO |
scc |
From the cache ( |
|
integer |
NO |
scc |
Same. |
|
integer |
NO |
scc |
Same. |
|
integer |
NO |
scc |
Same. |
|
integer |
NO |
scc |
Same. |
|
text |
NO (default 'aveloxis-cve-scc') |
constant |
Never supplied by the COPY ( |
Notes (parity with aveloxis_data.repo_labor):
The column names and meanings match
repo_labor's per-file columns:file_path,file_name,programming_language, and the five line and complexity counts. Aveloxis'sfile_pathis scc'sLocationrelative to its checkout, and itsfile_nameisfilepath.Base(Location)(../aveloxis/internal/collector/analysis.go, relative to the repository root).These
repo_laborcolumns have no counterpart here:repo_labor_id(its key),repo_id(joinwindow_idtorepo_labor_windowsinstead),repo_clone_date,rl_analysis_date,repo_url,tool_version,data_sourceanddata_collection_date.Aveloxis runs scc once over a checkout. Here each file is measured alone, by name and content (
--no-gitignore --no-ignore --no-scc-ignore), and the walk decides beforehand which files count. CLAUDE.md reports that, after the 0.4.10 recompute, the latest windows of redis and repos 132 and 281 equalrepo_laborper file on "all 7 columns" (redis 1538/312685/51652), and that three historical snapshots equal a real scc run ongit archive. Which 7 columns were compared is not stated in the code or changelog.For paths with invalid UTF-8,
clean()replaces each run of bad bytes with one U+FFFD. Aveloxis's paths reachrepo_laborwith one U+FFFD per invalid byte: itsencoding/jsondecoding of scc's output (streamSCCLabor,../aveloxis/internal/collector/analysis.go) replaces each one, as its pgx tracer (safeUTF8, defined in../aveloxis/internal/db/safe_utf8.goand called fromutf8_tracer.go) would. So a path with a run of two or more invalid bytes differs betweenrepo_labor_rollingandrepo_labor.
repo_labor_rolling_changed¶
One row is one file path touched by at least one commit that landed on the default branch in the window. The row compares the path at the window's start snapshot (start_sha) and end snapshot (end_sha) and gives the window's activity on it. The key is (window_id, file_path) (PK), with FK window_id to repo_labor_windows (cascade, deferred) and a CHECK on change_type IN ('A','D','M','N','X'). Rows are built by Labor.changed (pipeline/labor.go) and inserted by COPY in store.WriteWindow (store/labor.go), in the window's transaction. They are never updated and are deleted only by cascade. No command reads them.
Activity comes from store.WindowActivity (store/labor.go). It joins:
branch_commitsof the default branch, withlanded_atin (start, end], or [start, end] for the first window;commits;commit_files, which is sync'sgit log --no-renames --numstatof each non-merge commit against its parent. A rename is a delete plus an add. Merges have no files. Binary files have NULL counts (db/migrations/0002_branches.sql,gitx/log.go).
The rows are grouped by commit_files.file_path.
Column |
Type |
Null |
Source |
How it is derived / written |
|---|---|---|---|---|
|
bigint |
NO |
|
The id from the same transaction. |
|
text |
NO |
|
Each distinct path in the window's activity, in sorted order ( |
|
text |
NO |
derived from git trees |
Decided on the raw trees at start and end (regular files only), not on the walk ( |
|
text |
YES |
git blob id |
The blob at the path in the |
|
text |
YES |
git blob id |
The blob in the |
|
text |
YES |
scc language (cache) |
The end measurement's language if the end file is walked and counted, else the start's ( |
|
integer |
YES |
scc |
Set only if the start file is reached by the start tree's walk and counted by scc ( |
|
integer |
YES |
scc |
Same rule, for the end tree. |
|
integer |
YES |
scc |
Same as |
|
integer |
YES |
scc |
Same as |
|
integer |
YES |
derived |
Both sides measured: end - start. Only the end measured and the path absent at start: end. Only the start measured and the path absent at end: -start. Otherwise NULL, for example when the path is present at both ends but one side is walk-skipped or not counted ( |
|
integer |
YES |
derived |
The same rule as |
|
integer |
NO |
|
|
|
integer |
NO |
|
The same, for lines removed. |
|
integer |
NO |
|
|
|
integer |
NO |
|
|
Notes:
A file that the walk skips, or that scc does not count, still gets a row, with blobs and activity but NULL metrics (
Labor.changed,pipeline/labor.go).Start and end metrics come from the same
scc_blob_cacheas the snapshot, so they equal the snapshot values for the same blob and name.
scc_blob_cache¶
One row is one scc measurement of one file content under one file name, for one scc version and presentation scheme. The key is (blob_sha, file_name, scc_version) (PK). The CHECK scc_blob_cache_counted_consistent requires counted = (programming_language IS NOT NULL). Rows are written by labor runs (labor, and run's labor step) in store.PutMetrics (store/labor.go, called at pipeline/labor.go) after each scc batch, with ON CONFLICT (blob_sha, file_name, scc_version) DO NOTHING. The write is fenced by the repository lock and the labor gate, because the cache is shared by every repository. Rows are never updated. The only delete is labor --retry-uncounted. Holding the labor gate exclusively, it deletes the counted = false rows of the current cache version (store.DeleteUncounted, store/labor.go; cli/sync.go), and then requests a recompute of every enabled repository. Rows of other scc versions are never deleted. Only labor reads the cache (store.CachedMetrics, store/labor.go), for both the snapshot and the changed-file metrics.
How a miss is measured (pipeline/labor.go):
Each file not yet in the cache for its (blob, name, version) key is measured once per run.
Its blob is streamed from the clone (
git cat-file --batch) to its own scratch file<n>/<name>in a temp directory. Content and name are all scc sees, which is exactly the cache key.scc runs once per batch of
scc.batch_filesfiles (default 2000):scc -f json --by-file --no-cocomo --no-gitignore --no-ignore --no-scc-ignore .(labor/scc.go). It honoursscc.path,scc.timeout_seconds(default 1800) andscc.gomaxprocs(default 2, set asGOMAXPROCS).A failing batch is bisected until the failing file is alone (
pipeline/labor.go).
Column |
Type |
Null |
Source |
How it is derived / written |
|---|---|---|---|---|
|
text |
NO |
git blob id |
The blob from |
|
text |
NO |
git tree path |
|
|
text |
NO |
|
|
|
boolean |
NO |
scc output |
TRUE when scc's JSON output listed the scratch file, meaning scc classified it ( |
|
text |
YES |
scc JSON |
NULL exactly when |
|
integer |
YES |
scc |
NULL when not counted ( |
|
integer |
YES |
scc |
Same. |
|
integer |
YES |
scc |
Same. |
|
integer |
YES |
scc |
Same. |
|
integer |
YES |
scc |
Same. |
Notes:
The key does not include the repository, so a blob and name measured for one repository is reused for every other.
Upgrading scc produces a new
scc_versionkey, so every file is measured again under it. Old rows remain. Stored windows are not recomputed by the upgrade (seerepo_labor_windows.scc_version).A cached measurement does not depend on the count scheme. Changing
count_schemerecomputes windows but rescans nothing (CHANGELOG 0.4.10).--retry-uncountedalso re-measures files that scc legitimately does not classify. Those come back FALSE again (docs/configuration.md).