Skip to content

feat: Add scope feature flag API#8147

Open
denrase wants to merge 29 commits into
mainfrom
feat/feature-flag-scope-api
Open

feat: Add scope feature flag API#8147
denrase wants to merge 29 commits into
mainfrom
feat/feature-flag-scope-api

Conversation

@denrase

@denrase denrase commented Jun 22, 2026

Copy link
Copy Markdown
Collaborator

📜 Description

Adds public Swift APIs to record and remove feature flag evaluations on SentrySDK, SentryHub, and Scope. Feature flags are attached to regular error/message events, excluded from transactions/replays/feedback, and synchronized with scope observers, so crash-time scope snapshots also contain the latest flags context data.

💡 Motivation and Context

Closes #7989

💚 How did you test it?

Unit tests + Sample app

https://sentry-sdks.sentry.io/issues/6605337140/events/latest/?project=5428557&query=is%3Aunresolved&referrer=latest-event

Bildschirmfoto 2026-06-22 um 16 37 24

📝 Checklist

You have to check all boxes before merging:

  • I added tests to verify the changes.
  • No new PII added or SDK only sends newly added PII if sendDefaultPII is enabled.
  • I updated the docs if needed.
  • I updated the wizard if needed.
  • Review from the native team if needed.
  • No breaking change or entry added to the changelog.
  • No breaking change for hybrid SDKs or communicated to hybrid SDKs.
  • If I added a new public API, I also added it to the SentryObjC wrapper.

denrase added 20 commits June 2, 2026 15:45
Introduces the internal foundation for feature flag evaluation tracking.

The implementation adds a shared feature flag value/evaluation model plus dedicated buffers for scope and span storage. Scope storage keeps the latest unique evaluations up to the configured internal limit and evicts the oldest entry on overflow. Span storage has its own smaller per-span limit and rejects new flag names once full while still allowing updates to existing flags.

This also wires the private storage into scope lifecycle behavior:
- scope cloning copies feature flags without sharing future mutations
- `clear()` clears feature flags
- scope serialization includes the stored flag context
- span internals can store and serialize feature flag evaluations as span data

No public feature flag API is added in this PR; this is the private storage layer that later API and integration work will build on.
- faster insertion/lookup
- index shifting paths keep same o(n) complexity
Adds public Swift APIs to record and remove feature flag evaluations on SentrySDK, SentryHub, and Scope. Feature flags are attached to regular error/message events, excluded from transactions/replays/feedback, and synchronized with scope observers, so crash-time scope snapshots also contain the latest flags context data.
@github-actions

github-actions Bot commented Jun 22, 2026

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

Generated by 🚫 dangerJS against 4563681

@denrase denrase self-assigned this Jun 22, 2026
@denrase denrase marked this pull request as ready for review June 23, 2026 09:35
@denrase denrase requested a review from philipphofmann as a code owner June 23, 2026 09:35
@denrase

denrase commented Jun 23, 2026

Copy link
Copy Markdown
Collaborator Author

@philprime As this is introducing new public API, how would you prefer we do the ObjC surface? Here in this PR or in a separate issue? We do have a separte issue to track this, I'm fine with either way.

Base automatically changed from feat/feature-flag-models-and-buffer to main June 23, 2026 09:52
# Conflicts:
#	Sources/Sentry/SentryScope.m
#	Sources/Swift/FeatureFlags/SentryFeatureFlagBufferWrapper.swift
#	Sources/Swift/Scope.swift
#	Tests/SentryTests/SentryScopeSwiftTests.swift

@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 cb258e2. Configure here.

Comment thread Sources/Sentry/SentryScope.m
Comment thread Sources/Sentry/SentryScope.m
@denrase denrase added the ready-to-merge Use this label to trigger all PR workflows label Jun 23, 2026
@sentry

sentry Bot commented Jun 23, 2026

Copy link
Copy Markdown

📲 Install Builds

iOS

🔗 App Name App ID Version Configuration
SDK-Size io.sentry.sample.SDK-Size 9.18.0 (1) Release

⚙️ sentry-cocoa Build Distribution Settings

