Refactor code structure for improved readability and maintainability
Build Images and Deploy / Update-PROD-Stack (push) Successful in 1m19s

This commit is contained in:
2026-03-18 21:13:28 -04:00
parent 7bd99e8fcb
commit 8aca2ae14e
4 changed files with 24 additions and 13 deletions
-1
View File
@@ -345,7 +345,6 @@ npm run start # Start production web server
npm run worker:dev # Worker with hot reload (tsx watch) npm run worker:dev # Worker with hot reload (tsx watch)
npm run worker:prod # Worker for production npm run worker:prod # Worker for production
npm run db:push # Apply schema changes without migrations npm run db:push # Apply schema changes without migrations
npm run db:migrate # Create and apply a migration
npm run db:seed # Seed the admin user npm run db:seed # Seed the admin user
npm run db:studio # Open Prisma Studio (GUI) npm run db:studio # Open Prisma Studio (GUI)
``` ```
-1
View File
@@ -9,7 +9,6 @@
"worker:dev": "tsx watch src/worker/index.ts", "worker:dev": "tsx watch src/worker/index.ts",
"worker:prod": "tsx src/worker/index.ts", "worker:prod": "tsx src/worker/index.ts",
"db:push": "prisma db push", "db:push": "prisma db push",
"db:migrate": "prisma migrate dev",
"db:seed": "tsx prisma/seed.ts", "db:seed": "tsx prisma/seed.ts",
"db:studio": "prisma studio", "db:studio": "prisma studio",
"postinstall": "prisma generate" "postinstall": "prisma generate"
+23 -10
View File
@@ -33,8 +33,9 @@ function redisOpts() {
} }
} }
const connection = redisOpts() // Each Queue/Worker gets its own fresh connection options object.
// BullMQ Workers use blocking ioredis connections internally — sharing one
// options reference across instances can cause silent failures in some versions.
const prisma = new PrismaClient({ const prisma = new PrismaClient({
log: ['error', 'warn'], log: ['error', 'warn'],
}) })
@@ -49,9 +50,9 @@ function activeUntilFromNow(): Date {
// ── Queues (worker side) ────────────────────────────────────────────────────── // ── Queues (worker side) ──────────────────────────────────────────────────────
const priceUpdateQueue = new Queue('hashex-price-updates', { connection }) const priceUpdateQueue = new Queue('hashex-price-updates', { connection: redisOpts() })
const maintenanceQueue = new Queue('hashex-maintenance', { connection }) const maintenanceQueue = new Queue('hashex-maintenance', { connection: redisOpts() })
const schedulerQueue = new Queue('hashex-scheduler', { connection }) const schedulerQueue = new Queue('hashex-scheduler', { connection: redisOpts() })
// ── Workers ─────────────────────────────────────────────────────────────────── // ── Workers ───────────────────────────────────────────────────────────────────
@@ -157,7 +158,7 @@ const priceWorker = new Worker(
} }
}, },
{ {
connection, connection: redisOpts(),
concurrency: 1, // one Mastodon call at a time concurrency: 1, // one Mastodon call at a time
}, },
) )
@@ -185,7 +186,7 @@ const maintenanceWorker = new Worker(
console.log(`[maintenance] awarded research points to ${users.length} users`) console.log(`[maintenance] awarded research points to ${users.length} users`)
}, },
{ connection }, { connection: redisOpts() },
) )
/** /**
@@ -230,17 +231,29 @@ const schedulerWorker = new Worker(
console.log(`[scheduler] queued ${toQueue.length} price-update jobs (${hashtags.length - toQueue.length} already waiting)`) console.log(`[scheduler] queued ${toQueue.length} price-update jobs (${hashtags.length - toQueue.length} already waiting)`)
}, },
{ connection }, { connection: redisOpts() },
) )
// ── Error handlers ──────────────────────────────────────────────────────────── // ── Error handlers ────────────────────────────────────────────────────────────
for (const worker of [priceWorker, maintenanceWorker, schedulerWorker]) { // Worker-level connection errors (separate from per-job failures)
for (const [name, worker] of [['price', priceWorker], ['maintenance', maintenanceWorker], ['scheduler', schedulerWorker]] as const) {
worker.on('error', (err) => {
console.error(`[${name}-worker] connection error:`, err.message)
})
worker.on('failed', (job, err) => { worker.on('failed', (job, err) => {
console.error(`[worker] job ${job?.id} failed:`, err.message) console.error(`[${name}-worker] job ${job?.id} failed:`, err.message)
}) })
} }
// Diagnostic: log when price jobs complete or stall
priceWorker.on('completed', (job) => {
console.log(`[price-worker] completed job ${job.id} (${job.data?.tag})`)
})
priceWorker.on('stalled', (jobId) => {
console.warn(`[price-worker] stalled job ${jobId} — lock expired, will retry`)
})
// ── Repeatable jobs ─────────────────────────────────────────────────────────── // ── Repeatable jobs ───────────────────────────────────────────────────────────
async function setupRepeatableJobs() { async function setupRepeatableJobs() {
+1 -1
View File
File diff suppressed because one or more lines are too long