From 7376dcff2582a5f4dd4151b4a7559422d429720d Mon Sep 17 00:00:00 2001 From: juslesan Date: Fri, 24 Oct 2025 14:53:17 +0300 Subject: [PATCH 1/5] remove exit event handlers from trackerless-network --- packages/trackerless-network/src/NetworkStack.ts | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/packages/trackerless-network/src/NetworkStack.ts b/packages/trackerless-network/src/NetworkStack.ts index e9924005b2..cc16682cef 100644 --- a/packages/trackerless-network/src/NetworkStack.ts +++ b/packages/trackerless-network/src/NetworkStack.ts @@ -32,17 +32,6 @@ const stopInstances = async () => { const clonedInstances = [...instances] await Promise.all(clonedInstances.map((instance) => instance.stop())) } -const EXIT_EVENTS = [`exit`, `SIGINT`, `SIGUSR1`, `SIGUSR2`, `uncaughtException`, `unhandledRejection`, `SIGTERM`] -EXIT_EVENTS.forEach((event) => { - process.on(event, async (eventArg) => { - const isError = (event === 'uncaughtException') || (event === 'unhandledRejection') - if (isError) { - logger.error(`exit event: ${event}`, eventArg) - } - await stopInstances() - process.exit(isError ? 1 : 0) - }) -}) declare let window: any if (typeof window === 'object') { window.addEventListener('unload', async () => { From 161904af70594501e1a2a658a2eb6a78cb5429bc Mon Sep 17 00:00:00 2001 From: juslesan Date: Fri, 24 Oct 2025 14:57:47 +0300 Subject: [PATCH 2/5] Add exit handlers to streamr-node.ts --- packages/node/bin/streamr-node.ts | 26 +++++++++++++++++++++++++- packages/node/src/broker.ts | 10 ---------- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/packages/node/bin/streamr-node.ts b/packages/node/bin/streamr-node.ts index 2d536d6f9b..50dfcbdcb2 100644 --- a/packages/node/bin/streamr-node.ts +++ b/packages/node/bin/streamr-node.ts @@ -1,11 +1,13 @@ #!/usr/bin/env node import { program } from 'commander' import pkg from '../package.json' - +import { Logger } from '@streamr/utils' import { createBroker } from '../src/broker' import { readConfigAndMigrateIfNeeded } from '../src/config/migration' import { overrideConfigToEnvVarsIfGiven } from '../src/config/config' +const logger = new Logger(module) + program .version(pkg.version) .name('streamr-node') @@ -17,6 +19,28 @@ program const config = readConfigAndMigrateIfNeeded(configFile) overrideConfigToEnvVarsIfGiven(config) const broker = await createBroker(config) + + // Set up graceful shutdown handlers + const shutdown = async (exitCode: number) => { + await broker.stop() + process.exit(exitCode) + } + + const exitEvents = ['SIGINT', 'SIGTERM', 'SIGUSR1', 'SIGUSR2', 'exit'] + exitEvents.forEach((event) => { + process.on(event, () => shutdown(0)) + }) + + process.on('uncaughtException', (err) => { + logger.fatal('Encountered uncaughtException', { err }) + shutdown(1) + }) + + process.on('unhandledRejection', (err) => { + logger.fatal('Encountered unhandledRejection', { err }) + shutdown(1) + }) + if (!program.opts().test) { await broker.start() } else { diff --git a/packages/node/src/broker.ts b/packages/node/src/broker.ts index 9f236d9f9e..01ddf54e53 100644 --- a/packages/node/src/broker.ts +++ b/packages/node/src/broker.ts @@ -78,13 +78,3 @@ export const createBroker = async (configWithoutDefaults: Config): Promise { - logger.fatal('Encountered uncaughtException', { err }) - process.exit(1) -}) - -process.on('unhandledRejection', (err) => { - logger.fatal('Encountered unhandledRejection', { err }) - process.exit(1) -}) From 4172af9ef1d9745deb3e0a80c0164a24876d8cf2 Mon Sep 17 00:00:00 2001 From: juslesan Date: Mon, 10 Nov 2025 13:44:43 +0200 Subject: [PATCH 3/5] remove exit event --- packages/node/bin/streamr-node.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/node/bin/streamr-node.ts b/packages/node/bin/streamr-node.ts index 50dfcbdcb2..8a560cbc50 100644 --- a/packages/node/bin/streamr-node.ts +++ b/packages/node/bin/streamr-node.ts @@ -26,7 +26,7 @@ program process.exit(exitCode) } - const exitEvents = ['SIGINT', 'SIGTERM', 'SIGUSR1', 'SIGUSR2', 'exit'] + const exitEvents = ['SIGINT', 'SIGTERM', 'SIGUSR1', 'SIGUSR2'] exitEvents.forEach((event) => { process.on(event, () => shutdown(0)) }) From 559517ea8443399cbac49c4c786c72ea8e7b94dd Mon Sep 17 00:00:00 2001 From: juslesan Date: Wed, 19 Nov 2025 16:42:07 +0200 Subject: [PATCH 4/5] Add documentation about calling StreamrClient#destroy --- packages/sdk/README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/sdk/README.md b/packages/sdk/README.md index 4e13c84e0a..4314431c9d 100644 --- a/packages/sdk/README.md +++ b/packages/sdk/README.md @@ -118,6 +118,13 @@ streamr.resend(streamId, { last: 10 }, (msgs) => { }); ``` +### Clean up +After the StreamrClient is no longer used or the process is shutting down it is very important to call `destroy` on the StreamrClient. This ensures that the network node of the client will be shutdown gracefully. + +```js +await streamr.destroy() +``` + ___ **This Readme only scratches the surface of what's possible - be sure to [checkout our documentation](https://docs.streamr.network) for the full usage instructions.** From 704238304d58bab50c861679a608fa4637d50029 Mon Sep 17 00:00:00 2001 From: juslesan Date: Fri, 21 Nov 2025 11:28:11 +0200 Subject: [PATCH 5/5] Remove browser on unload listener clean up --- .../trackerless-network/src/NetworkStack.ts | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/packages/trackerless-network/src/NetworkStack.ts b/packages/trackerless-network/src/NetworkStack.ts index cc16682cef..84729f029e 100644 --- a/packages/trackerless-network/src/NetworkStack.ts +++ b/packages/trackerless-network/src/NetworkStack.ts @@ -8,7 +8,6 @@ import { toNodeId } from '@streamr/dht' import { Logger, MetricsContext, StreamID, StreamPartID, toStreamPartID, until } from '@streamr/utils' -import pull from 'lodash/pull' import { version as applicationVersion } from '../package.json' import { ContentDeliveryManager, ContentDeliveryManagerOptions, StreamPartDeliveryOptions } from './ContentDeliveryManager' import { ControlLayerNode } from './control-layer/ControlLayerNode' @@ -25,20 +24,6 @@ export interface NetworkOptions { const logger = new Logger(module) -const instances: NetworkStack[] = [] -const stopInstances = async () => { - // make a clone so that it is ok for each instance.stop() to remove itself from the list (at line 139) - // while the map function is iterating the list - const clonedInstances = [...instances] - await Promise.all(clonedInstances.map((instance) => instance.stop())) -} -declare let window: any -if (typeof window === 'object') { - window.addEventListener('unload', async () => { - await stopInstances() - }) -} - export class NetworkStack { private controlLayerNode?: ControlLayerNode @@ -61,7 +46,6 @@ export class NetworkStack { ...options.networkNode, metricsContext: this.metricsContext }) - instances.push(this) } async joinStreamPart( @@ -179,7 +163,6 @@ export class NetworkStack { async stop(): Promise { if (!this.stopped) { this.stopped = true - pull(instances, this) await this.contentDeliveryManager!.destroy() await this.controlLayerNode!.stop() this.contentDeliveryManager = undefined