Add server readiness check & init retries

Wait for the Next.js server and DB to be healthy before initializing services in docker/unified/app-start.sh. Adds a health probe with configurable timeout and retries, backoff retries for the /api/init call, improved logging, and error handling when the server process exits.

In src/lib/services/scheduler.service.ts, make re-encryption of notification backends non-fatal by catching and logging errors, and make creation of default scheduled jobs robust by creating each job independently with per-job error handling and logging. Summary counts are logged for created/failed defaults so failures don't block the scheduler from starting.
This commit is contained in:
kikootwo
2026-02-13 14:03:21 -05:00
parent c97df7798a
commit 3f8180a246
2 changed files with 103 additions and 17 deletions
+36 -11
View File
@@ -51,12 +51,18 @@ export class SchedulerService {
logger.info('Initializing scheduler service...');
// Re-encrypt any notification backends with plaintext sensitive fields
await getNotificationService().reEncryptUnprotectedBackends();
try {
await getNotificationService().reEncryptUnprotectedBackends();
} catch (error) {
logger.error('Failed to re-encrypt notification backends (non-fatal)', {
error: error instanceof Error ? error.message : String(error),
});
}
// Create default jobs if they don't exist
await this.ensureDefaultJobs();
// Load and schedule all enabled jobs
// Load and schedule all enabled jobs (works with whatever jobs exist in DB)
await this.scheduleAllJobs();
// Check and trigger overdue jobs
@@ -66,7 +72,8 @@ export class SchedulerService {
}
/**
* Ensure default jobs exist in database
* Ensure default jobs exist in database.
* Each job is created independently so a single failure doesn't block the rest.
*/
private async ensureDefaultJobs(): Promise<void> {
const defaults = [
@@ -128,18 +135,36 @@ export class SchedulerService {
},
];
for (const defaultJob of defaults) {
const existing = await prisma.scheduledJob.findFirst({
where: { type: defaultJob.type },
});
let created = 0;
let failed = 0;
if (!existing) {
await prisma.scheduledJob.create({
data: defaultJob,
for (const defaultJob of defaults) {
try {
const existing = await prisma.scheduledJob.findFirst({
where: { type: defaultJob.type },
});
if (!existing) {
await prisma.scheduledJob.create({
data: defaultJob,
});
created++;
logger.info(`Created default job: ${defaultJob.name} (enabled: ${defaultJob.enabled})`);
}
} catch (error) {
failed++;
logger.error(`Failed to create default job: ${defaultJob.name}`, {
type: defaultJob.type,
error: error instanceof Error ? error.message : String(error),
});
logger.info(`Created default job: ${defaultJob.name} (disabled by default)`);
}
}
if (failed > 0) {
logger.warn(`Default jobs: ${created} created, ${failed} failed — failed jobs will be retried on next restart`);
} else if (created > 0) {
logger.info(`Default jobs: ${created} created`);
}
}
/**