fix: Calculate job speed using last successful data retrieval timestamp

- Use updated_at (last successful data loop) instead of Date.now()
- Speed now reflects actual data retrieval rate, not declining over time
- Updated in table column, monitored job view, and stats row
- Fall back to Date.now() if updated_at is not available

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Alejandro Gutiérrez
2026-01-24 17:04:35 +00:00
parent 5165d65152
commit c2996bef1e

View File

@@ -665,9 +665,11 @@ export default function JobsView({ jobs, onSelectJob, isLoadingJob, onRefresh }:
const isStuck = row.status === 'running' &&
new Date().getTime() - new Date(row.created_at).getTime() > 10 * 60 * 1000;
// For actively running jobs (not stuck), calculate speed from elapsed time
// For actively running jobs (not stuck), calculate speed from last successful data retrieval
if (row.status === 'running' && !isStuck && row.started_at && row.reviews_count) {
const elapsed = (Date.now() - new Date(row.started_at).getTime()) / 1000;
// Use updated_at (last successful data loop) if available, otherwise fall back to Date.now()
const endTime = row.updated_at ? new Date(row.updated_at).getTime() : Date.now();
const elapsed = (endTime - new Date(row.started_at).getTime()) / 1000;
return elapsed > 0 ? row.reviews_count / elapsed : null;
}
return calculateSpeed(row.reviews_count, row.scrape_time);
@@ -679,9 +681,11 @@ export default function JobsView({ jobs, onSelectJob, isLoadingJob, onRefresh }:
const isStuck = isRunning &&
new Date().getTime() - new Date(job.created_at).getTime() > 10 * 60 * 1000;
// For actively running jobs (not stuck), show live speed
// For actively running jobs (not stuck), show live speed based on last successful data retrieval
if (isRunning && !isStuck && job.started_at && job.reviews_count) {
const elapsed = (Date.now() - new Date(job.started_at).getTime()) / 1000;
// Use updated_at (last successful data loop) if available, otherwise fall back to Date.now()
const endTime = job.updated_at ? new Date(job.updated_at).getTime() : Date.now();
const elapsed = (endTime - new Date(job.started_at).getTime()) / 1000;
const speed = elapsed > 0 ? job.reviews_count / elapsed : 0;
const isGood = speed >= 1;
const isSlow = speed < 0.5;
@@ -696,9 +700,10 @@ export default function JobsView({ jobs, onSelectJob, isLoadingJob, onRefresh }:
);
}
// For stuck jobs, show frozen speed in red
// For stuck jobs, show frozen speed in red (use updated_at for accurate speed)
if (isStuck && job.started_at && job.reviews_count) {
const elapsed = (Date.now() - new Date(job.started_at).getTime()) / 1000;
const endTime = job.updated_at ? new Date(job.updated_at).getTime() : Date.now();
const elapsed = (endTime - new Date(job.started_at).getTime()) / 1000;
const speed = elapsed > 0 ? job.reviews_count / elapsed : 0;
return (
<span className="font-medium text-red-600">
@@ -1371,9 +1376,13 @@ export default function JobsView({ jobs, onSelectJob, isLoadingJob, onRefresh }:
</div>
<div className="flex justify-between mt-1 text-xs text-gray-500">
<span>{Math.round(((monitoredJob.reviews_count || 0) / monitoredJob.total_reviews) * 100)}% complete</span>
{monitoredJob.status === 'running' && monitoredJob.reviews_count && monitoredJob.scrape_time && (
{monitoredJob.status === 'running' && monitoredJob.reviews_count && monitoredJob.started_at && (
<span>
{(monitoredJob.reviews_count / monitoredJob.scrape_time).toFixed(1)} reviews/sec
{(() => {
const endTime = monitoredJob.updated_at ? new Date(monitoredJob.updated_at).getTime() : Date.now();
const elapsed = (endTime - new Date(monitoredJob.started_at).getTime()) / 1000;
return elapsed > 0 ? (monitoredJob.reviews_count / elapsed).toFixed(1) : '0';
})()} reviews/sec
</span>
)}
</div>
@@ -1391,10 +1400,20 @@ export default function JobsView({ jobs, onSelectJob, isLoadingJob, onRefresh }:
<div className="bg-white rounded-lg p-3 border border-gray-200">
<div className="text-xs text-gray-500 mb-1">Speed</div>
<div className="text-lg font-semibold text-gray-900">
{monitoredJob.reviews_count && monitoredJob.scrape_time
? `${(monitoredJob.reviews_count / monitoredJob.scrape_time).toFixed(1)}/s`
: '-'
}
{(() => {
if (!monitoredJob.reviews_count || !monitoredJob.started_at) return '-';
// For running jobs, use updated_at; for completed, use scrape_time
if (monitoredJob.status === 'running') {
const endTime = monitoredJob.updated_at ? new Date(monitoredJob.updated_at).getTime() : Date.now();
const elapsed = (endTime - new Date(monitoredJob.started_at).getTime()) / 1000;
return elapsed > 0 ? `${(monitoredJob.reviews_count / elapsed).toFixed(1)}/s` : '-';
}
// For completed/partial jobs, use scrape_time if available
if (monitoredJob.scrape_time) {
return `${(monitoredJob.reviews_count / monitoredJob.scrape_time).toFixed(1)}/s`;
}
return '-';
})()}
</div>
</div>
<div className="bg-white rounded-lg p-3 border border-gray-200">