Upgrading
How to move an existing deployment from any earlier release to the
current one — and, just as important, which repairs aveloxis migrate
does not do for you.
The two halves of an upgrade
aveloxis migrateapplies every schema change and every one-shot SQL backfill between your old version and the new one. It is idempotent and safe to re-run. Since v0.28.4 the expensive one-shot data steps record their completion inaveloxis_ops.migration_ledger, so a later migrate is seconds — only the first migrate across a large version gap pays the full walk.Operator-run heal commands repair data the migrate cannot: they call the GitHub/GitLab APIs, run for hours at fleet scale, or need a judgment call (merging duplicate repositories, for example). They are deliberately not migrations. The table further down lists every one with the release that introduced it, so you can skip the ones that predate the version you are coming from.
Find the version you are coming from before you start — it decides which rows of that table apply:
SELECT schema_version FROM aveloxis_ops.schema_meta;
If that fails with relation "aveloxis_ops.schema_meta" does not exist,
the database predates v0.14.5 (the release that introduced the stamp).
Every row of the table below applies to it — the earliest entry is
v0.23.6 — so you do not strictly need the exact version, but the binary
that last wrote data tells you where you were: aveloxis serve migrates
at startup, so the newest tool_version on collected rows is the last
binary that migrated the schema:
SELECT tool_version
FROM aveloxis_data.repo_info
ORDER BY data_collection_date DESC NULLS LAST, repo_info_id DESC
LIMIT 1;
The standard ladder
cd <checkout> && go install ./cmd/aveloxis
aveloxis version # confirm the new binary
aveloxis stop all # serve, web, api (also cleans stale pidfiles)
aveloxis migrate --skip-views # schema + ledgered backfills; matviews later
# ... operator-run heals from the table below, in order ...
aveloxis refresh-views # rebuild the materialized views (add --aggregates for the dm_ tables; slow)
aveloxis start all
Run aveloxis migrate explicitly rather than letting aveloxis serve
migrate at startup: web and api never migrate and log an ERROR on a
schema-version mismatch, and (since v0.27.131) serve trusts the
schema-version stamp and skips the migration walk entirely once it
matches — so after any hand edit to the schema, run aveloxis migrate
once. Verify the stamp matches the binary afterwards:
SELECT schema_version FROM aveloxis_ops.schema_meta; -- equals `aveloxis version`
What to watch for in the migrate output
Migration steps are fail-closed (v0.19.4): a failing step fails the
migrate — every remaining step still runs, the error lists every
failed step, and serve refuses to start until they are fixed. Three
steps are deliberately warn-only because they wait for operator action
(a few other best-effort steps — the commits dedup index, the .git
suffix cleanup, the tool_version default sweep — also warn rather than
fail, but need nothing from you and simply re-run on the next migrate):
Log line |
Since |
What it means |
Action |
|---|---|---|---|
|
v0.25.32 |
|
|
|
v0.27.18 |
a writer bypassed the snapshot-replace path |
investigate the duplicates, then re-run migrate |
|
v0.25.30 (the index itself is v0.18.30; it was fatal from v0.19.4 until the v0.25.30 skip) |
the extension needs superuser to create |
performance only (monitor search falls back to sequential scans); |
One more gate is fail-closed rather than warn-only — it waits for
serve to be stopped:
Log line |
Since |
What it means |
Action |
|---|---|---|---|
|
v0.28.18 |
duplicate list registrations exist and a running |
stop |
The first migrate across a large gap can take a while. The long poles
(“ledgered” = recorded in migration_ledger after it completes and never
walked again; the others re-run on every migrate but converge to a cheap
no-op once their work is done):
Step |
Since |
Ledgered? |
Cost |
|---|---|---|---|
|
v0.25.6 |
yes |
scales with the commits table (about an hour at ~470M rows) |
|
v0.27.7 |
yes |
keyset windows over |
GitLab force-full flag (main-path comment-drop heal) |
v0.27.37 |
yes (v0.28.18) — seeded on upgrade from ≥ v0.27.37, so it does not re-run |
instant; on a database last migrated BELOW v0.27.37 it flags every collected GitLab repo for one full pass on its next cycle |
message-bridge |
v0.27.15 |
yes |
45–75 min on a fleet-scale |
|
v0.27.17 |
no — runs every migrate, a no-op once consolidated |
seconds to minutes once the FK-child indexes exist (v0.28.15); the first pass deletes one row per duplicate group with a deferred FK check per child table |
|
v0.27.38 |
no — but fast-skips once its final step has run |
keyset windows over |
PR meta-link backfill ( |
v0.27.104 |
yes |
tens of minutes over tens of millions of PR ids |
Configuration compatibility
An older aveloxis.json keeps working: unknown keys are ignored and
every new key takes its documented default (see
Configuration). Defaults that changed — check
whether you relied on the old value:
Key |
Old default |
New default |
Since |
|---|---|---|---|
|
30 |
0 — scancode subprocesses are killed immediately on |
v0.23.7 |
|
|
|
v0.26.0 |
|
silently fell back to Saturday |
honored (alias of |
v0.27.96 |
|
off |
on — lockfile closures + transitive findings + real SBOM graphs |
v0.27.136 |
|
(every repo on the same cadence) |
6 — archived repos recollect six times less often |
v0.28.1 |
Two behavior changes that need no configuration but are worth knowing:
since v0.27.139 incremental collection anchors since on the previous
round’s last_collected (the pre-v0.27.139 now − days_until_recollect
window silently skipped items last-updated between rounds), so the first
post-upgrade cycle per repo is transitional and heal-collection-gaps
below covers the history; and path values in aveloxis.json are never
$HOME-expanded — use absolute paths.
Operator-run heals, with the release that introduced each
Run every row whose Since cell names any release newer than the
version you are coming from, in table order. Several rows list later
extensions in parentheses — row 4 gained pull_request_repo owners in
v0.27.104 and row 6 gained platform_repo_id in v0.27.102, both long
after the row’s first release — so comparing against the first version
alone would skip a repair that does apply to you. Every command is idempotent and resumable;
re-running is always safe. Rows marked fleet-scale take hours on a
100K-repo fleet and minutes on a small one.
Order |
Command |
Since |
Repairs |
When |
|---|---|---|---|---|
1 |
|
v0.23.6 |
re-installs scc / scorecard / scancode and injects |
any install that predates v0.23.6 |
2 |
|
v0.25.32 (index precondition v0.28.18) |
case-variant duplicate repositories; the migrate skips the |
only when the migrate warns; needs the new binary’s migrate to have built the |
3 |
event-cohort SQL (below) |
v0.26.3 |
PR events silently dropped on quiet repos by the two-pass ETag self-alias bug; flags each affected repo for one full recollect |
any repo collected before v0.26.3 |
4 |
|
v0.26.5 (keyset batching v0.26.6; |
assignee / reviewer / PR-meta / PR-repo |
yes; fleet-scale — use |
5 |
|
v0.27.38 (probe index v0.27.67; per-pass stamps v0.28.1; cursor walk v0.28.8) |
message rows overwritten by the cross-kind platform-ID collision; the migrate captures the worklist, this consumes it |
yes; it also drains each repo’s leftover staging — run |
6 |
|
v0.27.79 ( |
description / languages / archived / |
yes; ~1.6 h per 94K repos |
7 |
|
v0.27.105 |
|
recommended; fleet-scale, marker-resumable, safe beside |
8 |
|
v0.27.140 (safe beside |
issues / PRs lost to the pre-v0.27.139 blind-window |
required for any repo collected before v0.27.139; must run on the new binary; fleet-scale (~65 h at |
9 |
|
— |
the materialized views over the healed data; add |
after rows 3–8 settle (the weekly rebuild also covers the views, and the |
10 |
|
v0.27.39 (index precondition v0.28.18) |
stranded repositories (a |
periodic, until the residue drains; its consolidation arms skip with a warning (and the run exits nonzero) until the new binary’s migrate has built the |
11 |
|
v0.28.1 |
the explicit “gone” state for deleted or privatized repositories that still hold data |
optional — display honesty only |
12 |
|
v0.27.4 (scan-side version normalization v0.27.72) |
empty OSV stub findings and malformed-purl false positives |
optional — the scheduled scans self-heal on their normal cadence |
13 |
|
v0.25.7 (forge-resolved lookups v0.27.152) |
registers Apache |
only if you enable mailing-list collection — see below |
Skipped as instance-specific: the load-foundation-* importers (only if
you track a foundation’s whole catalog) and register-mailing-list
(curated non-Apache lists).
The v0.26.3 event-cohort SQL
Before v0.26.3 the issue-event and PR-event feeds paginated the same
GitHub endpoint twice; on any repository where nothing changed between
the two passes the second one got a 304 and the entire PR-event history
was silently dropped. “Has PRs but no PR events” can be legitimate for
small quiet repos, so this is deliberately not an automatic migration.
Flag the affected cohort for one full recollect (lower the HAVING
threshold to taste):
UPDATE aveloxis_ops.collection_queue q
SET force_full_collect = TRUE
FROM (
SELECT pr.repo_id
FROM aveloxis_data.pull_requests pr
WHERE NOT EXISTS (SELECT 1 FROM aveloxis_data.pull_request_events e
WHERE e.repo_id = pr.repo_id)
GROUP BY pr.repo_id
HAVING COUNT(DISTINCT pr.pull_request_id) >= 50
) sub
WHERE q.repo_id = sub.repo_id
AND q.force_full_collect = FALSE;
Each flagged repo re-walks its full event history on its next cycle.
Mailing lists on an existing catalog
aveloxis load-apache-lists never inserts repositories. For each Apache
PMC it looks the PMC’s primary repository up in your catalog (URL
variants first, then a github.com redirect probe for renamed projects)
and registers that PMC’s lists only when the repository is found;
everything else is counted as skipped. On a catalog that already holds
some Apache repositories it therefore registers exactly those PMCs —
load-foundation-core-repos is only needed if you want every PMC’s
flagship repository imported first. A PMC whose sibling repository you
track (say apache/arrow-rs without apache/arrow) is skipped, because
mailing-list bodies attach to the PMC’s primary repository. The command
needs outbound network access (Apache’s projects.json /
podlings.json, lists.apache.org for list enumeration, and github.com
for the redirect probe) and moves each linked repository into an
Apache PMC: <slug> repo group.
aveloxis load-apache-lists --dry-run # [pmc] list → repo_id per PMC you hold; the rest are "skipped"
aveloxis load-apache-lists
Then enable the worker in aveloxis.json and restart serve:
{
"collection": {
"mailing_list_enabled": true,
"mailing_list_polite_email": "you@example.org",
"mailing_list_backfill_months": 6
}
}
mailing_list_polite_email is the contact address sent to the archive
admins in the User-Agent; mailing_list_backfill_months bounds the
first pass per list (0 = full history from each list’s first month).
aveloxis mailing-list-stats shows coverage as lists drain;
aveloxis verify-mailing-list reports which classification and routing
branches have produced rows. Run the mailing-list ingestion after the
issue / PR heals above have settled: messages are projected onto the
issues and pull requests already in the database, so a more complete
catalog links more mail. Full design in
Mailing-List Ingestion.
Where the per-release detail lives
Per-release notes are on the GitHub releases page. Every command above is documented in Commands; recovery procedures for specific incidents are in Troubleshooting; the transitional v0.25.x distribution-tracking knobs and their deprecation horizon are in Configuration.