Skip to content

Commit e4bbce7

Browse files
committed
fix: align packages-db writers with Tinybird ENGINE_VER semantics
Signed-off-by: Joana Maia <jmaia@contractor.linuxfoundation.org>
1 parent 5ed5eab commit e4bbce7

6 files changed

Lines changed: 121 additions & 21 deletions

File tree

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
-- rank_packages() now bumps last_synced_at on every UPDATE that touches a
2+
-- DS-exported field (impact, is_critical, last_rank_pass_at). last_synced_at
3+
-- is the Tinybird ENGINE_VER for the packages datasource; without this bump,
4+
-- ReplacingMergeTree may keep an older row when criticality changes without
5+
-- any other write path touching the package row.
6+
7+
CREATE OR REPLACE FUNCTION rank_packages(
8+
weight_downloads numeric DEFAULT 0.25,
9+
weight_dependent_packages numeric DEFAULT 0.25,
10+
weight_transitive numeric DEFAULT 0.50,
11+
critical_top_n_by_ecosystem jsonb DEFAULT '{"npm":400000,"go":100000,"maven":200000,"pypi":100000,"nuget":50000,"cargo":75000}'::jsonb
12+
)
13+
RETURNS TABLE(scored_rows int, ranked_rows int)
14+
LANGUAGE plpgsql AS $$
15+
DECLARE
16+
n_scored int;
17+
n_ranked int;
18+
BEGIN
19+
-- Step 1: score
20+
WITH percentile_scores AS (
21+
SELECT
22+
id,
23+
(
24+
weight_downloads * PERCENT_RANK() OVER (
25+
PARTITION BY ecosystem ORDER BY LOG(1 + COALESCE(downloads_last_30d, 0)))
26+
27+
+ weight_dependent_packages * PERCENT_RANK() OVER (
28+
PARTITION BY ecosystem ORDER BY LOG(1 + COALESCE(dependent_count, 0)))
29+
30+
+ weight_transitive * PERCENT_RANK() OVER (
31+
PARTITION BY ecosystem ORDER BY LOG(1 + COALESCE(transitive_dependent_count, 0)))
32+
)::numeric(10, 4) AS new_impact
33+
FROM packages
34+
WHERE ecosystem IN (SELECT jsonb_object_keys(critical_top_n_by_ecosystem))
35+
)
36+
UPDATE packages p
37+
SET impact = ps.new_impact,
38+
last_synced_at = NOW()
39+
FROM percentile_scores ps
40+
WHERE p.id = ps.id
41+
AND p.impact IS DISTINCT FROM ps.new_impact;
42+
43+
GET DIAGNOSTICS n_scored = ROW_COUNT;
44+
45+
-- Step 2: rank + flag
46+
WITH ranked AS (
47+
SELECT
48+
id, ecosystem,
49+
ROW_NUMBER() OVER (
50+
PARTITION BY ecosystem
51+
ORDER BY impact DESC NULLS LAST, id
52+
) AS r
53+
FROM packages
54+
WHERE purl IS NOT NULL
55+
AND ecosystem IN (SELECT jsonb_object_keys(critical_top_n_by_ecosystem))
56+
),
57+
flagged AS (
58+
SELECT
59+
id, r,
60+
COALESCE(
61+
r <= (critical_top_n_by_ecosystem ->> ecosystem)::int,
62+
FALSE
63+
) AS new_is_critical
64+
FROM ranked
65+
)
66+
UPDATE packages p
67+
SET rank_in_ecosystem = f.r,
68+
is_critical = f.new_is_critical,
69+
last_synced_at = NOW()
70+
FROM flagged f
71+
WHERE p.id = f.id
72+
AND (
73+
p.rank_in_ecosystem IS DISTINCT FROM f.r
74+
OR p.is_critical IS DISTINCT FROM f.new_is_critical
75+
);
76+
77+
GET DIAGNOSTICS n_ranked = ROW_COUNT;
78+
79+
-- Step 2.5: spotlight overrides
80+
UPDATE packages p
81+
SET is_critical = TRUE,
82+
last_synced_at = NOW()
83+
FROM package_criticality_spotlight s
84+
WHERE p.ecosystem = s.ecosystem
85+
AND (p.namespace IS NOT DISTINCT FROM s.namespace)
86+
AND p.name = s.name
87+
AND p.is_critical = FALSE;
88+
89+
-- Step 3: stamp last_rank_pass_at unconditionally
90+
UPDATE packages
91+
SET last_rank_pass_at = NOW(),
92+
last_synced_at = NOW()
93+
WHERE ecosystem IN (SELECT jsonb_object_keys(critical_top_n_by_ecosystem));
94+
95+
RETURN QUERY SELECT n_scored, n_ranked;
96+
END;
97+
$$;

services/apps/packages_worker/src/deps-dev/workflows/ingestRepos.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,13 @@ const REPOS_PG_COLUMNS = [
5858
'open_issues',
5959
]
6060

61+
// last_synced_at is intentionally left NULL on seed — it is owned by the GitHub
62+
// enricher as its freshness signal. created_at / updated_at use their column defaults.
6163
const REPOS_MERGE_SQL = `
6264
INSERT INTO repos (url, raw_project_type, raw_project_name, host, owner, name,
63-
description, homepage, stars, forks, open_issues, last_synced_at)
65+
description, homepage, stars, forks, open_issues)
6466
SELECT s.canonical_url, s.raw_project_type, s.raw_project_name, s.host, s.owner, s.name,
65-
s.description, s.homepage, s.stars, s.forks, s.open_issues, NOW()
67+
s.description, s.homepage, s.stars, s.forks, s.open_issues
6668
FROM staging.osspckgs_repos_raw s
6769
ON CONFLICT (url) DO NOTHING
6870
`

services/libs/data-access-layer/src/osspckgs/repos.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,20 @@ export async function findRepoIdsByUrl(
1515
* Inserts or updates a repo row keyed on url.
1616
* Uses COALESCE so richer data from other enrichers (GitHub, deps.dev) is never
1717
* overwritten with nulls from a partial write.
18+
* `last_synced_at` is intentionally NOT touched here — that column is owned by the
19+
* GitHub enricher as its freshness signal. Maven discovery only stamps updated_at.
1820
* Returns the repo id.
1921
*/
2022
export async function upsertRepo(qx: QueryExecutor, item: IDbRepoUpsert): Promise<number> {
2123
const row = await qx.selectOne(
2224
`
23-
INSERT INTO repos (url, host, owner, name, last_synced_at, updated_at)
24-
VALUES ($(url), $(host), $(owner), $(name), NOW(), NOW())
25+
INSERT INTO repos (url, host, owner, name, updated_at)
26+
VALUES ($(url), $(host), $(owner), $(name), NOW())
2527
ON CONFLICT (url) DO UPDATE SET
26-
host = COALESCE(EXCLUDED.host, repos.host),
27-
owner = COALESCE(EXCLUDED.owner, repos.owner),
28-
name = COALESCE(EXCLUDED.name, repos.name),
29-
last_synced_at = NOW(),
30-
updated_at = NOW()
28+
host = COALESCE(EXCLUDED.host, repos.host),
29+
owner = COALESCE(EXCLUDED.owner, repos.owner),
30+
name = COALESCE(EXCLUDED.name, repos.name),
31+
updated_at = NOW()
3132
RETURNING id
3233
`,
3334
item,

services/libs/data-access-layer/src/packages/osv.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ export async function getRangesForPackages(qx: QueryExecutor, ids: number[]): Pr
243243
export async function flipVulnerableFlags(qx: QueryExecutor, ids: number[]): Promise<number> {
244244
if (ids.length === 0) return 0
245245
return qx.result(
246-
`UPDATE packages SET has_critical_vulnerability = TRUE
246+
`UPDATE packages SET has_critical_vulnerability = TRUE, last_synced_at = NOW()
247247
WHERE id IN ($(ids:csv)) AND has_critical_vulnerability = FALSE`,
248248
{ ids },
249249
)
@@ -252,7 +252,7 @@ export async function flipVulnerableFlags(qx: QueryExecutor, ids: number[]): Pro
252252
export async function clearSafeFlags(qx: QueryExecutor, ids: number[]): Promise<number> {
253253
if (ids.length === 0) return 0
254254
return qx.result(
255-
`UPDATE packages SET has_critical_vulnerability = FALSE
255+
`UPDATE packages SET has_critical_vulnerability = FALSE, last_synced_at = NOW()
256256
WHERE id IN ($(ids:csv)) AND has_critical_vulnerability = TRUE`,
257257
{ ids },
258258
)

services/libs/tinybird/datasources/repos.datasource

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ DESCRIPTION >
1515
- `watchers` is the watcher count (0 if not yet enriched).
1616
- `openIssues` is the number of open issues (0 if not yet enriched).
1717
- `lastCommitAt` is the timestamp of the most recent commit (NULL if unknown).
18-
- `archived` is 1 if the repository is archived, 0 otherwise (0 default until enriched).
19-
- `disabled` is 1 if the repository is disabled, 0 otherwise (0 default until enriched).
20-
- `isFork` is 1 if this repository is a fork of another, 0 otherwise (0 default until enriched).
18+
- `archived` is 1 if the repository is archived, 0 if not, NULL until the GitHub enricher runs (deps.dev seed does not expose this).
19+
- `disabled` is 1 if the repository is disabled, 0 if not, NULL until the GitHub enricher runs.
20+
- `isFork` is 1 if this repository is a fork of another, 0 if not, NULL until the GitHub enricher runs.
2121
- `createdAt` is the repository creation date on GitHub/GitLab — a domain timestamp, not a row-insert timestamp.
2222
- `homepage` is the project homepage URL (empty string if not provided).
2323
- `rawProjectType` is the deps.dev project type string (e.g. 'GITHUB', 'GITLAB') for identity resolution.
@@ -42,9 +42,9 @@ SCHEMA >
4242
`watchers` Int32 `json:$.record.watchers` DEFAULT 0,
4343
`openIssues` Int32 `json:$.record.open_issues` DEFAULT 0,
4444
`lastCommitAt` Nullable(DateTime64(3)) `json:$.record.last_commit_at`,
45-
`archived` UInt8 `json:$.record.archived` DEFAULT 0,
46-
`disabled` UInt8 `json:$.record.disabled` DEFAULT 0,
47-
`isFork` UInt8 `json:$.record.is_fork` DEFAULT 0,
45+
`archived` Nullable(UInt8) `json:$.record.archived`,
46+
`disabled` Nullable(UInt8) `json:$.record.disabled`,
47+
`isFork` Nullable(UInt8) `json:$.record.is_fork`,
4848
`createdAt` Nullable(DateTime64(3)) `json:$.record.created_at`,
4949
`homepage` String `json:$.record.homepage` DEFAULT '',
5050
`rawProjectType` String `json:$.record.raw_project_type` DEFAULT '',

services/libs/tinybird/datasources/versions.datasource

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ DESCRIPTION >
99
- `name` is the package name — denormalized from packages for fast dependency resolution without a join.
1010
- `number` is the version string (e.g. '1.2.3', '2.0.0-beta.1').
1111
- `publishedAt` is when this version was published to the registry (NULL if unknown).
12-
- `isLatest` is 1 if this is the current latest stable version according to the registry, 0 otherwise (0 default until enriched).
13-
- `isYanked` is 1 if this version was retracted/yanked from the registry, 0 otherwise (0 default until enriched).
12+
- `isLatest` is 1 if this is the current latest stable version, 0 if not, NULL until registry workers enrich (deps.dev seed does not expose this).
13+
- `isYanked` is 1 if this version was retracted/yanked from the registry, 0 if not, NULL until registry workers enrich.
1414
- `isPrerelease` is 1 if the version string contains a pre-release identifier (alpha, beta, rc, etc.), 0 otherwise.
1515
- `licenses` is an array of SPDX-normalized license identifiers for this specific version (may differ from the package-level value).
1616
- `downloadCount` is the per-version download count where available (npm, crates); 0 if not tracked.
@@ -25,8 +25,8 @@ SCHEMA >
2525
`name` String `json:$.record.name`,
2626
`number` String `json:$.record.number`,
2727
`publishedAt` Nullable(DateTime64(3)) `json:$.record.published_at`,
28-
`isLatest` UInt8 `json:$.record.is_latest` DEFAULT 0,
29-
`isYanked` UInt8 `json:$.record.is_yanked` DEFAULT 0,
28+
`isLatest` Nullable(UInt8) `json:$.record.is_latest`,
29+
`isYanked` Nullable(UInt8) `json:$.record.is_yanked`,
3030
`isPrerelease` UInt8 `json:$.record.is_prerelease` DEFAULT 0,
3131
`licenses` Array(String) `json:$.record.licenses[:]` DEFAULT [],
3232
`downloadCount` UInt64 `json:$.record.download_count` DEFAULT 0,

0 commit comments

Comments
 (0)