bde148f286
* wip * some extra fixes * stuff kinda works? * need to figure out mocks * need to figure out mocks * fix db listener * gqlgen fix * minor gqlgen watch adjustment * lint fixes * delete old codegen file * converting migrations to ESM * getModuleDIrectory * vitest sort of works * added back ts-vitest * resolve gql double load * fixing test timeout configs * TSC lint fix * fix automate tests * moar debugging * debugging * more debugging * codegen update * server works * yargs migrated * chore(server): getting rid of global mocks for Server ESM (#5046) * got rid of email mock * got rid of comment mocks * got rid of multi region mocks * got rid of stripe mock * admin override mock updated * removed final mock * fixing import.meta.resolve calls * another import.meta.resolve fix * added requested test * nyc ESM fix * removed unneeded deps + linting * yarn lock forgot to commit * tryna fix flakyness * email capture util fix * sendEmail fix * fix TSX check * sender transporter fix + CR comments * merge main fix * test fixx * circleci fix * gqlgen bigint fix * error formatter fix * more error formatting improvements * esmloader added to Dockerfile * more dockerfile fixes * bg jobs fix
137 lines
4.3 KiB
TypeScript
137 lines
4.3 KiB
TypeScript
import { ensureErrorOrWrapAsCause } from '@/modules/shared/errors/ensureError'
|
|
import { join, merge } from 'lodash-es'
|
|
import VError from 'verror'
|
|
import {
|
|
FreeConnectionsCalculators,
|
|
MultiDBCheck,
|
|
ReadinessHandler,
|
|
RedisCheck
|
|
} from '@/healthchecks/types'
|
|
import { LivenessError, ReadinessError } from '@/healthchecks/errors'
|
|
import { calculatePercentageFreeConnections } from '@/healthchecks/connectionPool'
|
|
import { getGenericRedis } from '@/modules/shared/redis/redis'
|
|
|
|
export const handleLivenessFactory =
|
|
(deps: {
|
|
isRedisAlive: RedisCheck
|
|
areAllPostgresAlive: MultiDBCheck
|
|
getFreeConnectionsCalculators: () => FreeConnectionsCalculators
|
|
}) =>
|
|
async () => {
|
|
const allPostgresResults = await deps.areAllPostgresAlive()
|
|
const deadPostgresKeys = Object.entries(allPostgresResults)
|
|
.filter((result) => !result[1].isAlive)
|
|
.map((result) => result[0])
|
|
|
|
if (deadPostgresKeys.length) {
|
|
throw new LivenessError(
|
|
`Readiness health check failed. Postgres for ${join(
|
|
deadPostgresKeys,
|
|
', '
|
|
)} is not available.`,
|
|
{
|
|
cause: new VError.MultiError(
|
|
Object.entries(allPostgresResults).map((kv) =>
|
|
ensureErrorOrWrapAsCause(
|
|
//HACK: kv[1] is not typed correctly as the filter does not narrow the type
|
|
(kv[1] as { isAlive: false; err: unknown }).err,
|
|
'Unknown Postgres error.'
|
|
)
|
|
)
|
|
)
|
|
}
|
|
)
|
|
}
|
|
|
|
const redisClient = getGenericRedis()
|
|
const redisCheck = await deps.isRedisAlive({ client: redisClient })
|
|
if (!redisCheck.isAlive) {
|
|
throw new LivenessError('Liveness health check failed. Redis is not available.', {
|
|
cause: ensureErrorOrWrapAsCause(redisCheck.err, 'Unknown Redis error.')
|
|
})
|
|
}
|
|
|
|
const percentageFreeConnections = calculatePercentageFreeConnections({
|
|
...deps
|
|
})
|
|
const failingfreeConnectionsAboveThresholdKeys: string[] = []
|
|
for (const [region, percentageFree] of Object.entries(percentageFreeConnections)) {
|
|
//unready if less than 10%
|
|
if (percentageFree < 10) {
|
|
failingfreeConnectionsAboveThresholdKeys.push(region)
|
|
}
|
|
}
|
|
if (failingfreeConnectionsAboveThresholdKeys.length) {
|
|
throw new LivenessError(
|
|
`Liveness health check failed. Insufficient free database connections for a sustained duration for regions ${join(
|
|
failingfreeConnectionsAboveThresholdKeys,
|
|
', '
|
|
)}.`
|
|
)
|
|
}
|
|
|
|
return {
|
|
details: {
|
|
postgres: merge(
|
|
allPostgresResults,
|
|
Object.fromEntries(
|
|
Object.entries(percentageFreeConnections).map(([k, v]) => [
|
|
k,
|
|
{ percentageFreeConnections: v.toFixed(0) }
|
|
])
|
|
)
|
|
),
|
|
redis: true
|
|
}
|
|
}
|
|
}
|
|
|
|
export const handleReadinessFactory = (deps: {
|
|
isRedisAlive: RedisCheck
|
|
getFreeConnectionsCalculators: () => FreeConnectionsCalculators
|
|
}): ReadinessHandler => {
|
|
return async () => {
|
|
const redisClient = getGenericRedis()
|
|
const redisCheck = await deps.isRedisAlive({ client: redisClient })
|
|
if (!redisCheck.isAlive) {
|
|
throw new ReadinessError(
|
|
'Readiness health check failed. Redis is not available.',
|
|
{
|
|
cause: ensureErrorOrWrapAsCause(redisCheck.err, 'Unknown Redis error.')
|
|
}
|
|
)
|
|
}
|
|
|
|
const percentageFreeConnections = calculatePercentageFreeConnections({
|
|
...deps
|
|
})
|
|
const failingfreeConnectionsAboveThresholdKeys: string[] = []
|
|
for (const [region, percentageFree] of Object.entries(percentageFreeConnections)) {
|
|
//unready if less than 10%
|
|
if (percentageFree < 10) {
|
|
failingfreeConnectionsAboveThresholdKeys.push(region)
|
|
}
|
|
}
|
|
if (failingfreeConnectionsAboveThresholdKeys.length) {
|
|
throw new LivenessError(
|
|
`Liveness health check failed. Insufficient free database connections for a sustained duration for regions ${join(
|
|
failingfreeConnectionsAboveThresholdKeys,
|
|
', '
|
|
)}.`
|
|
)
|
|
}
|
|
|
|
return {
|
|
details: {
|
|
postgres: Object.fromEntries(
|
|
Object.entries(percentageFreeConnections).map(([k, v]) => [
|
|
k,
|
|
{ percentageFreeConnections: v.toFixed(0) }
|
|
])
|
|
),
|
|
redis: true
|
|
}
|
|
}
|
|
}
|
|
}
|