Skip to content

Conversation

@JPeer264
Copy link
Member

@JPeer264 JPeer264 commented Nov 7, 2025

Problem

Previously, the client would process all incoming events without any limit, which could lead to unbounded growth of pending events/promises in memory. This could cause performance issues and memory pressure in high-throughput scenarios. This occurs when two conditions are met:

  • when an integration with an async processEvent are added (e.g. ContextLines, which is a defaultIntegration)
  • events, e.g. Sentry.captureException, are called synchronously
Sentry.init({ ... });

// ...

for (let i = 0; i < 5000; i++) {
  Sentry.captureException(new Error());
}

Solution

This PR adds a PromiseBuffer to the Client class to limit the number of concurrent event processing operations.

  • Introduced a _promiseBuffer in the Client class that limits concurrent event processing
  • The buffer size defaults to DEFAULT_TRANSPORT_BUFFER_SIZE (64) but can be configured via transportOptions.bufferSize
  • When the buffer is full, events are rejected and properly tracked as dropped events with the queue_overflow reason
    • Please tak
  • Modified the _process() method to:
    • Accept a task producer function instead of a promise directly (lazy evaluation)
    • Use the promise buffer to manage concurrent operations
    • Track the data category for proper dropped event categorization

Special 👀 on

  • About reusing transportOptions.bufferSize: Not sure if this is the best technique, but IMO both should have the same size - because if it wouldn't it would be capped at a later stage (asking myself if the transport still needs the promise buffer - as we have it now way earlier in place)
  • The _process takes now a DataCategory. At the time of the process the event type is almost unknown. Not sure if I assumed the categories correctly there, or if there is another technique of getting the type (edit: a comment by Cursor helped a little and I added a helper function)
  • recordDroppedEvent is now printing it one after each other - theoretically we can count all occurences and print the count on it. I decided against this one, since it would delay the user feedback - this can be challenged though

@JPeer264 JPeer264 force-pushed the jp/memory-leak branch 2 times, most recently from 619a016 to 1695dd4 Compare November 7, 2025 15:43
processEvent(event: Event): Event | null | PromiseLike<Event | null> {
return new Promise(resolve => setTimeout(() => resolve(event), 1));
}
}
Copy link

Choose a reason for hiding this comment

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

Bug: Async Test Integration Missing Event Processor Setup

The AsyncTestIntegration defines a processEvent method but lacks a setupOnce or setup method to register it as an event processor. Without registration, the async processEvent won't execute, causing tests using this integration to pass incorrectly without actually exercising the promise buffer's async event handling logic.

Fix in Cursor Fix in Web

@github-actions
Copy link
Contributor

github-actions bot commented Nov 7, 2025

size-limit report 📦

Path Size % Change Change
@sentry/browser 24.67 kB +0.31% +74 B 🔺
@sentry/browser - with treeshaking flags 23.15 kB +0.31% +71 B 🔺
@sentry/browser (incl. Tracing) 41.26 kB +0.09% +34 B 🔺
@sentry/browser (incl. Tracing, Profiling) 45.54 kB +0.1% +43 B 🔺
@sentry/browser (incl. Tracing, Replay) 79.74 kB +0.05% +35 B 🔺
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags 69.42 kB +0.07% +48 B 🔺
@sentry/browser (incl. Tracing, Replay with Canvas) 84.44 kB +0.06% +43 B 🔺
@sentry/browser (incl. Tracing, Replay, Feedback) 96.65 kB +0.09% +80 B 🔺
@sentry/browser (incl. Feedback) 41.35 kB +0.21% +84 B 🔺
@sentry/browser (incl. sendFeedback) 29.35 kB +0.29% +82 B 🔺
@sentry/browser (incl. FeedbackAsync) 34.29 kB +0.3% +101 B 🔺
@sentry/react 26.38 kB +0.32% +82 B 🔺
@sentry/react (incl. Tracing) 43.24 kB +0.09% +37 B 🔺
@sentry/vue 29.11 kB +0.14% +40 B 🔺
@sentry/vue (incl. Tracing) 43.08 kB +0.2% +85 B 🔺
@sentry/svelte 24.68 kB +0.31% +76 B 🔺
CDN Bundle 26.95 kB +0.22% +58 B 🔺
CDN Bundle (incl. Tracing) 41.84 kB +0.16% +64 B 🔺
CDN Bundle (incl. Tracing, Replay) 78.35 kB +0.08% +57 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback) 83.82 kB +0.06% +50 B 🔺
CDN Bundle - uncompressed 79.07 kB +0.29% +223 B 🔺
CDN Bundle (incl. Tracing) - uncompressed 124.19 kB +0.18% +221 B 🔺
CDN Bundle (incl. Tracing, Replay) - uncompressed 240.22 kB +0.1% +221 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 252.98 kB +0.09% +221 B 🔺
@sentry/nextjs (client) 45.39 kB +0.19% +86 B 🔺
@sentry/sveltekit (client) 41.65 kB +0.11% +42 B 🔺
@sentry/node-core 50.83 kB +0.15% +74 B 🔺
@sentry/node 157.91 kB +0.06% +90 B 🔺
@sentry/node - without tracing 92.71 kB +0.08% +73 B 🔺
@sentry/aws-serverless 106.43 kB +0.07% +66 B 🔺

View base workflow run

@github-actions
Copy link
Contributor

github-actions bot commented Nov 7, 2025

node-overhead report 🧳

Note: This is a synthetic benchmark with a minimal express app and does not necessarily reflect the real-world performance impact in an application.

Scenario Requests/s % of Baseline Prev. Requests/s Change %
GET Baseline 9,406 - 9,103 +3%
GET With Sentry 1,710 18% 1,364 +25%
GET With Sentry (error only) 6,153 65% 6,074 +1%
POST Baseline 1,188 - 1,206 -1%
POST With Sentry 584 49% 511 +14%
POST With Sentry (error only) 1,065 90% 1,064 +0%
MYSQL Baseline 3,322 - 3,273 +1%
MYSQL With Sentry 445 13% 463 -4%
MYSQL With Sentry (error only) 2,720 82% 2,675 +2%

View base workflow run

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants