Skip to content

perf: copy-on-write for sentry_value_t clone#1794

Open
jpnurmi wants to merge 5 commits into
masterfrom
jpnurmi/ref/copy-on-write
Open

perf: copy-on-write for sentry_value_t clone#1794
jpnurmi wants to merge 5 commits into
masterfrom
jpnurmi/ref/copy-on-write

Conversation

@jpnurmi

@jpnurmi jpnurmi commented Jun 4, 2026

Copy link
Copy Markdown
Collaborator

Problem:

Both out-of-process handlers, crashpad and native, flush the global scope to __sentry-event whenever any scope properties change, to make it available for the handler process.

In sentry__scope_apply_to_event, the process of merging the scope into an event object is quite heavy. Passing references (event { tags: ref(scope->tags) }) is out of question, because that way any before_send modifications would leak to the scope and cause data loss. Therefore, we currently pass clones (event { tags: clone(scope->tags) }) instead. The problem is that the more data it has (tags, context, ...), the slower it gets.

Solution:

Pass (event { tags: cow(scope->tags) }) where the list of tags is scope->tags with refcount 2. Only and only if the before_send hook modifies it, the copy is detached so that the scope remains untouched.

Details:

Add copy-on-write (COW) semantics to sentry__value_clone() for list and object values. Instead of deep-copying items/pairs on clone, the new thing_t shares the underlying list_t/obj_t data via a data-level refcount. The actual copy is deferred to mutation time (thing_detach), making clone O(1) for flat containers.

Two-level refcounting:

  • thing_t.refcount: how many sentry_value_t handles reference the thing
  • list_t.refcount / obj_t.refcount: how many thing_ts share the data

Container children (nested lists/objects) are still recursively cloned to maintain value semantics at each nesting level.

pytest -v "tests/benchmark.py::test_benchmark_scope[crashpad-set_tag]":

1000x Median before Median after Improvement
Windows 4.826 ms 1.843 ms 61.81%
macOS 0.988 ms 0.174 ms 82.39%
Linux 0.004 ms 0.003 ms 25.00%

Close: #1551

@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 2 potential issues.

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 ec86d1f. Configure here.

Comment thread src/sentry_value.c
Comment thread src/sentry_value.c
@jpnurmi jpnurmi changed the title ref: copy-on-write for sentry_value_t clone wip(ref): copy-on-write for sentry_value_t clone Jun 5, 2026
@jpnurmi jpnurmi force-pushed the jpnurmi/ref/copy-on-write branch from ec86d1f to 3e7dd7b Compare July 6, 2026 09:53
@jpnurmi jpnurmi changed the title wip(ref): copy-on-write for sentry_value_t clone WIP: perf: copy-on-write for sentry_value_t clone Jul 6, 2026
Comment thread src/sentry_value.c
jpnurmi and others added 5 commits July 7, 2026 16:26
Add copy-on-write (COW) semantics to sentry__value_clone() for list and
object values. Instead of deep-copying items/pairs on clone, the new
thing_t shares the underlying list_t/obj_t data via a data-level
refcount. The actual copy is deferred to mutation time (thing_detach),
making clone O(1) for flat containers.

Two-level refcounting:
- thing_t.refcount: how many sentry_value_t handles reference the thing
- list_t.refcount / obj_t.refcount: how many thing_ts share the data

Container children (nested lists/objects) are still recursively cloned
to maintain value semantics at each nesting level.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
In `thing_clone_children`, if `sentry__value_clone` fails for a nested
container, the error was ignored. This resulted in clones that appeared
usable but were missing nested data. Add a check to return false on
clone failure, ensuring the entire operation fails rather than producing
an incomplete result.

Co-Authored-By: OpenAI Codex <noreply@openai.com>
Keep object copy-on-write detach atomic when cloning keys fails. Track
initialized pairs so partial clones can be freed without installing null keys.

Co-Authored-By: OpenAI Codex <noreply@openai.com>
@jpnurmi jpnurmi force-pushed the jpnurmi/ref/copy-on-write branch from 3e7dd7b to bffeb8e Compare July 7, 2026 14:27
@jpnurmi jpnurmi changed the title WIP: perf: copy-on-write for sentry_value_t clone perf: copy-on-write for sentry_value_t clone Jul 7, 2026
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.

before_send event modifications leak to the scope

1 participant