@@ -27,24 +27,24 @@ interface BatchResult {
2727
2828async function processBatch (
2929 qx : QueryExecutor ,
30- offset : number ,
3130 config : ReturnType < typeof getPomFetcherConfig > ,
3231) : Promise < BatchResult > {
3332 const packages = await listMavenPackagesToEnrich ( qx , {
3433 limit : config . batchSize ,
35- offset,
34+ offset : 0 ,
3635 staleDays : config . staleDays ,
3736 } )
3837
3938 if ( packages . length === 0 ) {
4039 return { processed : 0 , skipped : 0 , errors : 0 }
4140 }
4241
43- log . info ( { offset , count : packages . length } , 'Processing POM batch...' )
42+ log . info ( { count : packages . length } , 'Processing POM batch...' )
4443
4544 let processed = 0
4645 let skipped = 0
4746 let errors = 0
47+ const PROGRESS_EVERY = 25
4848
4949 // Process in small concurrent groups to be polite to Maven Central
5050 for ( let i = 0 ; i < packages . length ; i += config . concurrency ) {
@@ -62,16 +62,30 @@ async function processBatch(
6262 }
6363
6464 try {
65- log . info ( { groupId, artifactId } , 'Fetching POM...' )
66-
6765 // Step 1: resolve latest version from maven-metadata.xml
6866 const version = await resolveLatestVersion ( groupId , artifactId )
6967 if ( ! version ) {
7068 log . warn ( { groupId, artifactId } , 'Could not resolve latest version, skipping' )
69+ // Upsert a minimal record so last_synced_at is set — prevents this package
70+ // from re-appearing in every batch within the same pass.
71+ // ingestionSource 'pom_fetcher_no_version' marks that it was tried but had no
72+ // resolvable version on Maven Central (404 on maven-metadata.xml).
73+ await upsertPackage ( qx , {
74+ purl : `pkg:maven/${ groupId } /${ artifactId } ` ,
75+ ecosystem : 'maven' ,
76+ namespace : groupId ,
77+ name : artifactId ,
78+ description : null ,
79+ homepage : null ,
80+ declaredRepositoryUrl : null ,
81+ licenses : null ,
82+ licensesRaw : null ,
83+ latestVersion : null ,
84+ ingestionSource : 'pom_fetcher_no_version' ,
85+ } )
7186 skipped ++
7287 return
7388 }
74- log . info ( { groupId, artifactId, version } , 'Version resolved, extracting POM...' )
7589
7690 // Step 2: fetch + resolve POM (follows parent chain)
7791 const result = await extractArtifact ( groupId , artifactId , version , ( msg ) => {
@@ -83,19 +97,6 @@ async function processBatch(
8397 errors ++
8498 return
8599 }
86- log . info (
87- {
88- groupId,
89- artifactId,
90- version,
91- licenses : result . licenses ,
92- scmUrl : result . scmUrl ,
93- developers : result . developers . length ,
94- contributors : result . contributors . length ,
95- parentHops : result . parentHops ,
96- } ,
97- 'POM extracted, upserting...' ,
98- )
99100
100101 // Step 3: upsert into `packages`
101102 // purl at package level has no version (package-level identifier)
@@ -151,6 +152,17 @@ async function processBatch(
151152 }
152153 } ) ,
153154 )
155+
156+ // done = packages processed so far (based on loop index, always accurate)
157+ const done = i + group . length
158+ const prevDone = i
159+ const crossedBoundary = Math . floor ( done / PROGRESS_EVERY ) > Math . floor ( prevDone / PROGRESS_EVERY )
160+ if ( crossedBoundary || done === packages . length ) {
161+ log . info (
162+ { done, total : packages . length , processed, skipped, errors } ,
163+ `Progress: ${ done } /${ packages . length } ` ,
164+ )
165+ }
154166 }
155167
156168 return { processed, skipped, errors }
@@ -170,21 +182,20 @@ export async function runPomEnrichmentLoop(
170182 config : ReturnType < typeof getPomFetcherConfig > ,
171183 isShuttingDown : ( ) => boolean ,
172184) : Promise < void > {
173- let offset = 0
174185 let totalProcessed = 0
175186 let totalSkipped = 0
176187 let totalErrors = 0
177188 let passNumber = 0
178189 let passStartedAt = Date . now ( )
179190
180191 while ( ! isShuttingDown ( ) ) {
181- if ( offset === 0 ) {
192+ if ( totalProcessed + totalSkipped + totalErrors === 0 ) {
182193 passNumber ++
183194 passStartedAt = Date . now ( )
184195 log . info ( { pass : passNumber } , 'Starting pass' )
185196 }
186197
187- const result = await processBatch ( qx , offset , config )
198+ const result = await processBatch ( qx , config )
188199
189200 if ( result . processed + result . skipped + result . errors === 0 ) {
190201 // Nothing left in this pass — log summary and sleep
@@ -200,21 +211,18 @@ export async function runPomEnrichmentLoop(
200211 `Pass complete. Sleeping ${ config . idleSleepSec } s before next pass.` ,
201212 )
202213 await new Promise ( ( r ) => setTimeout ( r , config . idleSleepSec * 1000 ) )
203- offset = 0
204214 totalProcessed = 0
205215 totalSkipped = 0
206216 totalErrors = 0
207- passStartedAt = Date . now ( )
208217 continue
209218 }
210219
211220 totalProcessed += result . processed
212221 totalSkipped += result . skipped
213222 totalErrors += result . errors
214- offset += config . batchSize
215223
216224 log . info (
217- { offset , processed : result . processed , skipped : result . skipped , errors : result . errors } ,
225+ { processed : result . processed , skipped : result . skipped , errors : result . errors , totalProcessed , totalSkipped , totalErrors } ,
218226 'Batch complete' ,
219227 )
220228 }
0 commit comments