diff --git a/package.json b/package.json index bb9c66b..a482e4c 100644 --- a/package.json +++ b/package.json @@ -74,12 +74,14 @@ "@types/chai": "^4.2.22", "@types/mocha": "^9.0.0", "@types/sinon": "^10.0.6", + "@types/wtfnode": "^0.7.0", "@typescript-eslint/eslint-plugin": "^4.26.0", "@typescript-eslint/parser": "^4.26.0", "chai": "^4.3.4", "eslint": "^7.27.0", "mocha": "^9.1.3", - "sinon": "^12.0.1" + "sinon": "^12.0.1", + "wtfnode": "^0.9.1" }, "extollo": { "discover": true, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8cce976..b33927c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -21,6 +21,7 @@ specifiers: '@types/sinon': ^10.0.6 '@types/ssh2': ^0.5.46 '@types/uuid': ^8.3.0 + '@types/wtfnode': ^0.7.0 '@typescript-eslint/eslint-plugin': ^4.26.0 '@typescript-eslint/parser': ^4.26.0 bcrypt: ^5.0.1 @@ -50,6 +51,7 @@ specifiers: typedoc-plugin-sourcefile-url: ^1.0.6 typescript: ^4.2.3 uuid: ^8.3.2 + wtfnode: ^0.9.1 zod: ^3.11.6 dependencies: @@ -99,12 +101,14 @@ devDependencies: '@types/chai': 4.2.22 '@types/mocha': 9.0.0 '@types/sinon': 10.0.6 + '@types/wtfnode': 0.7.0 '@typescript-eslint/eslint-plugin': 4.26.0_942c48837be95e76bb4156c78358cdbe '@typescript-eslint/parser': 4.26.0_eslint@7.27.0+typescript@4.2.3 chai: 4.3.4 eslint: 7.27.0 mocha: 9.1.3 sinon: 12.0.1 + wtfnode: 0.9.1 packages: @@ -447,6 +451,10 @@ packages: resolution: {integrity: sha512-0LbEEx1zxrYB3pgpd1M5lEhLcXjKJnYghvhTRgaBeUivLHMDM1TzF3IJ6hXU2+8uA4Xz+5BA63mtZo5DjVT8iA==} dev: false + /@types/wtfnode/0.7.0: + resolution: {integrity: sha512-kdBHgE9+M1Os7UqWZtiLhKye5reFl8cPBYyCsP2fatwZRz7F7GdIxIHZ20Kkc0hYBfbXE+lzPOTUU1I0qgjtHA==} + dev: true + /@typescript-eslint/eslint-plugin/4.26.0_942c48837be95e76bb4156c78358cdbe: resolution: {integrity: sha512-yA7IWp+5Qqf+TLbd8b35ySFOFzUfL7i+4If50EqvjT6w35X8Lv0eBHb6rATeWmucks37w+zV+tWnOXI9JlG6Eg==} engines: {node: ^10.12.0 || >=12.0.0} @@ -3112,6 +3120,11 @@ packages: /wrappy/1.0.2: resolution: {integrity: sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=} + /wtfnode/0.9.1: + resolution: {integrity: sha512-Ip6C2KeQPl/F3aP1EfOnPoQk14Udd9lffpoqWDNH3Xt78svxPbv53ngtmtfI0q2Te3oTq79XKTnRNXVIn/GsPA==} + hasBin: true + dev: true + /xtend/4.0.2: resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} engines: {node: '>=0.4'} diff --git a/src/lifecycle/Application.ts b/src/lifecycle/Application.ts index c3e1580..02437ac 100644 --- a/src/lifecycle/Application.ts +++ b/src/lifecycle/Application.ts @@ -8,13 +8,14 @@ import { StandardLogger, universalPath, UniversalPath, + FileLogger, + ifDebugging, } from '../util' import {Logging} from '../service/Logging' import {RunLevelErrorHandler} from './RunLevelErrorHandler' import {Unit, UnitStatus} from './Unit' import * as dotenv from 'dotenv' import {CacheFactory} from '../support/cache/CacheFactory' -import {FileLogger} from '../util/logging/FileLogger' /** * Helper function that resolves and infers environment variable values. @@ -260,6 +261,13 @@ export class Application extends Container { async run(): Promise { try { await this.up() + + ifDebugging('extollo.wontstop', () => { + setTimeout(() => { + import('wtfnode').then(wtf => wtf.dump()) + }, 1000) + }) + await this.down() } catch (e: unknown) { if ( e instanceof Error ) { diff --git a/src/support/bus/RedisBus.ts b/src/support/bus/RedisBus.ts index ae961e3..6a667cd 100644 --- a/src/support/bus/RedisBus.ts +++ b/src/support/bus/RedisBus.ts @@ -121,7 +121,8 @@ export class RedisBus implements EventBus { } down(): Awaitable { - this.subscriberConnection?.disconnect() - this.publisherConnection?.disconnect() + // The Redis service will clean up the connections when the framework exits, + // so we don't need to do anything here. + return undefined } } diff --git a/src/support/redis/Redis.ts b/src/support/redis/Redis.ts index f6cb364..3b8d096 100644 --- a/src/support/redis/Redis.ts +++ b/src/support/redis/Redis.ts @@ -4,7 +4,7 @@ import * as IORedis from 'ioredis' import {RedisOptions} from 'ioredis' import {Logging} from '../../service/Logging' import {Unit} from '../../lifecycle/Unit' -import {AsyncPipe} from '../../util' +import {AsyncPipe, Collection} from '../../util' export {RedisOptions} from 'ioredis' @@ -28,19 +28,25 @@ export class Redis extends Unit { */ private connection?: IORedis.Redis + /** + * Collection of all Redis connections opened by this service. + * We keep track of these here so we can make sure -all- of them get closed + * when the framework tries to shut down. + * @private + */ + private spawnedConnections: Collection = new Collection() + async up(): Promise { this.logging.info('Attempting initial connection to Redis...') - this.logging.debug('Config:') - this.logging.debug(Config) this.logging.debug(this.config) await this.getConnection() } async down(): Promise { this.logging.info('Disconnecting Redis...') - if ( this.connection?.status === 'ready' ) { - await this.connection.disconnect() - } + await this.spawnedConnections + .where('status', '=', 'ready') + .awaitMapCall('disconnect') } /** @@ -59,7 +65,9 @@ export class Redis extends Unit { */ public async getNewConnection(): Promise { const options = this.config.get('redis.connection') as RedisOptions - return new IORedis(options) + const inst = new IORedis(options) + this.spawnedConnections.push(inst) + return inst } /**