@@ -16,27 +16,61 @@ const log = getServiceChildLogger('go')
1616const PROXY_SOURCE = 'go-proxy'
1717const PKGGODEV_SOURCE = 'pkg-go-dev'
1818
19- // TODO: filter to critical packages once computed
19+ export interface GoScanCursor {
20+ criticalAfter : string
21+ after : string
22+ }
23+
24+ type GoRow = { purl : string ; name : string }
25+
26+ // Two independent purl-keyset cursors — one for critical packages, one for everything else —
27+ // each ordered/paginated purely by purl so WHERE and ORDER BY always match (no gaps, no
28+ // duplicates). A single query sorted by is_critical DESC with one shared purl cursor was tried
29+ // and rejected: the cursor advances to the last row's purl, and when a batch is critical-heavy
30+ // that purl can be far ahead of unprocessed non-critical rows, permanently excluding them for
31+ // the rest of the run.
2032async function getGoBatch (
2133 qx : QueryExecutor ,
34+ isCritical : boolean ,
2235 afterPurl : string ,
2336 batchSize : number ,
24- ) : Promise < Array < { purl : string ; name : string } > > {
37+ ) : Promise < GoRow [ ] > {
38+ if ( batchSize <= 0 ) return [ ]
2539 return qx . select (
2640 `SELECT purl, name FROM packages
27- WHERE ecosystem = 'go' AND purl > $(after)
28- ORDER BY last_synced_at ASC NULLS FIRST, purl ASC
41+ WHERE ecosystem = 'go' AND is_critical = $(isCritical) AND purl > $(after)
42+ ORDER BY purl ASC
2943 LIMIT $(limit)` ,
30- { after : afterPurl , limit : batchSize } ,
44+ { isCritical , after : afterPurl , limit : batchSize } ,
3145 )
3246}
3347
48+ // Drains not-yet-processed critical packages first, then tops up the rest of the batch with
49+ // non-critical ones — so a rate-limit run only ever starves the non-critical tail.
50+ async function getGoPriorityBatch (
51+ qx : QueryExecutor ,
52+ cursor : GoScanCursor ,
53+ batchSize : number ,
54+ ) : Promise < { rows : GoRow [ ] ; nextCursor : GoScanCursor } > {
55+ const critical = await getGoBatch ( qx , true , cursor . criticalAfter , batchSize )
56+ const nonCritical = await getGoBatch ( qx , false , cursor . after , batchSize - critical . length )
57+
58+ return {
59+ rows : [ ...critical , ...nonCritical ] ,
60+ nextCursor : {
61+ criticalAfter :
62+ critical . length > 0 ? critical [ critical . length - 1 ] . purl : cursor . criticalAfter ,
63+ after : nonCritical . length > 0 ? nonCritical [ nonCritical . length - 1 ] . purl : cursor . after ,
64+ } ,
65+ }
66+ }
67+
3468export async function enrichGoVersionsBatch (
35- afterPurl : string ,
69+ cursor : GoScanCursor ,
3670 batchSize : number ,
37- ) : Promise < string | null > {
71+ ) : Promise < GoScanCursor | null > {
3872 const qx = await getPackagesDb ( )
39- const rows = await getGoBatch ( qx , afterPurl , batchSize )
73+ const { rows, nextCursor } = await getGoPriorityBatch ( qx , cursor , batchSize )
4074 if ( rows . length === 0 ) return null
4175
4276 const { fetchTimeoutMs, proxyConcurrency } = getGoConfig ( )
@@ -89,15 +123,15 @@ export async function enrichGoVersionsBatch(
89123 }
90124
91125 log . info ( { count : rows . length , concurrency : proxyConcurrency } , 'Enriched go versions batch' )
92- return rows [ rows . length - 1 ] . purl
126+ return nextCursor
93127}
94128
95129export async function enrichGoStatusBatch (
96- afterPurl : string ,
130+ cursor : GoScanCursor ,
97131 batchSize : number ,
98- ) : Promise < string | null > {
132+ ) : Promise < GoScanCursor | null > {
99133 const qx = await getPackagesDb ( )
100- const rows = await getGoBatch ( qx , afterPurl , batchSize )
134+ const { rows, nextCursor } = await getGoPriorityBatch ( qx , cursor , batchSize )
101135 if ( rows . length === 0 ) return null
102136
103137 const { fetchTimeoutMs } = getGoConfig ( )
@@ -137,5 +171,5 @@ export async function enrichGoStatusBatch(
137171 }
138172
139173 log . info ( { count : rows . length } , 'Enriched go status batch' )
140- return rows [ rows . length - 1 ] . purl
174+ return nextCursor
141175}
0 commit comments