12import { pickBy, mapKeys, uniq, compact, unionBy } from 'lodash-es' 16let cleanupWired = false 17const wireProcessCleanup = () => { 18 if (cleanupWired) return 21 process.on('beforeExit', cleanup) 22 process.on('SIGTERM', () => { cleanup(); process.exit(143) }) 23 process.on('SIGINT', () => { cleanup(); process.exit(130) }) 26export const withDb = async <T,>(wrappedFnc: (dbH: DbHandles) => Promise<T>, opts: {reuseConn?: boolean}): Promise<T> => { 27 const ctx = dbCtx.getStore() 30 return await wrappedFnc(ctx) 33 // Cached conn died (e.g., port-forward died after laptop sleep). Evict stale 34 // pools, bounded-wait for the watchdog to revive the tunnel, then fall through 35 // to the full rebuild below which retries wrappedFnc with fresh connections. 42 const {reuseConn} = opts 44 // dbSecrets: { db: 'games1db_qs', authDb: 'authprod_qs' } 47 // Build { dbKey: secretName } from dbSecrets or fall back to secretsMapping 48 const dbKeyToSecretH: Record<string, string> = dbSecrets 49 ? pickBy(dbSecrets, secName => secName !== disabledSecret) 50 // fallback for entrypoints not yet migrated to dbSecrets 52 pickBy(secretsMapping, (secName, qsName) => qsName.endsWith('db_qs') && secName !== disabledSecret), 53 (secName, qsName) => { 54 // defaultdb_qs → db, authdb_qs → authDb 55 if (qsName === 'defaultdb_qs') return 'db' 56 return qsName.replace(/db_qs$/, '') + 'Db' 60 // k8sPortFwdCtx is a process-global accumulator (genGlobal2Ctx); a skip-guard would 61 // pin the first job's svc list and starve later jobs needing different dbs — always union. 62 const existingProxyA = k8sPortFwdCtx.getStore()?.k8sProxyA || [] 63 const decomposed = await Promise.all( 64 uniq(Object.values(dbKeyToSecretH)).map(async secName => { 69 const k8sProxyA = unionBy([...compact(decomposed).filter(needsProxyQs), ...existingProxyA], 'svcName') 70 k8sPortFwdCtx.enterWith({...k8sPortFwdCtx.getStore(), k8sProxyA}) 72 const wrappedFnc2 = async (): Promise<T> => { 73 return await (async function chain(idx = 0, dbH: DbHandles = {}): Promise<T> { 75 const entries = Object.entries(dbKeyToSecretH) 76 if (idx >= entries.length) { 77 return dbCtx.run(dbH, async () => { 78 return await loadersCtx.run({}, async () => { 79 return await wrappedFnc(dbH) 84 const [dbKey, secretName] = entries[idx] 85 return await secMapDbConnWrap({qsName: secretName, reuseConn, pgAdType: 'Knex'}, async (knexConn) => { 86 if (knexConn) dbH[dbKey] = knexConn 87 const pgAdType = 'Kys' 88 return await secMapDbConnWrap({qsName: secretName, reuseConn, pgAdType}, async (kysConn) => { 89 if (kysConn) dbH[`${dbKey}Kys`] = kysConn 90 return await chain(idx + 1, dbH) 98 const maxStaleRetries = 2 99 for (let attempt = 0; ; attempt++) { 101 return await runWithPortFwd()