Skip to content

perf(api-service): reduce DB round-trips for inbox mark-seen#10674

Draft
cursor[bot] wants to merge 1 commit intonextfrom
cursor/slow-endpoint-performance-5f07
Draft

perf(api-service): reduce DB round-trips for inbox mark-seen#10674
cursor[bot] wants to merge 1 commit intonextfrom
cursor/slow-endpoint-performance-5f07

Conversation

@cursor
Copy link
Copy Markdown
Contributor

@cursor cursor bot commented Apr 13, 2026

Context

APM data for the production US API over the last day showed the inbox “mark notifications as seen” web transaction as the slowest among high-volume routes, with database time as the dominant contributor and external time present but secondary. Error rate for that route was negligible relative to other endpoints, so the change targets latency, not correctness.

Root cause

The handler ultimately goes through MessageRepository.updateMessagesStatus, which:

  1. Loads all matching document _ids for the filter.
  2. For each batch of IDs, runs a primary updateMany-equivalent for the status fields, then a second conditional update for firstSeenDate when marking as seen.

With the previous batch size, a single request that touched many messages issued many sequential database round-trips (two writes per batch). The inbox use case also split explicit ID updates into small batches, which multiplied the preliminary ID find work.

What we changed

  • MessageRepository: Use a larger ID chunk for status updates and, for the “mark as seen” path, send the paired updates as one ordered bulkWrite per chunk (still honoring the prior write concern). This preserves behavior (including firstSeenDate backfill) while cutting round-trips.
  • BaseRepository.bulkWrite: Allow optional writeConcern / session so repositories can pass through Mongo options without touching _model directly.
  • MarkNotificationsAsSeen: Increase the per-repository batch size when updating by notification IDs so fewer full find+update cycles run for large ID lists.

Risk / review notes

  • Ordered bulkWrite keeps the main status update before the firstSeenDate conditional update within each chunk.
  • Chunk size is chosen to balance BSON limits on $in queries vs fewer round-trips.

Testing

  • pnpm exec tsc -p libs/dal/tsconfig.json --noEmit
  • API unit tests could not be run in this environment because the test script sets NODE_OPTIONS=--no-experimental-strip-types, which Node v20 rejects.
Open in Web View Automation 

- MessageRepository: larger status-update chunks and ordered bulkWrite
  pairing main update with firstSeenDate backfill (same write concern)
- BaseRepository.bulkWrite: optional writeConcern/session passthrough
- MarkNotificationsAsSeen: larger per-call ID batches to reduce prefetch finds

Co-authored-by: Dima Grossman <dima@grossman.io>
@netlify
Copy link
Copy Markdown

netlify bot commented Apr 13, 2026

Deploy Preview for dashboard-v2-novu-staging canceled.

Name Link
🔨 Latest commit c640284
🔍 Latest deploy log https://app.netlify.com/projects/dashboard-v2-novu-staging/deploys/69dc8a01b2dfa6000861683c

@github-actions
Copy link
Copy Markdown
Contributor

Hey there and thank you for opening this pull request! 👋

We require pull request titles to follow specific formatting rules and it looks like your proposed title needs to be adjusted.

Your PR title is: perf(api-service): reduce DB round-trips for inbox mark-seen

Requirements:

  1. Follow the Conventional Commits specification
  2. As a team member, include Linear ticket ID at the end: fixes TICKET-ID or include it in your branch name

Expected format: feat(scope): Add fancy new feature fixes NOV-123

Details:

PR title must end with 'fixes TICKET-ID' (e.g., 'fixes NOV-123') or include ticket ID in branch name

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant