Skip to content

feat(flags): add utils and flag context need for feature flag integrations #14194

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 24 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
26722d7
Init package files
aliu39 Oct 29, 2024
72f9cc0
Merge branch 'develop' into aliu/launch-darkly
aliu39 Oct 29, 2024
419cd83
Revert changelog and move types file
aliu39 Oct 29, 2024
610da4d
Add ld to dependencies and skeleton code. Get rid of core/
aliu39 Oct 29, 2024
5b2e5ec
Fix readme, rename types file, bring back core/
aliu39 Oct 29, 2024
66b1253
Fix readme 2
aliu39 Oct 30, 2024
023604a
Merge branch 'develop' into aliu/launch-darkly
aliu39 Oct 30, 2024
1f99c28
Merge branch 'aliu/launch-darkly' of https://github.com/getsentry/sen…
aliu39 Oct 30, 2024
85acc6d
Finish implementing, minus scope.flags
aliu39 Oct 30, 2024
3b3a767
Implement flag buffer in sentry scope
aliu39 Oct 30, 2024
ab3181d
Revert changelog
aliu39 Oct 30, 2024
22684ea
fix types
michellewzhang Oct 30, 2024
79e5b24
docstring
michellewzhang Oct 30, 2024
63649c5
Export FeatureFlag type in index.ts
aliu39 Oct 30, 2024
923500f
Merge branch 'aliu/launch-darkly' of https://github.com/getsentry/sen…
aliu39 Oct 30, 2024
bd04755
Clean up comments
aliu39 Oct 30, 2024
91a4db9
Fix build (uses yalc for scope changes) and use LRUMap util
aliu39 Oct 31, 2024
1849673
Merge branch 'develop' into aliu/launch-darkly
aliu39 Nov 5, 2024
1ca9d53
Tweak hook name and docs
aliu39 Nov 5, 2024
893cd62
FlagBuffer class and interface, add to scope._contexts. Remove old fl…
aliu39 Nov 6, 2024
f172683
Call clone in scope.clone()
aliu39 Nov 6, 2024
b96c17f
Rewrite as a util fx instead of class. Use in LD integration
aliu39 Nov 6, 2024
f9a659b
Delete LD pkg
aliu39 Nov 6, 2024
d9c5860
Delete LD from package.json and update lock
aliu39 Nov 6, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion packages/core/src/scope.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,13 @@ import type {
SeverityLevel,
User,
} from '@sentry/types';
import { dateTimestampInSeconds, generatePropagationContext, isPlainObject, logger, uuid4 } from '@sentry/utils';
import {
dateTimestampInSeconds,
generatePropagationContext,
isPlainObject,
logger,
uuid4,
} from '@sentry/utils';

import { updateSession } from './session';
import { _getSpanForScope, _setSpanForScope } from './utils/spanOnScope';
Expand Down Expand Up @@ -122,6 +128,13 @@ class ScopeClass implements ScopeInterface {
newScope._tags = { ...this._tags };
newScope._extra = { ...this._extra };
newScope._contexts = { ...this._contexts };
if (this._contexts.flags) {
// The flags context needs a deep copy.
newScope._contexts.flags = {
values: [...this._contexts.flags.values]
}
}

newScope._user = this._user;
newScope._level = this._level;
newScope._session = this._session;
Expand Down
7 changes: 7 additions & 0 deletions packages/types/src/context.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { Primitive } from './misc';
import type { SpanOrigin } from './span';
import type { FeatureFlag } from './flags'

export type Context = Record<string, unknown>;

Expand All @@ -13,6 +14,7 @@ export interface Contexts extends Record<string, Context | undefined> {
cloud_resource?: CloudResourceContext;
state?: StateContext;
profile?: ProfileContext;
flags?: FeatureFlagContext;
}

export interface StateContext extends Record<string, unknown> {
Expand Down Expand Up @@ -124,3 +126,8 @@ export interface MissingInstrumentationContext extends Record<string, unknown> {
package: string;
['javascript.is_cjs']?: boolean;
}

export interface FeatureFlagContext extends Record<string, unknown> {
// This should only be modified by @sentry/util methods (insertToFlagBuffer).
readonly values: FeatureFlag[];
}
2 changes: 2 additions & 0 deletions packages/types/src/flags.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// Key names match the type used by Sentry frontend.
export type FeatureFlag = { readonly flag: string; readonly result: boolean };
1 change: 1 addition & 0 deletions packages/types/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export type { Event, EventHint, EventType, ErrorEvent, TransactionEvent } from '
export type { EventProcessor } from './eventprocessor';
export type { Exception } from './exception';
export type { Extra, Extras } from './extra';
export type { FeatureFlag } from './flags';
// eslint-disable-next-line deprecation/deprecation
export type { Hub } from './hub';
export type { Integration, IntegrationClass, IntegrationFn } from './integration';
Expand Down
38 changes: 38 additions & 0 deletions packages/utils/src/flags.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import type { FeatureFlag } from '@sentry/types';

/**
* Ordered LRU cache for storing feature flags in the scope context. The name
* of each flag in the buffer is unique, and the output of getAll() is ordered
* from oldest to newest.
*/

export const FLAG_BUFFER_SIZE = 100;

/**
* Insert into a FeatureFlag array while maintaining ordered LRU properties.
* After inserting:
* - The flag is guaranteed to be at the end of `flags`.
* - No other flags with the same name exist in `flags`.
* - The length of `flags` does not exceed FLAG_BUFFER_SIZE. If needed, the
* oldest inserted flag is evicted.
*/
export function insertToFlagBuffer(flags: FeatureFlag[], name: string, value: boolean): void {
// Check if the flag is already in the buffer
const index = flags.findIndex(f => f.flag === name);

if (index !== -1) {
// The flag was found, remove it from its current position - O(n)
flags.splice(index, 1);
}

if (flags.length === FLAG_BUFFER_SIZE) {
// If at capacity, pop the earliest flag - O(n)
flags.shift();
}

// Push the flag to the end - O(1)
flags.push({
flag: name,
result: value,
});
}
1 change: 1 addition & 0 deletions packages/utils/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,4 @@ export * from './buildPolyfills';
export * from './propagationContext';
export * from './vercelWaitUntil';
export * from './version';
export * from './flags';
Loading