Skip to content

feat(scope): Split global scope and expose isolation scope - #2160

Merged
jpnurmi merged 1 commit into
jpnurmi/perf/scope-batch-updatefrom
jpnurmi/feat/scope-access
Oct 15, 2026
Merged

jpnurmi merged 1 commit into
jpnurmi/perf/scope-batch-updatefrom
jpnurmi/feat/scope-access

Conversation

@jpnurmi

@jpnurmi jpnurmi commented Oct 2, 2026 •

Copy link
Copy Markdown
Collaborator

Split the existing global scope into:

  • an internal, read-only global scope for SDK/OS metadata and configured defaults, and
  • a mutable isolation scope used by top-level APIs.

Move user data, observers, and backend flushing to the isolation scope, and add sentry_scope_acquire() and sentry_scope_release() for direct access to it:

sentry_scope_t *scope = sentry_acquire_scope();
do_something(scope);
sentry_scope_release(scope);

Implement Sentry's three-scope model with global -> isolation -> local scope layering. Retain parent references and merge layers when preparing events, telemetry, and attachments. Local values take precedence over isolation and global defaults.

The SDK follows the "global mode" used by other Sentry SDKs for client applications: all threads share the same process-wide isolation scope. Changes made through top-level APIs or an acquired reference are shared across all threads, retaining backwards compatibility with the previous implementation around a single global scope.

Dev docs: https://develop.sentry.dev/sdk/foundations/state-management/scopes/#isolation-scope

@github-actions

github-actions Bot commented Oct 2, 2026 •

Copy link
Copy Markdown
Messages
📖 Do not forget to update Sentry-docs with your feature once the pull request gets approved.

Generated by 🚫 dangerJS against 59a151f

@jpnurmi
jpnurmi force-pushed the jpnurmi/feat/scope-access branch from 706849a to d5c21b0 Compare October 2, 2026 16:54
@jpnurmi
jpnurmi added this pull request to stack #2161 October 2, 2026 16:56
Comment thread src/sentry_scope.c

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Stale Bugbot comment from a previous run.

Comment thread src/sentry_scope.c Outdated
@jpnurmi
jpnurmi force-pushed the jpnurmi/feat/scope-access branch 3 times, most recently from 8b7e22e to 4dd460f Compare October 2, 2026 19:18

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Stale Bugbot comment from a previous run.

Comment thread src/sentry_core.c Outdated
Comment thread src/sentry_core.c Outdated
Comment thread src/sentry_scope.c Outdated
@codecov

codecov Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 85.67119% with 95 lines in your changes missing coverage. Please review.
✅ Project coverage is 75.61%. Comparing base (0c9b059) to head (59a151f).

Additional details and impacted files
@@                         Coverage Diff                         @@
##           jpnurmi/perf/scope-batch-update    #2160      +/-   ##
===================================================================
+ Coverage                            75.58%   75.61%   +0.03%     
===================================================================
  Files                                  103      103              
  Lines                                28348    28685     +337     
  Branches                              5192     5290      +98     
===================================================================
+ Hits                                 21427    21691     +264     
- Misses                                5565     5628      +63     
- Partials                              1356     1366      +10     
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@jpnurmi

jpnurmi commented Oct 5, 2026 •

Copy link
Copy Markdown
Collaborator Author

Discussion continued from #2107 (comment)

Something like sentry_get_scope() could be a good name. Eventually, we may want to support isolation scopes, and these API calls would automatically work with such too.

@limbonaut Unfortunately, a plain getter wouldn't work because returning an owned reference would not be safe, and shared_ptr-like automatic incref/decref semantics are impossible in C99, because MSVC doesn't support cleanup attribute.

Here's a proposal to "acquire" a reference, which needs to be released after you're done. If/when adding support for isolation scopes, one could add sentry_acquire_current_scope() and sentry_acquire_isolation_scope() alongside it. What do you think?

@limbonaut limbonaut left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Looks like it needs more work than initially expected 😅 The current implementation causes issues in some sentry_scope_set_x and capture_x functions, such as sentry_scope_set_tag. Also, I believe, changes are needed in the apply_to_x family of functions to check whether the passed scope is actually one of the layered scopes (currently only the global scope is layered). I.e. if (passed_scope == global) passed_scope = null.

Specific examples:

  1. Acquired scope mutations via sentry_scope_set_x skip backend flush.
  2. sentry_scope_capture_event(scope, event, NULL) does not check for global scope and, for instance, adds breadcrumbs twice.
  3. sentry_scope_add_breadcrumb skips crash persistence and filtering.

If you think it's too much overhead for what we currently need, we can still go back to adding dedicated functions for batched updates on the global scope.

Comment thread include/sentry.h
* `sentry_local_scope_new`.
* `sentry_local_scope_new`, or acquired via `sentry_acquire_global_scope`.
*/
SENTRY_API void sentry_scope_free(sentry_scope_t *scope);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Yeah, I think release is a better name to pair with acquire, even if free is used underneath. After all, even sentry_value has a dedicated decref function. scope_free can be confusing. Should scopes adopt refcounting semantics in the API akin to sentry_value?

@limbonaut

Copy link
Copy Markdown
Collaborator

Here's a proposal to "acquire" a reference, which needs to be released after you're done. If/when adding support for isolation scopes, one could add sentry_acquire_current_scope() and sentry_acquire_isolation_scope() alongside it. What do you think?

We already use similar semantics in sentry_value_t. I'd add a dedicated release function, as free can be confusing in the context of an acquired scope reference.

@jpnurmi

jpnurmi commented Oct 5, 2026

Copy link
Copy Markdown
Collaborator Author

Thanks, good catch! ❤️ The global mutators in sentry_core have all kinds of extra actions, and this bypasses all that. 😅

@jpnurmi
jpnurmi marked this pull request as draft October 5, 2026 10:56
@jpnurmi
jpnurmi removed this pull request from stack #2161 October 6, 2026 15:08
@jpnurmi
jpnurmi force-pushed the jpnurmi/feat/scope-access branch from 06f4d7e to 88ad7b5 Compare October 6, 2026 15:21
@jpnurmi
jpnurmi changed the base branch from master to jpnurmi/ref/global-scope October 6, 2026 15:21
@jpnurmi
jpnurmi force-pushed the jpnurmi/feat/scope-access branch from 88ad7b5 to b475fd3 Compare October 9, 2026 08:19
@jpnurmi jpnurmi changed the title feat(scope): Add global scope acquisition feat(scope): Split global scope and expose isolation scope Oct 9, 2026
@jpnurmi
jpnurmi changed the base branch from jpnurmi/ref/global-scope to jpnurmi/perf/scope-batch-update October 9, 2026 08:28
@jpnurmi
jpnurmi added this pull request to stack #2183 October 9, 2026 08:28
@jpnurmi
jpnurmi force-pushed the jpnurmi/feat/scope-access branch from 5923605 to 7fd5154 Compare October 9, 2026 09:09
@jpnurmi
jpnurmi force-pushed the jpnurmi/feat/scope-access branch from 7fd5154 to 5fc726d Compare October 9, 2026 10:39
@jpnurmi

jpnurmi commented Oct 9, 2026

Copy link
Copy Markdown
Collaborator Author

I'm back with a new proposal with ideas stolen straight from https://develop.sentry.dev/sdk/foundations/state-management/scopes/ 🤓

I ended up adding a separate isolation scope as a child of the global scope, and exposing direct access to that instead. There might still be some quirks and details to tweak, but this already gives a lot cleaner separation between:

  • read-only global scope with SDK/OS metadata and configured defaults from options, versus
  • mutable isolation scope that inherits its defaults from the global parent scope

For example, clearing the isolation scope preserves the inherited defaults, so there's no need to fight back and try to restore them after clearing.

For now, it operates in a "global mode" as sentry-dotnet and sentry-java call it, meaning there's only one process-wide isolation scope. I think it's a good start, makes sense for client applications, and retains backwards compatibility with what we had. Should be doable to extend this later with thread-local isolation scopes as an opt-in, if we want.

@jpnurmi
jpnurmi force-pushed the jpnurmi/feat/scope-access branch from bd58389 to b6698d0 Compare October 12, 2026 07:58
@jpnurmi
jpnurmi force-pushed the jpnurmi/feat/scope-access branch from b6698d0 to 820f31f Compare October 12, 2026 09:10
@jpnurmi
jpnurmi marked this pull request as ready for review October 12, 2026 10:02

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Stale Bugbot comment from a previous run.

Comment thread src/backends/sentry_backend_crashpad.cpp
Comment thread src/sentry_scope.c
@limbonaut

Copy link
Copy Markdown
Collaborator

I have a few concerns about this scope model implementation. I'm not sure the global scope is necessarily supposed to be read-only. As I understand it, it's just a scope that's applied to the whole application (i.e. process).

The idea behind an isolation scope is to provide a separate layer for clearly defined runtime jobs (think of a single request in a web application that handles multiple requests). But what constitutes a clearly defined job in a native application? I think this can only be defined by users. The specs mention that the isolation scope is supposed to be forked automatically by SDK integrations, but I don't think it's possible to automate this without knowing anything about the app's purpose.

Here, the isolation scope feels like another layer in the global sense. It doesn't really provide much isolation. I think it should be more about scope lifetime (maybe thread locality), not scope's mutability.

Another way to think about isolation scopes is in terms of the old Hubs model. When users needed isolation for a distinct job, they could make another Hub, which would keep reporting for that job separate from others.

