From 0bc2355226fd358040b5a058c30021555c725aa2 Mon Sep 17 00:00:00 2001 From: Austin McKinley Date: Tue, 23 Sep 2025 15:17:49 -0700 Subject: [PATCH] Start migrating blink. Also add better shutdown handling for ogcard --- .../workflows/build-and-push-link-aws.yaml | 7 +- bskylink/src/bin.ts | 79 +++++++++++++------ bskylink/src/db/index.ts | 10 +++ bskyogcard/src/index.ts | 30 ++++++- 4 files changed, 98 insertions(+), 28 deletions(-) diff --git a/.github/workflows/build-and-push-link-aws.yaml b/.github/workflows/build-and-push-link-aws.yaml index 1c17d6e190..7fbbc23c82 100644 --- a/.github/workflows/build-and-push-link-aws.yaml +++ b/.github/workflows/build-and-push-link-aws.yaml @@ -1,6 +1,11 @@ name: build-and-push-link-aws on: workflow_dispatch: + pull_request: + paths: + - 'bskylink/**' + - 'Dockerfile.bskylink' + - '.github/workflows/build-and-push-link-aws.yaml' env: REGISTRY: ${{ secrets.AWS_ECR_REGISTRY_USEAST2_PACKAGES_REGISTRY }} @@ -45,7 +50,7 @@ jobs: uses: docker/build-push-action@v4 with: context: . - push: ${{ github.event_name != 'pull_request' }} + push: true file: ./Dockerfile.bskylink tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} diff --git a/bskylink/src/bin.ts b/bskylink/src/bin.ts index 3e0746a989..3dea1a7527 100644 --- a/bskylink/src/bin.ts +++ b/bskylink/src/bin.ts @@ -1,29 +1,62 @@ import {Database, envToCfg, httpLogger, LinkService, readEnv} from './index.js' + async function main() { - const env = readEnv() - const cfg = envToCfg(env) - if (cfg.db.migrationUrl) { - const migrateDb = Database.postgres({ - url: cfg.db.migrationUrl, - schema: cfg.db.schema, + try { + httpLogger.info('Starting blink service') + + const env = readEnv() + const cfg = envToCfg(env) + + httpLogger.info( + { + port: cfg.service.port, + safelinkEnabled: cfg.service.safelinkEnabled, + hasDbUrl: !!cfg.db.url, + hasDbMigrationUrl: !!cfg.db.migrationUrl, + }, + 'Configuration loaded', + ) + + if (cfg.db.migrationUrl) { + httpLogger.info('Running database migrations') + const migrateDb = Database.postgres({ + url: cfg.db.migrationUrl, + schema: cfg.db.schema, + }) + await migrateDb.migrateToLatestOrThrow() + await migrateDb.close() + httpLogger.info('Database migrations completed') + } + + httpLogger.info('Creating LinkService') + const link = await LinkService.create(cfg) + + if (link.ctx.cfg.service.safelinkEnabled) { + httpLogger.info('Starting Safelink client') + link.ctx.safelinkClient.runFetchEvents() + } + + await link.start() + httpLogger.info('Link service is running') + + process.on('SIGTERM', async () => { + httpLogger.info('Link service is stopping') + await link.destroy() + httpLogger.info('Link service is stopped') }) - await migrateDb.migrateToLatestOrThrow() - await migrateDb.close() + } catch (error) { + httpLogger.error( + { + error: String(error), + stack: error instanceof Error ? error.stack : undefined, + }, + 'Failed to start blink service', + ) + process.exit(1) } - - const link = await LinkService.create(cfg) - - if (link.ctx.cfg.service.safelinkEnabled) { - link.ctx.safelinkClient.runFetchEvents() - } - - await link.start() - httpLogger.info('link service is running') - process.on('SIGTERM', async () => { - httpLogger.info('link service is stopping') - await link.destroy() - httpLogger.info('link service is stopped') - }) } -main() +main().catch(error => { + console.error('Unhandled startup error:', error) + process.exit(1) +}) diff --git a/bskylink/src/db/index.ts b/bskylink/src/db/index.ts index 7fe6aa536f..d335f80146 100644 --- a/bskylink/src/db/index.ts +++ b/bskylink/src/db/index.ts @@ -34,6 +34,16 @@ export class Database { static postgres(opts: PgOptions): Database { const {schema, url, txLockNonce} = opts + log.info( + { + schema, + poolSize: opts.poolSize, + poolMaxUses: opts.poolMaxUses, + poolIdleTimeoutMs: opts.poolIdleTimeoutMs, + }, + 'Creating database connection', + ) + const pool = opts.pool ?? new Pg.Pool({ diff --git a/bskyogcard/src/index.ts b/bskyogcard/src/index.ts index 110c3f50b7..53b5dbd6e1 100644 --- a/bskyogcard/src/index.ts +++ b/bskyogcard/src/index.ts @@ -62,7 +62,10 @@ export class CardService { // Start main application server this.server = this.app.listen(this.ctx.cfg.service.port) this.server.keepAliveTimeout = 90000 - this.terminator = createHttpTerminator({server: this.server}) + this.terminator = createHttpTerminator({ + server: this.server, + gracefulTerminationTimeout: 15000, // 15s timeout for in-flight requests + }) await events.once(this.server, 'listening') // Start separate metrics server @@ -73,13 +76,32 @@ export class CardService { }) this.metricsServer = metricsApp.listen(this.ctx.cfg.service.metricsPort) - this.metricsTerminator = createHttpTerminator({server: this.metricsServer}) + this.metricsTerminator = createHttpTerminator({ + server: this.metricsServer, + gracefulTerminationTimeout: 2000, // 2s timeout for metrics server + }) await events.once(this.metricsServer, 'listening') } async destroy() { + const startTime = Date.now() + this.ctx.abortController.abort() - await this.terminator?.terminate() - await this.metricsTerminator?.terminate() + + const shutdownPromises = [] + + if (this.terminator) { + shutdownPromises.push(this.terminator.terminate()) + } + + if (this.metricsTerminator) { + shutdownPromises.push(this.metricsTerminator.terminate()) + } + + await Promise.all(shutdownPromises) + + const elapsed = Date.now() - startTime + const {httpLogger} = await import('./logger.js') + httpLogger.info(`Graceful shutdown completed in ${elapsed}ms`) } }