Skip to content

fix: Attempted fix for dashboard not loading sometimes#1109

Open
Klakurka wants to merge 1 commit intomasterfrom
fix/dashboard-not-loading
Open

fix: Attempted fix for dashboard not loading sometimes#1109
Klakurka wants to merge 1 commit intomasterfrom
fix/dashboard-not-loading

Conversation

@Klakurka
Copy link
Member

@Klakurka Klakurka commented Feb 20, 2026

Related to #1098

Description

Sometimes (especially after a re-deploy) the dashboard doesn't load without doing an extra refresh.

Test plan

I'm not 100% how to reproduce it without trying this for real in prod but perhaps there is a good way? We can also give it a quick deploy in prod together.

Summary by CodeRabbit

  • Bug Fixes
    • Concurrent method calls now gracefully handle duplicate requests by returning the existing in-flight result instead of throwing an error, improving reliability when identical requests are made simultaneously.

@Klakurka Klakurka self-assigned this Feb 20, 2026
@Klakurka Klakurka added the bug Something isn't working label Feb 20, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 20, 2026

📝 Walkthrough

Walkthrough

Modified in-flight method call tracking in Redis module from Set-based approach with error-throwing to Promise-mapping with deduplication. Concurrent calls now return the existing promise instead of throwing, enabling call deduplication.

Changes

Cohort / File(s) Summary
Redis In-Flight Call Deduplication
redis/index.ts
Replaced per-user Set tracking with Promise-based object mapping. Calls now return existing promise instead of throwing on duplicates. Updated type definitions and concurrency check logic.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Suggested reviewers

  • lissavxo

Poem

🐰 A rabbit hops through promises of grace,
No more throwing when calls share a place,
Deduplication's the name of the game,
Return what exists—no need for the blame! 🎀

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title vaguely refers to a dashboard loading issue but does not clearly convey the actual technical change: switching from Set-based to Promise-based concurrency handling for in-flight method calls. Consider a more specific title that reflects the core technical change, such as 'fix: Handle duplicate in-flight method calls by returning existing promise instead of throwing' or similar.
✅ Passed checks (2 passed)
Check name Status Explanation
Description check ✅ Passed The description addresses the user-facing symptom (dashboard not loading sometimes) and includes a test plan, but lacks technical details about the underlying fix and the architectural changes made to the codebase.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/dashboard-not-loading

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@redis/index.ts`:
- Around line 97-100: The code double-awaits and miscasts the resolved value:
change the return to cast the stored promise before awaiting so you await the
Promise<T>, not assert the resolved value as a promise; locate the pendingCalls
lookup (this.pendingCalls[userId][methodName]) and replace `return await (await
existingCall as Promise<T>)` with `return await (existingCall as Promise<T>)`
(or simply `return await existingCall` with the appropriate generic typing) to
ensure the cast applies to the Promise, not its resolved value.
- Around line 108-113: The code sets this.pendingCalls[userId][methodName] =
undefined which leaves the property present so Object.keys(...) never becomes
zero; change the removal to delete this.pendingCalls[userId][methodName] (in the
block that checks this.pendingCalls[userId]?.[methodName] === pendingCall) so
the property is actually removed and the subsequent cleanup check for
Object.keys(this.pendingCalls[userId]).length === 0 in the pendingCalls cleanup
logic will work as intended.

Comment on lines +97 to 100
const existingCall = this.pendingCalls[userId][methodName]
if (existingCall !== undefined) {
return await (await existingCall as Promise<T>)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Double-await produces an incorrect type assertion on the resolved value, not the Promise.

return await (await existingCall as Promise<T>)

The inner await existingCall already resolves Promise<unknown> to the plain T value. The subsequent as Promise<T> then incorrectly asserts that plain T value (e.g. a DashboardData object) as a Promise<T>, and the outer await re-awaits it. This only works by coincidence because await nonThenable returns the value unchanged; if T were itself a PromiseLike, the second await would unwrap it one extra level, producing Awaited<T> instead of T.

The cast should be applied to existingCall (the promise) before awaiting:

🐛 Proposed fix
-      return await (await existingCall as Promise<T>)
+      return await (existingCall as Promise<T>)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@redis/index.ts` around lines 97 - 100, The code double-awaits and miscasts
the resolved value: change the return to cast the stored promise before awaiting
so you await the Promise<T>, not assert the resolved value as a promise; locate
the pendingCalls lookup (this.pendingCalls[userId][methodName]) and replace
`return await (await existingCall as Promise<T>)` with `return await
(existingCall as Promise<T>)` (or simply `return await existingCall` with the
appropriate generic typing) to ensure the cast applies to the Promise, not its
resolved value.

Comment on lines +108 to 113
if (this.pendingCalls[userId]?.[methodName] === pendingCall) {
this.pendingCalls[userId][methodName] = undefined
}
if (this.pendingCalls[userId] !== undefined && Object.keys(this.pendingCalls[userId]).length === 0) {
this.pendingCalls[userId] = {}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

= undefined leaves the key in the object, making the size-zero check permanently dead code.

this.pendingCalls[userId][methodName] = undefined does not remove the key — Object.keys(obj) still returns it. After any completed call, Object.keys(this.pendingCalls[userId]).length is always ≥ 1, so the second if branch (lines 111-113) can never fire.

Use delete to actually remove the key so that the cleanup branch works as intended:

🐛 Proposed fix
     if (this.pendingCalls[userId]?.[methodName] === pendingCall) {
-      this.pendingCalls[userId][methodName] = undefined
+      delete this.pendingCalls[userId][methodName]
     }
     if (this.pendingCalls[userId] !== undefined && Object.keys(this.pendingCalls[userId]).length === 0) {
       this.pendingCalls[userId] = {}
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@redis/index.ts` around lines 108 - 113, The code sets
this.pendingCalls[userId][methodName] = undefined which leaves the property
present so Object.keys(...) never becomes zero; change the removal to delete
this.pendingCalls[userId][methodName] (in the block that checks
this.pendingCalls[userId]?.[methodName] === pendingCall) so the property is
actually removed and the subsequent cleanup check for
Object.keys(this.pendingCalls[userId]).length === 0 in the pendingCalls cleanup
logic will work as intended.

@Klakurka Klakurka requested a review from chedieck February 25, 2026 23:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant