@@ -755,34 +755,30 @@ function computeChunkEta(job: any) {
755755}
756756
757757// ETA for the entire job (all remaining files + merges).
758- // Uses job.started_at so rate includes both loading and merging time naturally.
758+ // Driven by file progress, not pgRows-vs-bqRows: pgRows is the merged delta and
759+ // converges to staging size (after dedup), never to the raw BQ scan count — so a
760+ // bqRows target overstates remaining work, and a pgRows/total-elapsed rate is diluted
761+ // by the export+download+staging phases where pgRows is still 0. Files are the unit
762+ // that actually advances, and elapsed already amortizes every per-file phase.
759763// eslint-disable-next-line @typescript-eslint/no-explicit-any
760764function computeTotalEta ( job : any ) {
761765 if ( ! [ 'loading' , 'merging' ] . includes ( job . status ) ) return null
762- const pgRows = Number ( job . row_count_pg ) || 0
763766 const trc = job . table_row_counts ?? { }
764767 const filesDone = Number ( trc [ 'progress:done' ] ) || 0
765768 const filesTotal = Number ( trc [ 'progress:total' ] ) || 0
766- const stagingRows = Number ( job . row_count_staging ) || 0
767- if ( ! filesDone || ! filesTotal || ! pgRows || ! job . started_at ) return null
768- const bqRows = Number ( job . row_count_bq ) || 0
769- const rowsPerFile =
770- bqRows > 0 && filesTotal > 0
771- ? bqRows / filesTotal
772- : stagingRows > 0
773- ? stagingRows / filesDone
774- : 0
775- if ( ! rowsPerFile ) return null
776- const estimatedTotal = bqRows > 0 ? bqRows : rowsPerFile * filesTotal
777- if ( pgRows >= estimatedTotal ) return null
769+ if ( ! filesDone || ! filesTotal || ! job . started_at ) return null
770+ if ( filesDone >= filesTotal ) return null
778771 const elapsedMs = Date . now ( ) - new Date ( job . started_at ) . getTime ( )
779- if ( elapsedMs < 10000 || pgRows < 1000 ) return null
780- const ratePerMs = pgRows / elapsedMs
781- if ( ratePerMs <= 0 ) return null
772+ if ( elapsedMs < 10000 ) return null
773+ const msPerFile = elapsedMs / filesDone
774+ const remainingMs = msPerFile * ( filesTotal - filesDone )
775+ if ( remainingMs <= 0 ) return null
776+ // Throughput display: staging rows landed per minute over the whole job.
777+ const stagingRows = Number ( job . row_count_staging ) || 0
782778 return {
783- ms : ( estimatedTotal - pgRows ) / ratePerMs ,
784- ratio : Math . min ( pgRows / estimatedTotal , 1 ) ,
785- ratePerMin : ratePerMs * 60000 ,
779+ ms : remainingMs ,
780+ ratio : filesDone / filesTotal ,
781+ ratePerMin : stagingRows > 0 ? ( stagingRows / elapsedMs ) * 60000 : 0 ,
786782 }
787783}
788784
0 commit comments