Fix(datasets): size the Sentry monitor's maxRuntime to the scheduled rows

Rows update one after another, each with up to 30 minutes, so four datasets
can legitimately take two hours while the monitor failed a run after one.
maxRuntime is now every scheduled row's timeout plus a 10-minute margin.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
jason5ng32andClaude Opus 5.5 committed 2026-10-05 02:44:05 +08:00
1 parent ce21aa33bb
commit 1db89e1dc4
2 files changed
+24 -2

No files matched your search

+13 -1
View File
@@ -341,6 +341,18 @@ export const rowsMissingACheck = async (rows, { pattern, now = new Date() }) =>
return stale;
};
/**
* Sentry Crons settings for a run updating `rowCount` datasets in turn: its
* longest legitimate run is every row at its timeout, plus a margin, so a
* healthy run is never marked failed. Exported for tests.
*/
export const monitorConfigFor = (pattern, rowCount, timezone) => ({
schedule: { type: 'crontab', value: pattern },
timezone,
checkinMargin: 60,
maxRuntime: Math.ceil((rowCount * UPDATE_TIMEOUT_MS) / 60000) + 10,
});
let scheduler = null;
/**
@@ -360,7 +372,7 @@ export const startDatasetScheduler = (rows) => {
scheduler = new Cron(pattern, { protect: true, unref: true }, () => withCronMonitor(
'dataset-update',
() => updateDatasets(scheduled),
{ schedule: { type: 'crontab', value: pattern }, timezone, checkinMargin: 60, maxRuntime: 60 },
monitorConfigFor(pattern, scheduled.length, timezone),
).catch((error) => logger.error({ err: error }, 'dataset update run failed')));
const next = scheduler.nextRun()?.toLocaleString('en-US', { hour12: false });
logger.info(`🗓️ Dataset auto update: ${scheduled.map((row) => row.id).join(', ')} at cron ${pattern} (next ${next})`);
+11 -1
View File
@@ -15,7 +15,7 @@ import lockfile from 'proper-lockfile';
import {
STATE_FILE, LOCK_FILE, updateDataset, bootstrapDataset, readState, isPresent,
isAutoUpdateEnabled, startDatasetScheduler, DEFAULT_UPDATE_CRON, rowsMissingACheck, watchDatasets,
updateDatasets, downloadToFile,
updateDatasets, downloadToFile, monitorConfigFor,
} from '../common/dataset-updater.js';
import logger from '../common/logger.js';
import { setUpstreamUserAgent } from '../common/fetch-with-timeout.js';
@@ -302,6 +302,16 @@ describe('startDatasetScheduler', () => {
});
});
describe('monitorConfigFor', () => {
it('allows every scheduled row its full timeout, plus a margin', () => {
const config = monitorConfigFor('30 4 * * *', 4, 'Asia/Singapore');
assert.equal(config.maxRuntime, 4 * 30 + 10);
assert.deepEqual(config.schedule, { type: 'crontab', value: '30 4 * * *' });
assert.equal(config.timezone, 'Asia/Singapore');
assert.equal(monitorConfigFor('30 4 * * *', 1).maxRuntime, 40);
});
});
describe('rowsMissingACheck', () => {
const at = (day, hour, minute) => new Date(2026, 9, day, hour, minute); // local time, like the cron
const pattern = '30 4 * * *';