backend-plugin-api: separate out hook in addShutdownHook

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2023-02-06 15:54:19 +01:00
parent 2c68a05fd3
commit e716946103
14 changed files with 88 additions and 71 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/backend-plugin-api': minor
---
**BREAKING**: Split out the hook for both lifecycle services so that the first parameter of `addShutdownHook` is the hook function, and the second is the options.
+8
View File
@@ -0,0 +1,8 @@
---
'@backstage/backend-test-utils': patch
'@backstage/backend-app-api': patch
'@backstage/backend-common': patch
'@backstage/plugin-catalog-backend': patch
---
Updated usage of the lifecycle service.
+14 -15
View File
@@ -479,10 +479,7 @@ createBackendPlugin({
// do some other stuff.
}, 1000);
lifecycle.addShutdownHook({
fn: () => clearInterval(interval),
logger,
});
lifecycle.addShutdownHook(() => clearInterval(interval));
},
});
},
@@ -499,17 +496,19 @@ The following example shows how to override the default implementation of the li
```ts
class MyCustomLifecycleService implements RootLifecycleService {
constructor(private readonly logger: LoggerService) {
['SIGKILL', 'SIGTERM'].map(signal =>
process.on(signal, () => this.shutdown()),
);
}
constructor(private readonly logger: LoggerService) {}
#isCalled = false;
#shutdownTasks: Array<LifecycleServiceShutdownHook> = [];
#shutdownTasks: Array<{
hook: LifecycleServiceShutdownHook;
options?: LifecycleServiceShutdownOptions;
}> = [];
addShutdownHook(options: LifecycleServiceShutdownHook): void {
this.#shutdownTasks.push(options);
addShutdownHook(
hook: LifecycleServiceShutdownHook,
options?: LifecycleServiceShutdownOptions,
): void {
this.#shutdownTasks.push({ hook, options });
}
async shutdown(): Promise<void> {
@@ -520,10 +519,10 @@ class MyCustomLifecycleService implements RootLifecycleService {
this.logger.info(`Running ${this.#shutdownTasks.length} shutdown tasks...`);
await Promise.all(
this.#shutdownTasks.map(async hook => {
const { logger = this.logger } = hook;
this.#shutdownTasks.map(async ({ hook, options }) => {
const logger = options?.logger ?? this.logger;
try {
await hook.fn();
await hook();
logger.info(`Shutdown hook succeeded`);
} catch (error) {
logger.error(`Shutdown hook failed, ${error}`);
@@ -17,6 +17,7 @@ import {
createServiceFactory,
coreServices,
LifecycleServiceShutdownHook,
LifecycleServiceShutdownOptions,
} from '@backstage/backend-plugin-api';
/**
@@ -33,11 +34,12 @@ export const lifecycleFactory = createServiceFactory({
async factory({ rootLifecycle, logger, pluginMetadata }) {
const plugin = pluginMetadata.getId();
return {
addShutdownHook(options: LifecycleServiceShutdownHook): void {
rootLifecycle.addShutdownHook({
...options,
logger: options.logger?.child({ plugin }) ?? logger,
addShutdownHook(
hook: LifecycleServiceShutdownHook,
options?: LifecycleServiceShutdownOptions,
): void {
rootLifecycle.addShutdownHook(hook, {
logger: options?.logger?.child({ plugin }) ?? logger,
});
},
};
@@ -104,12 +104,7 @@ export const rootHttpRouterFactory = createServiceFactory({
{ logger },
);
lifecycle.addShutdownHook({
async fn() {
await server.stop();
},
logger,
});
lifecycle.addShutdownHook(() => server.stop());
await server.start();
@@ -21,11 +21,7 @@ describe('lifecycleService', () => {
it('should execute registered shutdown hook', async () => {
const service = new BackendLifecycleImpl(getVoidLogger());
const hook = jest.fn();
service.addShutdownHook({
fn: async () => {
hook();
},
});
service.addShutdownHook(() => hook());
// should not execute the hook more than once.
await service.shutdown();
await service.shutdown();
@@ -35,10 +31,16 @@ describe('lifecycleService', () => {
it('should not throw errors', async () => {
const service = new BackendLifecycleImpl(getVoidLogger());
service.addShutdownHook({
fn: async () => {
throw new Error('oh no');
},
service.addShutdownHook(() => {
throw new Error('oh no');
});
await expect(service.shutdown()).resolves.toBeUndefined();
});
it('should not throw async errors', async () => {
const service = new BackendLifecycleImpl(getVoidLogger());
service.addShutdownHook(async () => {
throw new Error('oh no');
});
await expect(service.shutdown()).resolves.toBeUndefined();
});
@@ -18,6 +18,7 @@ import {
createServiceFactory,
coreServices,
LifecycleServiceShutdownHook,
LifecycleServiceShutdownOptions,
RootLifecycleService,
LoggerService,
} from '@backstage/backend-plugin-api';
@@ -26,10 +27,16 @@ export class BackendLifecycleImpl implements RootLifecycleService {
constructor(private readonly logger: LoggerService) {}
#isCalled = false;
#shutdownTasks: Array<LifecycleServiceShutdownHook> = [];
#shutdownTasks: Array<{
hook: LifecycleServiceShutdownHook;
options?: LifecycleServiceShutdownOptions;
}> = [];
addShutdownHook(options: LifecycleServiceShutdownHook): void {
this.#shutdownTasks.push(options);
addShutdownHook(
hook: LifecycleServiceShutdownHook,
options?: LifecycleServiceShutdownOptions,
): void {
this.#shutdownTasks.push({ hook, options });
}
async shutdown(): Promise<void> {
@@ -40,10 +47,10 @@ export class BackendLifecycleImpl implements RootLifecycleService {
this.logger.info(`Running ${this.#shutdownTasks.length} shutdown tasks...`);
await Promise.all(
this.#shutdownTasks.map(async hook => {
const { logger = this.logger } = hook;
this.#shutdownTasks.map(async ({ hook, options }) => {
const logger = options?.logger ?? this.logger;
try {
await hook.fn();
await hook();
logger.info(`Shutdown hook succeeded`);
} catch (error) {
logger.error(`Shutdown hook failed, ${error}`);
@@ -81,12 +81,10 @@ export function createSqliteDatabaseClient(
});
// If the dev store is available we save the database state on shutdown
deps.lifecycle.addShutdownHook({
async fn() {
const connection = await database.client.acquireConnection();
const data = connection.serialize();
await devStore.save(dataKey, data);
},
deps.lifecycle.addShutdownHook(async () => {
const connection = await database.client.acquireConnection();
const data = connection.serialize();
await devStore.save(dataKey, data);
});
} else {
database = knexFactory(knexConfig);
+9 -4
View File
@@ -294,14 +294,19 @@ export interface IdentityService extends IdentityApi {}
// @public (undocumented)
export interface LifecycleService {
addShutdownHook(options: LifecycleServiceShutdownHook): void;
addShutdownHook(
hook: LifecycleServiceShutdownHook,
options?: LifecycleServiceShutdownOptions,
): void;
}
// @public (undocumented)
export type LifecycleServiceShutdownHook = {
fn: () => void | Promise<void>;
export type LifecycleServiceShutdownHook = () => void | Promise<void>;
// @public (undocumented)
export interface LifecycleServiceShutdownOptions {
logger?: LoggerService;
};
}
// @public
export interface LoggerService {
@@ -19,14 +19,17 @@ import { LoggerService } from './LoggerService';
/**
* @public
*/
export type LifecycleServiceShutdownHook = {
fn: () => void | Promise<void>;
export type LifecycleServiceShutdownHook = () => void | Promise<void>;
/**
* @public
*/
export interface LifecycleServiceShutdownOptions {
/**
* Optional {@link LoggerService} that will be used for logging instead of the default logger.
*/
logger?: LoggerService;
};
}
/**
* @public
@@ -35,5 +38,8 @@ export interface LifecycleService {
/**
* Register a function to be called when the backend is shutting down.
*/
addShutdownHook(options: LifecycleServiceShutdownHook): void;
addShutdownHook(
hook: LifecycleServiceShutdownHook,
options?: LifecycleServiceShutdownOptions,
): void;
}
@@ -28,6 +28,7 @@ export type { HttpRouterService } from './HttpRouterService';
export type {
LifecycleService,
LifecycleServiceShutdownHook,
LifecycleServiceShutdownOptions,
} from './LifecycleService';
export type { LoggerService, LogMeta } from './LoggerService';
export type { PermissionsService } from './PermissionsService';
@@ -40,10 +40,8 @@ beforeAll(async () => {
env.registerInit({
deps: { lifecycle: coreServices.lifecycle },
async init({ lifecycle }) {
lifecycle.addShutdownHook({
fn() {
globalTestBackendHasBeenStopped = true;
},
lifecycle.addShutdownHook(() => {
globalTestBackendHasBeenStopped = true;
});
},
});
@@ -145,7 +143,7 @@ describe('TestBackend', () => {
lifecycle: coreServices.lifecycle,
},
async init({ lifecycle }) {
lifecycle.addShutdownHook({ fn: shutdownSpy });
lifecycle.addShutdownHook(shutdownSpy);
},
});
},
@@ -129,12 +129,7 @@ export async function startTestBackend<
{ logger },
);
lifecycle.addShutdownHook({
async fn() {
await server.stop();
},
logger,
});
lifecycle.addShutdownHook(() => server.stop(), { logger });
await server.start();
@@ -97,11 +97,7 @@ export const catalogPlugin = createBackendPlugin({
const { processingEngine, router } = await builder.build();
await processingEngine.start();
lifecycle.addShutdownHook({
fn: async () => {
await processingEngine.stop();
},
});
lifecycle.addShutdownHook(() => processingEngine.stop());
httpRouter.use(router);
},
});