Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs(examples/supply-chain-app): fix resource cleanup of shutdown logic #3364

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,15 @@ export interface ISupplyChainAppDummyInfrastructureOptions {
keychain?: IPluginKeychain;
}

function ledgerStopFailErrorMsg(ledgerName: Readonly<string>): string {
return (
`Failed to stop ${ledgerName} ledger. This is most likely safe to ignore if the ` +
`error states that the container was not running to begin with. This usually means` +
`that the process exited before the application boot has finished and it did not` +
`have enough time to start launching the ${ledgerName} ledger yet.`
);
}

/**
* Contains code that is meant to simulate parts of a production grade deployment
* that would otherwise not be part of the application itself.
Expand Down Expand Up @@ -131,12 +140,21 @@ export class SupplyChainAppDummyInfrastructure {
public async stop(): Promise<void> {
try {
this.log.info(`Stopping...`);
await Promise.all([
this.besu.stop().then(() => this.besu.destroy()),
this.quorum.stop().then(() => this.quorum.destroy()),
this.fabric.stop().then(() => this.fabric.destroy()),
await Promise.allSettled([
this.besu
.stop()
.then(() => this.besu.destroy())
.catch((ex) => this.log.warn(ledgerStopFailErrorMsg("Besu"), ex)),
this.quorum
.stop()
.then(() => this.quorum.destroy())
.catch((ex) => this.log.warn(ledgerStopFailErrorMsg("Quorum"), ex)),
this.fabric
.stop()
.then(() => this.fabric.destroy())
.catch((ex) => this.log.warn(ledgerStopFailErrorMsg("Fabric"), ex)),
]);
this.log.info(`Stopped OK`);
this.log.info(`Ledgers of dummy infrastructure Stopped OK`);
} catch (ex) {
this.log.error(`Stopping crashed: `, ex);
throw ex;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export async function launchApp(
await supplyChainApp.start();
} catch (ex) {
console.error(`SupplyChainApp crashed. Existing...`, ex);
await supplyChainApp?.stop();
await supplyChainApp.stop();
process.exit(-1);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,15 +191,28 @@ export class SupplyChainApp {
this.log.debug(`Starting SupplyChainApp...`);

if (!this.options.disableSignalHandlers) {
exitHook((callback: IAsyncExitHookDoneCallback) => {
console.log(`Executing Registered signal handler to stop container.`);
this.stop().then(callback);
exitHook((onHookDone: IAsyncExitHookDoneCallback) => {
this.log.info("Starting async-exit-hook for supply-chain-app ...");
this.stop()
.catch((ex: unknown) => {
this.log.warn("Failed async-exit-hook for supply-chain-app", ex);
throw ex;
})
.finally(() => {
this.log.info("Concluded async-exit-hook for supply-chain-app ...");
onHookDone();
});
this.log.info("Started async-exit-hook for supply-chain-app OK");
});
this.log.debug(`Registered signal handlers for graceful auto-shutdown`);
this.log.info("Registered async-exit-hook for supply-chain-app shutdown");
}

this.onShutdown(async () => {
this.log.info("SupplyChainApp onShutdown() - stopping ledgers...");
await this.ledgers.stop();
this.log.info("SupplyChainApp onShutdown() - stopped ledgers OK");
});
await this.ledgers.start();
this.onShutdown(() => this.ledgers.stop());

const contractsInfo = await this.ledgers.deployContracts();

Expand Down Expand Up @@ -441,8 +454,12 @@ export class SupplyChainApp {
}

public async stop(): Promise<void> {
let i = 0;
for (const hook of this.shutdownHooks) {
i++;
this.log.info("Executing exit hook #%d...", i);
await hook(); // FIXME add timeout here so that shutdown does not hang
this.log.info("Executed exit hook #%d OK", i);
}
}

Expand Down Expand Up @@ -590,15 +607,25 @@ export class SupplyChainApp {
properties.authorizationConfigJson =
await this.getOrCreateAuthorizationConfig();
properties.crpcPort = 0;
// We must disable the API server's own shutdown hooks because if we didn't
// it would clash with the supply chain app's own shutdown hooks and the
// async functions wouldn't be waited for their conclusion leaving the containers
// running after the supply chain app NodeJS process has exited.
properties.enableShutdownHook = false;

const apiServer = new ApiServer({
config: properties,
httpServerApi,
httpServerCockpit,
pluginRegistry,
enableShutdownHook: false,
});

this.onShutdown(() => apiServer.shutdown());
this.onShutdown(async () => {
this.log.info("SupplyChainApp onShutdown() - stopping API server");
await apiServer.shutdown();
this.log.info("SupplyChainApp onShutdown() - stopped API server OK");
});

await apiServer.start();

Expand Down
Loading