Comment on lines +750 to +755
- (void)updateFeatureFlagsContext
{
// Must be called while synchronized on _contextDictionary.
NSDictionary<NSString *, id> *_Nullable featureFlags = [_featureFlagBuffer serializeForContext];
if (featureFlags.count > 0) {
_contextDictionary[@"flags"] = featureFlags;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Bug: The new feature flag mechanism uses the context key "flags". This can collide with user data if a developer uses the public setContextValue:forKey:@"flags" API, causing data loss.
Severity: MEDIUM

Suggested Fix

To prevent this collision, either document that the "flags" key is reserved and should not be used with setContextValue:forKey:, or implement a protection mechanism. For example, you could add input validation to setContextValue:forKey: to reject the "flags" key, or namespace the internal feature flag key (e.g., sentry_feature_flags) to avoid conflicts with user-defined keys.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.

Location: Sources/Sentry/SentryScope.m#L750-L755

Potential issue: The new feature flag implementation stores its data in the scope's
context under the key `"flags"`. However, the public API `setContextValue:forKey:`
allows users to set any context key, including `"flags"`. This creates a collision risk.
If a user calls `scope.setContext(value: myDict, key: "flags")`, it will overwrite the
feature flag data stored by the SDK. Conversely, subsequent feature flag updates will
overwrite the user's custom context. This can lead to data loss for either feature flags
or user-defined context, as the `"flags"` key is not documented as reserved or
protected.

Did we get this right? 👍 / 👎 to inform future reviews.

@github-actions

github-actions Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Performance metrics 🚀

  Plain With Sentry Diff
Startup time 1206.52 ms 1251.42 ms 44.89 ms
Size 24.14 KiB 1.20 MiB 1.18 MiB

Baseline results on branch: main

Startup times

Revision Plain With Sentry Diff
53097d6 1218.02 ms 1251.70 ms 33.68 ms
1c5ecda 1219.35 ms 1253.76 ms 34.41 ms
e0946cf 1233.33 ms 1258.57 ms 25.24 ms
8180609 1214.67 ms 1243.36 ms 28.69 ms
85ee155 1227.40 ms 1251.52 ms 24.12 ms
151b3ee 1238.69 ms 1270.73 ms 32.05 ms
379d045 1210.00 ms 1241.24 ms 31.24 ms
7814719 1220.98 ms 1254.14 ms 33.16 ms
1aa0a18 1225.33 ms 1255.65 ms 30.31 ms
4c437ea 1217.19 ms 1250.79 ms 33.60 ms

App size

Revision Plain With Sentry Diff
53097d6 24.14 KiB 1.16 MiB 1.13 MiB
1c5ecda 24.14 KiB 1.15 MiB 1.12 MiB
e0946cf 24.14 KiB 1.16 MiB 1.13 MiB
8180609 24.14 KiB 1.16 MiB 1.13 MiB
85ee155 24.14 KiB 1.16 MiB 1.13 MiB
151b3ee 24.14 KiB 1.15 MiB 1.13 MiB
379d045 24.14 KiB 1.18 MiB 1.15 MiB
7814719 24.14 KiB 1.15 MiB 1.13 MiB
1aa0a18 24.14 KiB 1.18 MiB 1.15 MiB
4c437ea 24.14 KiB 1.18 MiB 1.15 MiB

Previous results on branch: feat/feature-flag-scope-api

Startup times

Revision Plain With Sentry Diff
b0c666c 1221.48 ms 1255.76 ms 34.28 ms

App size

Revision Plain With Sentry Diff
b0c666c 24.14 KiB 1.20 MiB 1.17 MiB

@philprime philprime left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Good progress, couple of small comments.

Regarding your question in adding the ObjC wrapper API in this PR too: Yes please do that. If this pull requests is merged, and we do a release, the changelog will mention that new API is available, but it won't be for SentryObjC.

We should not see these two targets as different SDKs, but the drift must stay minimal

}
@synchronized(_contextDictionary) {
[_contextDictionary removeAllObjects];
[_featureFlagBuffer removeAll];

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

m: Shouldn't this be in a synchronized block for the _featureFlagBuffer instead?

Comment on lines +705 to +709
BOOL isRegularEvent
= event.type == nil || [event.type isEqualToString:SentryEnvelopeItemTypes.event];
if (!isRegularEvent) {
[newContext removeObjectForKey:@"flags"];
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

m: Please add a comment explaining the reasoning behind this. Also add a SDK log warning for users, so they understand why their flags context was removed

}
}

- (void)updateFeatureFlagsContext

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

m: I believe this method must synchronize feature flag buffer too, as there is no contract in place to block devs from calling it directly.

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

Labels

ready-to-merge Use this label to trigger all PR workflows

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Scope API and Event Payloads

2 participants