Skip to content

Commit

Permalink
feat: add initial prov start
Browse files Browse the repository at this point in the history
  • Loading branch information
xnanodax committed Jan 24, 2025
1 parent ec3ff16 commit 1271fee
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 10 deletions.
64 changes: 61 additions & 3 deletions src/v3/ActiveTrace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,20 @@ export interface FinalState<TracerScopeT> {
lastRequiredSpanAndAnnotation?: SpanAndAnnotation<TracerScopeT>
}

type InitialTraceState = 'recording'
type InitialTraceState = 'initial'
export type NonTerminalTraceStates =
| InitialTraceState
| 'recording'
| 'debouncing'
| 'waiting-for-interactive'
type TerminalTraceStates = 'interrupted' | 'complete'
export type TraceStates = NonTerminalTraceStates | TerminalTraceStates

interface OnEnterRecording {
transitionToState: 'recording'
transitionFromState: 'initial'
}

interface OnEnterInterrupted {
transitionToState: 'interrupted'
transitionFromState: NonTerminalTraceStates
Expand All @@ -71,6 +77,7 @@ interface OnEnterDebouncing {
}

type OnEnterStatePayload<AllPossibleScopesT> =
| OnEnterRecording
| OnEnterInterrupted
| OnEnterComplete<AllPossibleScopesT>
| OnEnterDebouncing
Expand Down Expand Up @@ -150,7 +157,7 @@ export class TraceStateMachine<
options: PrepareAndEmitRecordingOptions<AllPossibleScopesT>,
) => void
}
currentState: TraceStates = 'recording'
currentState: TraceStates = 'initial'
/** the span that ended at the furthest point in time */
lastRelevant: SpanAndAnnotation<AllPossibleScopesT> | undefined
lastRequiredSpan: SpanAndAnnotation<AllPossibleScopesT> | undefined
Expand Down Expand Up @@ -226,9 +233,21 @@ export class TraceStateMachine<
* if we have long tasks before FMP, we want to use them as a potential grouping post FMP.
*/
debouncingSpanBuffer: SpanAndAnnotation<AllPossibleScopesT>[] = []
#provisionalBuffer: SpanAndAnnotation<AllPossibleScopesT>[] = []

// eslint-disable-next-line consistent-return
#processProvisionalBuffer(): OnEnterStatePayload<AllPossibleScopesT> | void {
// process items in the buffer (stick the scope in the entries) (if its empty, well we can skip this!)
let span: SpanAndAnnotation<AllPossibleScopesT> | undefined
// eslint-disable-next-line no-cond-assign
while ((span = this.#provisionalBuffer.shift())) {
const transition = this.emit('onProcessSpan', span)
if (transition) return transition
}
}

readonly states = {
recording: {
initial: {
onEnterState: () => {
this.setGlobalDeadline(
this.context.input.startTime.epoch +
Expand All @@ -237,6 +256,40 @@ export class TraceStateMachine<
]!.timeoutDuration,
)
},
// onInitializeStart: () => ({
// transitionToState: 'recording',
// }),

onProcessSpan: (
spanAndAnnotation: SpanAndAnnotation<AllPossibleScopesT>,
) => {
// if any span is interrupted!
// else, add into array buffer
this.#provisionalBuffer.push(spanAndAnnotation)
},

onInterrupt: (reason: TraceInterruptionReason) => ({
transitionToState: 'interrupted',
interruptionReason: reason,
}),

onDeadline: (deadlineType: DeadlineType) => {
if (deadlineType === 'global') {
return {
transitionToState: 'interrupted',
interruptionReason: 'timeout',
}
}
// other cases should never happen
return undefined
},
},
recording: {
// eslint-disable-next-line consistent-return
onEnterState: () => {
const transition = this.#processProvisionalBuffer()
if (transition) return transition
},

onProcessSpan: (
spanAndAnnotation: SpanAndAnnotation<AllPossibleScopesT>,
Expand Down Expand Up @@ -774,6 +827,7 @@ export class TraceStateMachine<
if (transitionPayload) {
const transitionFromState = this.currentState as NonTerminalTraceStates
this.currentState = transitionPayload.transitionToState
// PROV START TODO: update types
const onEnterStateEvent: OnEnterStatePayload<AllPossibleScopesT> = {
transitionFromState,
...transitionPayload,
Expand Down Expand Up @@ -808,6 +862,10 @@ export class ActiveTrace<
>
private readonly deduplicationStrategy?: SpanDeduplicationStrategy<AllPossibleScopesT>

get isProvisional() {
return this.stateMachine.currentState === 'initial'
}

recordedItems: Set<SpanAndAnnotation<AllPossibleScopesT>> = new Set()
stateMachine: TraceStateMachine<
TracerScopeKeysT,
Expand Down
20 changes: 14 additions & 6 deletions src/v3/spanTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,8 @@ export type ComponentLifecycleSpanType =

export type SpanType = NativePerformanceEntryType | ComponentLifecycleSpanType

export interface StartTraceConfig<
TracerScopeT,
OriginatedFromT extends string,
> {
export interface BaseStartTraceConfig<OriginatedFromT extends string> {
id?: string
scope: TracerScopeT
startTime?: Partial<Timestamp>
originatedFrom: OriginatedFromT
/**
Expand All @@ -45,8 +41,20 @@ export interface StartTraceConfig<
attributes?: Attributes
}

export interface StartTraceConfigWithOptionalScope<
TracerScopeT,
OriginatedFromT extends string,
> extends BaseStartTraceConfig<OriginatedFromT> {
scope: TracerScopeT | undefined
}

export interface StartTraceConfig<TracerScopeT, OriginatedFromT extends string>
extends BaseStartTraceConfig<OriginatedFromT> {
scope: TracerScopeT
}

export interface ActiveTraceInput<TracerScopeT, OriginatedFromT extends string>
extends StartTraceConfig<TracerScopeT, OriginatedFromT> {
extends StartTraceConfigWithOptionalScope<TracerScopeT, OriginatedFromT> {
id: string
startTime: Timestamp
}
Expand Down
34 changes: 33 additions & 1 deletion src/v3/traceManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
import { ensureTimestamp } from './ensureTimestamp'
import { type SpanMatcherFn } from './matchSpan'
import type { SpanAnnotationRecord } from './spanAnnotationTypes'
import type { Span, StartTraceConfig } from './spanTypes'
import type { BaseStartTraceConfig, Span, StartTraceConfig } from './spanTypes'
import type { TraceRecording } from './traceRecordingTypes'
import type {
CompleteTraceDefinition,
Expand Down Expand Up @@ -160,6 +160,9 @@ export class TraceManager<
} as ComputedValueDefinition<TracerScopeKeysT, AllPossibleScopesT, OriginatedFromT>)
},
start: (input) => this.startTrace(completeTraceDefinition, input),
provisionalStart: (inputWithScope) => {
this.provisionalStartTrace(completeTraceDefinition, inputWithScope)
},
}
}

Expand All @@ -182,6 +185,33 @@ export class TraceManager<
SelectScopeByKey<TracerScopeKeysT, AllPossibleScopesT>,
OriginatedFromT
>,
): string {
const traceId = this.provisionalStartTrace(definition, input)
this.initializeActiveTrace()
return traceId
}

// can have config changed until we move into recording
// from input: scope (required), attributes (optional, merge into)
// from definition, can add items to: requiredSpans (additionalRequiredSpans), debounceOn (additionalDebounceOnSpans)
// documentation: interruption still works and all the other events are buffered
private initializeActiveTrace(inputAndDefinitionModifications) {
const currentTrace = this.activeTrace
// if a trace has an undefined scope, it means it
if (!currentTrace?.isProvisional) {
// this is an already initialized active trace, do nothing:
return
}
// else, we want to initialize the trace with the scope and other modifications:
// TODO ...
}

// todo: wont have scope yet
private provisionalStartTrace<
const TracerScopeKeysT extends KeysOfUnion<AllPossibleScopesT>,
>(
definition: CompleteTraceDefinition<TracerScopeKeysT, AllPossibleScopesT>,
input: BaseStartTraceConfig,
): string {
if (this.activeTrace) {
this.activeTrace.interrupt('another-trace-started')
Expand Down Expand Up @@ -209,6 +239,8 @@ export class TraceManager<
definition,
input: {
...input,
// scope will be overwritten later during initialization of the trace
scope: undefined,
startTime: ensureTimestamp(input.startTime),
id,
},
Expand Down

0 comments on commit 1271fee

Please sign in to comment.