diff --git a/changelog.d/671.bugfix b/changelog.d/671.bugfix new file mode 100644 index 00000000..a7a0b965 --- /dev/null +++ b/changelog.d/671.bugfix @@ -0,0 +1 @@ +Ensure Hookshot shuts down faster when running feeds. \ No newline at end of file diff --git a/src/Bridge.ts b/src/Bridge.ts index 30ab5625..b3ef3f49 100644 --- a/src/Bridge.ts +++ b/src/Bridge.ts @@ -52,6 +52,7 @@ export class Bridge { private github?: GithubInstance; private adminRooms: Map = new Map(); private widgetApi?: BridgeWidgetApi; + private feedReader?: FeedReader; private provisioningApi?: Provisioner; private replyProcessor = new RichRepliesPreprocessor(true); @@ -77,6 +78,8 @@ export class Bridge { } public stop() { + this.feedReader?.stop(); + this.tokenStore.stop(); this.as.stop(); if (this.queue.stop) this.queue.stop(); } @@ -129,17 +132,6 @@ export class Bridge { this.github, ); - if (this.config.feeds?.enabled) { - new FeedReader( - this.config.feeds, - this.connectionManager, - this.queue, - // Use default bot when storing account data - this.as.botClient, - ); - } - - if (this.config.provisioning) { const routers = []; if (this.config.jira) { @@ -756,6 +748,19 @@ export class Bridge { } await queue.onIdle(); log.info(`All connections loaded`); + + // Load feeds after connections, to limit the chances of us double + // posting to rooms if a previous hookshot instance is being replaced. + if (this.config.feeds?.enabled) { + this.feedReader = new FeedReader( + this.config.feeds, + this.connectionManager, + this.queue, + // Use default bot when storing account data + this.as.botClient, + ); + } + await this.as.begin(); log.info(`Bridge is now ready. Found ${this.connectionManager.size} connections`); this.ready = true; diff --git a/src/UserTokenStore.ts b/src/UserTokenStore.ts index 9035e6ca..c0d01dfe 100644 --- a/src/UserTokenStore.ts +++ b/src/UserTokenStore.ts @@ -81,6 +81,12 @@ export class UserTokenStore extends TypedEmitter { this.key = await fs.readFile(this.keyPath); } + public stop() { + for (const session of this.oauthSessionStore.values()) { + clearTimeout(session.timeout); + } + } + public async storeUserToken(type: TokenType, userId: string, token: string, instanceUrl?: string): Promise { if (!this.config.checkPermission(userId, type, BridgePermissionLevel.login)) { throw new ApiError('User does not have permission to log in to service', ErrCode.ForbiddenUser); diff --git a/src/feeds/FeedReader.ts b/src/feeds/FeedReader.ts index 6f37730c..645fbaec 100644 --- a/src/feeds/FeedReader.ts +++ b/src/feeds/FeedReader.ts @@ -111,6 +111,9 @@ export class FeedReader { static readonly seenEntriesEventType = "uk.half-shot.matrix-hookshot.feed.reader.seenEntries"; + private shouldRun = true; + private timeout?: NodeJS.Timeout; + constructor( private readonly config: BridgeConfigFeeds, private readonly connectionManager: ConnectionManager, @@ -140,6 +143,11 @@ export class FeedReader { }); } + public stop() { + clearTimeout(this.timeout); + this.shouldRun = false; + } + private calculateFeedUrls(): void { // just in case we got an invalid URL somehow const normalizedUrls = []; @@ -305,7 +313,11 @@ export class FeedReader { log.debug(`Feed fetching took ${elapsed / 1000}s, sleeping for ${sleepFor / 1000}s`); } - setTimeout(() => { + + this.timeout = setTimeout(() => { + if (!this.shouldRun) { + return; + } void this.pollFeeds(); }, sleepFor); }