-
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
13 changed files
with
159 additions
and
13 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,18 @@ | ||
--- | ||
"@vue-storefront/middleware": major | ||
--- | ||
|
||
- [CHANGED] [BREAKING] Changed return type of `createServer()` from `Express` to `Server` (from built-in `node:http` package). Both of those types' interfaces have the `.listen()` method with the same shape. In some older templates for starting the middleware (`middleware.js` in your repo) you come across: | ||
|
||
```ts | ||
async function runMiddleware(app: Express) { | ||
``` | ||
If you're using that older template, please change the `Express` type to `Server`: | ||
```diff | ||
+ import { Server } from "node:http" | ||
+ async function runMiddleware(app: Server) { | ||
- async function runMiddleware(app: Express) { | ||
``` | ||
- [ADDED] New GET /readyz endpoint for middleware for using with Kubernetes readiness probes. Please see https://docs.alokai.com/middleware/guides/readiness-probes for more information |
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,29 @@ | ||
## Readiness probes | ||
|
||
Users of Alokai Cloud have their middleware deployed in Kubernetes. Readiness probes allow an application to temporarily mark itself as unable to serve requests in a Kubernetes cluster. | ||
|
||
As of version 5.0.0 of the `@vue-storefront/middleware` package, you can launch the middleware and send a GET request to the `http://localhost:4000/readyz` endpoint. The response will contain either a success message or a list of errors describing why the readiness probe failed. | ||
|
||
The same endpoint is queried automatically by Alokai Cloud every few seconds to check whether requests should be routed to a particular middleware replica. One such case is if a middleware instance is being killed (if it receives a `SIGTERM` signal), it should stop accepting traffic, but wait a few seconds before shutting down to handle any requests that are still being handledby that instance. | ||
|
||
You can read more about Kubernetes readiness probes in the [official documentation](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#define-readiness-probes). | ||
|
||
|
||
|
||
### Adding custom readiness probes | ||
|
||
To add custom readiness probes, pass them to the `readinessProbes` property when calling `createServer`. | ||
|
||
```ts | ||
const customReadinessProbe = async () => { | ||
const dependentServiceRunning = await axios.get('http://someservice:3000/healthz'); | ||
if(dependentServiceRunning.status !== 200) { | ||
throw new Error('Service that the middleware depends on is offline. The middleware is temporarily not ready to accept connections.') | ||
} | ||
} | ||
const app = await createServer(config, { readinessProbes: [customReadinessProbe]}); | ||
``` | ||
|
||
In order for custom readiness probes to be implemented correctly, they need to do two things: | ||
1. they must all be async or return a promise (the return value is not checked, it's expected to be void/undefined) | ||
2. they must all throw an exception when you want a readiness probe to fail |
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,23 @@ | ||
import { HealthCheckError } from "@godaddy/terminus"; | ||
import { createReadyzHandler } from "../../src/terminus"; | ||
|
||
describe("terminus", () => { | ||
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 |
---|---|---|
@@ -1,4 +1,4 @@ | ||
export * from "./types"; | ||
|
||
export * from "./createServer"; | ||
export * from "./apiClientFactory"; | ||
export * from "./terminus"; |
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,44 @@ | ||
import { HealthCheckError, TerminusOptions } from "@godaddy/terminus"; | ||
import { setTimeout } from "node:timers/promises"; | ||
import { ReadinessProbe } from "./types"; | ||
|
||
export const createReadyzHandler = | ||
(readinessChecks: ReadinessProbe[]) => 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: ReadinessProbe[] | ||
): 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