@jpnurmi

jpnurmi commented Oct 13, 2026 •

Copy link
Copy Markdown
Collaborator Author

I actually had async signal-safe thread-local storage implementation at first, but then deliberately chose to drop it and start with one process-wide isolation scope. This model is directly inspired by the "global mode" in sentry-dotnet and sentry-java, used by default for client/mobile/desktop apps:

This not only keeps behavioral backwards compatibility, but also operates in the way direct Native SDK users presumably expect by default going forward, matching the "client/mobile/desktop" default in other SDKs. However, the structure is suitable for extending it with opt-in thread-local isolation scopes later.

@jpnurmi
jpnurmi force-pushed the jpnurmi/feat/scope-access branch from 0b11a8c to 23a4316 Compare October 13, 2026 09:04
@limbonaut

limbonaut commented Oct 13, 2026 •

Copy link
Copy Markdown
Collaborator

Here, to summarize the discussion we had with @jpnurmi and our conclusions.

The global mode for the isolation scope is the right call for most desktop applications. This means that the isolation scope is a layer on top of the global scope, and it's also a process-wide context. Currently, its main purpose is to isolate the data that goes onto global scope from sentry_scope_clear() and similar mutations. This simplifies the data protection if we expose an accessor for the process-wide scope.

At some point, we may want (not settled yet) to expose the global_mode option, that, if disabled, would turn isolation scopes into thread-specific scopes (one per thread), with the crash handlers support, so each thread would get a dedicated context to write into. This would be more suitable for server applications, which handle tasks in separate threads. We are still not sure what role the isolation scopes should have in a generic desktop application, but the global mode seems to be the right model.

As it currently stands, the global scope would effectively be read-only from public API, but we may add accessors for it in the future.

@limbonaut limbonaut left a comment •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I can see why it's useful from data protection point. It's a good mechanism to avoid dodgy code that keeps re-adding data after clear(). And the mechanism is very straightforward and clean. One remaining question I have: which options should be intializing the global data, and which should go onto isolation scope? Things like attachments, when added through options, cannot be removed via usual means, unless we provide accessor to the global scope. What about changes made via initial_scope, should that go to isolation?

@jpnurmi

jpnurmi commented Oct 13, 2026

Copy link
Copy Markdown
Collaborator Author

One remaining question I have: which options should be intializing the global data, and which should go onto isolation scope? Things like attachments, when added through options, cannot be removed via usual means, unless we provide accessor to the global scope.

The split is, in a way, analogous to sentry_options_set_xxx where the options are frozen after SDK init vs. sentry_set_xxx for later runtime changes.

  • SDK/OS defaults and init options -> global scope
  • any later runtime changes -> isolation scope

What about changes made via initial_scope, should that go to isolation?

The initial_scope hook is nicely between the two category definitions. I chose the isolation scope for it because it felt like "early seeding" of scope changes. It could probably just as well be the global scope, which would provide means for tweaking global defaults. Do you have any preference?

@jpnurmi jpnurmi added the v1 Targets 1.0 (v1 branch) label Oct 14, 2026
@jpnurmi
jpnurmi removed this pull request from stack #2183 October 14, 2026 10:46
@jpnurmi
jpnurmi force-pushed the jpnurmi/feat/scope-access branch from 23a4316 to c8e99a2 Compare October 14, 2026 10:47
@jpnurmi
jpnurmi added this pull request to stack #2198 October 14, 2026 10:51
Split the existing global scope into an internal, read-only global
scope for SDK/OS metadata and configured defaults, and a mutable
isolation scope used by top-level APIs. Move user data, observers, and
backend flushing to the isolation scope.

Add `sentry_scope_acquire()` and `sentry_scope_release()` for direct
isolation scope access.

Implement Sentry's three-scope model with global -> isolation -> local
scope layering. Retain parent references and merge layers when preparing
events, telemetry, and attachments. Local values take precedence over
isolation and global defaults.

The SDK follows the global mode used by other Sentry SDKs for client
applications: all threads share the same process-wide isolation scope.
Changes made through top-level APIs or an acquired reference are shared
across all threads.

See:
- https://develop.sentry.dev/sdk/foundations/state-management/scopes/#isolation-scope
@jpnurmi
jpnurmi force-pushed the jpnurmi/feat/scope-access branch from c8e99a2 to 59a151f Compare October 15, 2026 12:18

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 59a151f. Configure here.

Comment thread src/sentry_scope.c
@jpnurmi
jpnurmi merged commit 1f7b255 into v1 Oct 15, 2026
67 checks passed
@jpnurmi
jpnurmi deleted the jpnurmi/feat/scope-access branch October 15, 2026 13:06
@jpnurmi jpnurmi mentioned this pull request Oct 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

v1 Targets 1.0 (v1 branch)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants