fix: remove in-memory cache to fix multi-instance status inconsistency#11
Merged
Merged
Conversation
The TimeOffCache was an in-memory singleton Map per Node process. In a multi-instance Rocket.Chat deployment each instance held its own isolated cache with a 1-hour TTL, while MongoDB was the only shared store. After a user ended their time off on one instance, other instances kept serving the stale ON_TIME_OFF entry until their cache expired, wrongly notifying senders that the user was still OOO and forcing repeated slash-command retries. Remove the cache entirely and read/write straight through the persistence repository. Lookups use the existing indexed association query, so the app stays performant while every instance always sees the current status. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
scuciatto
approved these changes
Jun 25, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
In a multi-instance Rocket.Chat deployment, the TimeOff app reports stale "out of office" status. After a user runs
/timeoff end(orstart), other instances keep showing the old status for up to an hour — forcing users to re-run the slash command repeatedly until it "sticks".Root cause
TimeOffCachewas an in-memory singletonMapwith a 1-hour TTL. Each Rocket.Chat instance runs its own Node process, so each had its own isolated copy of this cache. MongoDB (the apps-engine persistence layer) is the only store actually shared between instances./timeoff endon instance A →saveTimeOff()writes to MongoDB and refreshes instance A's cache.ON_TIME_OFFentry (TTL up to 1h).Fix
Remove the in-memory cache entirely and read/write straight through the persistence repository. MongoDB is already the shared source of truth, and lookups use the existing indexed association query (
MISC:'timeoff'+USER:coreUserId) — a cheap point lookup called at most once per DM/command. So every instance always sees the current status on the first try, with no meaningful performance cost.A cache layered on top of the shared persistence store provided no real benefit here and was the direct cause of the bug.
Changes
TimeOffCache.ts— deletedservices/TimeOffService.ts—saveTimeOff/getTimeOffByUserIdgo straight through the repositoryTimeOffApp.ts— removed the cache import and theinvalidateCache()call inonEnableThe public
ITimeOffServiceinterface is unchanged, so callers needed no edits.Testing
npm run typecheckandnpm run lintpass clean🤖 Generated with Claude Code