-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
182 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"@vue-storefront/middleware": minor | ||
--- | ||
|
||
[ADDED] New GET /readyz endpoint for middleware for using with Kubernetes readiness probes. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
4 changes: 2 additions & 2 deletions
4
packages/middleware/__tests__/integration/createServer.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
import { HealthCheckError } from "@godaddy/terminus"; | ||
import { | ||
createMemoryFullnessReadinessCheck, | ||
createReadyzHandler, | ||
} from "../../src/terminus"; | ||
|
||
describe("terminus", () => { | ||
describe("terminus: createMemoryFUllnessReadinessCheck", () => { | ||
it("throws if memory usage over threshold", () => { | ||
expect.assertions(1); | ||
const memoryLimit = 3; | ||
const memoryLimitThreshold = 0.5; | ||
|
||
jest | ||
.spyOn(process, "memoryUsage") | ||
// @ts-expect-error other props not needed | ||
.mockImplementationOnce(() => ({ rss: 2 })); | ||
|
||
const memoryFullnessReadinessCheck = createMemoryFullnessReadinessCheck( | ||
memoryLimit, | ||
memoryLimitThreshold | ||
); | ||
|
||
expect(memoryFullnessReadinessCheck()).rejects.toThrow(); | ||
}); | ||
it("doesn't throw if memory usage is under threshold", () => { | ||
const memoryLimit = 3; | ||
const memoryLimitThreshold = 0.5; | ||
|
||
jest | ||
.spyOn(process, "memoryUsage") | ||
// @ts-expect-error other props not needed | ||
.mockImplementationOnce(() => ({ rss: 1 })); | ||
|
||
const memoryFullnessReadinessCheck = createMemoryFullnessReadinessCheck( | ||
memoryLimit, | ||
memoryLimitThreshold | ||
); | ||
|
||
expect(memoryFullnessReadinessCheck()).resolves.toBe(undefined); | ||
}); | ||
}); | ||
|
||
describe("createReadyzHandler", () => { | ||
it("throws on failing ready check", () => { | ||
const readinessChecks = [ | ||
async () => { | ||
throw new Error(); | ||
}, | ||
]; | ||
const readyzHandler = createReadyzHandler(readinessChecks); | ||
|
||
expect(readyzHandler()).rejects.toThrow(HealthCheckError); | ||
}); | ||
it("doesn't throw on succeeding ready check", () => { | ||
const readinessChecks = [async () => undefined]; | ||
const readyzHandler = createReadyzHandler(readinessChecks); | ||
|
||
expect(readyzHandler()).resolves.toBe(undefined); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
import { HealthCheckError, TerminusOptions } from "@godaddy/terminus"; | ||
import { setTimeout } from "node:timers/promises"; | ||
import { ReadinessCheck } from "./types"; | ||
|
||
/* | ||
* When running in Alokai Cloud, middleware pods are ran with a `ALOKAI_MEMORY_LIMIT` environment variable | ||
* This limit represents what is the Kubernetes resource limit on memory of the pod (a number in kilobytes), which if crossed, will kill the app | ||
* Here we're using that env var to monitor the current memory usage vs. the limit | ||
* The existence of this env var is a workaround for the lack of `process.availableMemory()` in Node 18 (available in Node 20, which e.g. Alokai bootstrapper doesn't use yet), which retrieves the memory limit from kernel cgroups | ||
*/ | ||
export const createMemoryFullnessReadinessCheck = | ||
(memoryLimit: number, memoryLimitThreshold: number): ReadinessCheck => | ||
async () => { | ||
const memoryUtilization = process.memoryUsage().rss / memoryLimit; | ||
if (memoryUtilization > memoryLimitThreshold) { | ||
throw new Error( | ||
`Process memory usage over threshold - current is ${memoryUtilization} where ALOKAI_MEMORY_LIMIT_THRESHOLD is ${memoryLimitThreshold}` | ||
); | ||
} | ||
}; | ||
|
||
// ALOKAI_MEMORY_LIMT won't be set in local dev envs etc. | ||
const defaultReadinessChecks = process.env.ALOKAI_MEMORY_LIMT | ||
? [ | ||
createMemoryFullnessReadinessCheck( | ||
Number.parseInt(process.env.ALOKAI_MEMORY_LIMIT, 10), | ||
Number.parseFloat(process.env.ALOKAI_MEMORY_LIMIT_THRESHOLD ?? "0.8") | ||
), | ||
] | ||
: []; | ||
|
||
export const createReadyzHandler = | ||
(readinessChecks: ReadinessCheck[]) => async () => { | ||
// call all provided readiness checks in parallel | ||
// warning: because Promise.allSettled (also happens for .all()) is used, | ||
// all readiness checks need to return a promise (that is, need to be async functions) | ||
const calledReadinessChecks = await Promise.allSettled( | ||
readinessChecks.map((fn) => fn()) | ||
); | ||
|
||
const readinessCheckFailureReasons = calledReadinessChecks.reduce< | ||
unknown[] | ||
>( | ||
(failureReasons, settledReadinessCheck) => | ||
settledReadinessCheck.status === "rejected" | ||
? [...failureReasons, settledReadinessCheck.reason] | ||
: failureReasons, | ||
[] | ||
); | ||
|
||
if (readinessCheckFailureReasons.length) { | ||
throw new HealthCheckError( | ||
"Readiness check failed", | ||
readinessCheckFailureReasons | ||
); | ||
} | ||
}; | ||
|
||
export const createTerminusOptions = ( | ||
readinessChecks: ReadinessCheck[] = defaultReadinessChecks | ||
): TerminusOptions => { | ||
return { | ||
useExit0: true, | ||
// In case some requests are still being handled when SIGTERM was received, naively wait in hopes that they will be resolved in that time, and only then shut down the process | ||
beforeShutdown: () => setTimeout(10 ** 4), | ||
healthChecks: { | ||
verbatim: true, | ||
"/readyz": createReadyzHandler(readinessChecks), | ||
}, | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
3 changes: 1 addition & 2 deletions
3
packages/sdk/src/__tests__/integration/__config__/jest.setup.global.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters