fix: graceful shutdown — write last-run file on SIGTERM, show interrupted state in status
This commit is contained in:
@@ -27,7 +27,12 @@ import {
|
||||
const isPreview = process.argv.includes('--preview');
|
||||
|
||||
async function main() {
|
||||
acquireLock('applier', resolve(__dir, 'data'));
|
||||
const lock = acquireLock('applier', resolve(__dir, 'data'));
|
||||
lock.onShutdown(() => {
|
||||
writeFileSync(resolve(__dir, 'data/applier_last_run.json'), JSON.stringify({
|
||||
finished_at: null, finished: false, note: 'interrupted'
|
||||
}, null, 2));
|
||||
});
|
||||
console.log('🚀 claw-apply: Job Applier starting\n');
|
||||
|
||||
const settings = loadConfig(resolve(__dir, 'config/settings.json'));
|
||||
|
||||
@@ -20,9 +20,30 @@ import { DEFAULT_FIRST_RUN_DAYS } from './lib/constants.mjs';
|
||||
import { generateKeywords } from './lib/keywords.mjs';
|
||||
|
||||
async function main() {
|
||||
acquireLock('searcher', resolve(__dir, 'data'));
|
||||
const lock = acquireLock('searcher', resolve(__dir, 'data'));
|
||||
console.log('🔍 claw-apply: Job Searcher starting\n');
|
||||
|
||||
let totalAdded = 0, totalSeen = 0;
|
||||
const platformsRun = [];
|
||||
const startedAt = Date.now();
|
||||
|
||||
const writeLastRun = (finished = false) => {
|
||||
writeFileSync(resolve(__dir, 'data/searcher_last_run.json'), JSON.stringify({
|
||||
started_at: startedAt,
|
||||
finished_at: finished ? Date.now() : null,
|
||||
finished,
|
||||
added: totalAdded,
|
||||
seen: totalSeen,
|
||||
skipped_dupes: totalSeen - totalAdded,
|
||||
platforms: platformsRun,
|
||||
}, null, 2));
|
||||
};
|
||||
|
||||
lock.onShutdown(() => {
|
||||
console.log(' Writing partial results to last-run file...');
|
||||
writeLastRun(false);
|
||||
});
|
||||
|
||||
// Load config
|
||||
const settings = loadConfig(resolve(__dir, 'config/settings.json'));
|
||||
const searchConfig = loadConfig(resolve(__dir, 'config/search_config.json'));
|
||||
@@ -51,10 +72,6 @@ async function main() {
|
||||
const lookbackDays = isFirstRun ? (searchConfig.first_run_days || DEFAULT_FIRST_RUN_DAYS) : null;
|
||||
if (isFirstRun) console.log(`📅 First run — looking back ${lookbackDays} days\n`);
|
||||
|
||||
let totalAdded = 0;
|
||||
let totalSeen = 0;
|
||||
const platformsRun = [];
|
||||
|
||||
// Group searches by platform
|
||||
const liSearches = searchConfig.searches.filter(s => s.platforms?.includes('linkedin'));
|
||||
const wfSearches = searchConfig.searches.filter(s => s.platforms?.includes('wellfound'));
|
||||
@@ -136,14 +153,7 @@ async function main() {
|
||||
console.log(`\n${summary.replace(/\*/g, '')}`);
|
||||
if (totalAdded > 0) await sendTelegram(settings, summary);
|
||||
|
||||
// Write last-run metadata for status.mjs
|
||||
writeFileSync(resolve(__dir, 'data/searcher_last_run.json'), JSON.stringify({
|
||||
finished_at: Date.now(),
|
||||
added: totalAdded,
|
||||
seen: totalSeen,
|
||||
skipped_dupes: totalSeen - totalAdded,
|
||||
platforms: platformsRun,
|
||||
}, null, 2));
|
||||
writeLastRun(true);
|
||||
|
||||
console.log('\n✅ Search complete');
|
||||
return { added: totalAdded, seen: totalSeen };
|
||||
|
||||
20
lib/lock.mjs
20
lib/lock.mjs
@@ -26,9 +26,21 @@ export function acquireLock(name, dataDir) {
|
||||
const release = () => {
|
||||
try { unlinkSync(lockFile); } catch {}
|
||||
};
|
||||
process.on('exit', release);
|
||||
process.on('SIGINT', () => { release(); process.exit(130); });
|
||||
process.on('SIGTERM', () => { release(); process.exit(143); });
|
||||
|
||||
return release;
|
||||
// Graceful shutdown — call registered cleanup before exiting
|
||||
const shutdownHandlers = [];
|
||||
const shutdown = (code) => async () => {
|
||||
console.log(`\n⚠️ ${name}: signal received, shutting down gracefully...`);
|
||||
for (const fn of shutdownHandlers) {
|
||||
try { await fn(); } catch {}
|
||||
}
|
||||
release();
|
||||
process.exit(code);
|
||||
};
|
||||
|
||||
process.on('exit', release);
|
||||
process.on('SIGINT', shutdown(130));
|
||||
process.on('SIGTERM', shutdown(143));
|
||||
|
||||
return { release, onShutdown: (fn) => shutdownHandlers.push(fn) };
|
||||
}
|
||||
|
||||
@@ -108,7 +108,9 @@ function formatReport(s) {
|
||||
const sr = s.searcher;
|
||||
const searcherLine = sr.running
|
||||
? `🔄 Running now — ${q.total} jobs found so far`
|
||||
: `⏸️ Last ran ${timeAgo(sr.last_run?.finished_at)}`;
|
||||
: sr.last_run?.finished === false
|
||||
? `⚠️ Last run interrupted ${timeAgo(sr.last_run?.started_at)} (partial results saved)`
|
||||
: `⏸️ Last ran ${timeAgo(sr.last_run?.finished_at)}`;
|
||||
const lastRunDetail = sr.last_run && !sr.running
|
||||
? ` Found ${sr.last_run.added} new jobs (${sr.last_run.seen} seen, ${sr.last_run.skipped_dupes || 0} dupes)`
|
||||
: null;
|
||||
|
||||
Reference in New Issue
Block a user