Skip to content

Commit

Permalink
feat: prov start pair 2
Browse files Browse the repository at this point in the history
  • Loading branch information
xnanodax committed Jan 24, 2025
1 parent 1271fee commit 2b84ef7
Show file tree
Hide file tree
Showing 3 changed files with 177 additions and 31 deletions.
60 changes: 50 additions & 10 deletions src/v3/ActiveTrace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export type TraceStates = NonTerminalTraceStates | TerminalTraceStates

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

interface OnEnterInterrupted {
Expand Down Expand Up @@ -263,9 +263,40 @@ export class TraceStateMachine<
onProcessSpan: (
spanAndAnnotation: SpanAndAnnotation<AllPossibleScopesT>,
) => {
// if any span is interrupted!
const spanEndTimeEpoch =
spanAndAnnotation.span.startTime.epoch +
spanAndAnnotation.span.duration

if (spanEndTimeEpoch > this.#timeoutDeadline) {
// we consider this interrupted, because of the clamping of the total duration of the operation
// as potential other events could have happened and prolonged the operation
// we can be a little picky, because we expect to record many operations
// it's best to compare like-to-like
return {
transitionToState: 'interrupted',
interruptionReason: 'timeout',
}
}

// if the entry matches any of the interruptOn criteria,
// transition to complete state with the 'matched-on-interrupt' interruptionReason
if (this.context.definition.interruptOn) {
for (const doesSpanMatch of this.context.definition.interruptOn) {
if (doesSpanMatch(spanAndAnnotation, this.context)) {
return {
transitionToState: 'complete',
interruptionReason: 'matched-on-interrupt',
lastRequiredSpanAndAnnotation: this.lastRequiredSpan,
completeSpanAndAnnotation: this.completeSpan,
}
}
}
}

// else, add into array buffer
this.#provisionalBuffer.push(spanAndAnnotation)

return undefined
},

onInterrupt: (reason: TraceInterruptionReason) => ({
Expand All @@ -284,11 +315,13 @@ export class TraceStateMachine<
return undefined
},
},

recording: {
// eslint-disable-next-line consistent-return
onEnterState: () => {
const transition = this.#processProvisionalBuffer()
if (transition) return transition
onEnterState: (_transition: OnEnterRecording) => {
const nextTransition = this.#processProvisionalBuffer()
if (nextTransition) return nextTransition

return undefined
},

onProcessSpan: (
Expand Down Expand Up @@ -377,7 +410,7 @@ export class TraceStateMachine<
// we want to ensure the end of the operation captures
// the final, settled state of the component
debouncing: {
onEnterState: (payload: OnEnterDebouncing) => {
onEnterState: (_payload: OnEnterDebouncing) => {
if (!this.lastRelevant) {
// this should never happen
return {
Expand Down Expand Up @@ -510,7 +543,7 @@ export class TraceStateMachine<
},

'waiting-for-interactive': {
onEnterState: (payload: OnEnterWaitingForInteractive) => {
onEnterState: (_payload: OnEnterWaitingForInteractive) => {
if (!this.lastRelevant) {
// this should never happen
return {
Expand Down Expand Up @@ -850,7 +883,13 @@ export class ActiveTrace<
AllPossibleScopesT,
const OriginatedFromT extends string,
> {
readonly definition: CompleteTraceDefinition<
readonly sourceDefinition: CompleteTraceDefinition<
TracerScopeKeysT,
AllPossibleScopesT,
OriginatedFromT
>
/** the mutable definition */
definition: CompleteTraceDefinition<
TracerScopeKeysT,
AllPossibleScopesT,
OriginatedFromT
Expand Down Expand Up @@ -895,7 +934,8 @@ export class ActiveTrace<
>,
deduplicationStrategy?: SpanDeduplicationStrategy<AllPossibleScopesT>,
) {
this.definition = definition
this.definition = structuredClone(definition)
this.sourceDefinition = definition
this.input = {
...input,
startTime: ensureTimestamp(input.startTime),
Expand Down
111 changes: 91 additions & 20 deletions src/v3/traceManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import type {
TraceContext,
TraceDefinition,
TraceManagerConfig,
TraceModifications,
Tracer,
} from './types'
import type { KeysOfUnion } from './typeUtils'
Expand All @@ -44,15 +45,19 @@ export class TraceManager<
| ActiveTrace<KeysOfUnion<AllPossibleScopesT>, AllPossibleScopesT, string>
| undefined = undefined

private reportErrorFn: (error: Error) => void

constructor({
reportFn,
reportErrorFn,
generateId,
performanceEntryDeduplicationStrategy,
}: TraceManagerConfig<AllPossibleScopesT, string>) {
this.reportFn = reportFn
this.generateId = generateId
this.performanceEntryDeduplicationStrategy =
performanceEntryDeduplicationStrategy
this.reportErrorFn = reportErrorFn
}

createTracer<
Expand Down Expand Up @@ -160,9 +165,10 @@ export class TraceManager<
} as ComputedValueDefinition<TracerScopeKeysT, AllPossibleScopesT, OriginatedFromT>)
},
start: (input) => this.startTrace(completeTraceDefinition, input),
provisionalStart: (inputWithScope) => {
this.provisionalStartTrace(completeTraceDefinition, inputWithScope)
},
provisionalStart: (input) =>
this.provisionalStartTrace(completeTraceDefinition, input),
initializeProvisional: (inputAndDefinitionModifications) =>
void this.initializeActiveTrace(inputAndDefinitionModifications),
}
}

Expand All @@ -185,34 +191,96 @@ export class TraceManager<
SelectScopeByKey<TracerScopeKeysT, AllPossibleScopesT>,
OriginatedFromT
>,
): string {
): string | undefined {
const traceId = this.provisionalStartTrace(definition, input)
this.initializeActiveTrace()
if (!traceId) return undefined
this.initializeActiveTrace({ scope: input.scope })
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:
private initializeActiveTrace<
TracerScopeKeysT extends KeysOfUnion<AllPossibleScopesT>,
OriginatedFromT extends string,
>(
inputAndDefinitionModifications: TraceModifications<
TracerScopeKeysT,
AllPossibleScopesT,
OriginatedFromT
>,
): void {
if (!this.activeTrace) {
this.reportErrorFn(
new Error(
`No currently active trace when initializing a trace. Call tracer.startTrace(...) or tracer.provisionalStartTrace(...) beforehand.`,
),
)
return
}

// this is an already initialized active trace, do nothing:
if (!this.activeTrace.isProvisional) {
this.reportErrorFn(
new Error(
`You are trying to initialize a trace that has already been initialized before (${this.activeTrace.definition.name}).`,
),
)
return
}

const { scope, attributes } = this.activeTrace.input

this.activeTrace.input.scope = scope
this.activeTrace.input.attributes = {
...this.activeTrace.input.attributes,
...attributes,
}

const additionalRequiredSpans = convertMatchersToFns<
TracerScopeKeysT,
AllPossibleScopesT,
OriginatedFromT
>(inputAndDefinitionModifications.additionalRequiredSpans)

const additionalDebounceOnSpans = convertMatchersToFns<
TracerScopeKeysT,
AllPossibleScopesT,
OriginatedFromT
>(inputAndDefinitionModifications.additionalDebounceOnSpans)

const { activeTrace } = this

if (additionalRequiredSpans?.length) {
activeTrace.definition.requiredSpans = [
...activeTrace.sourceDefinition.requiredSpans,
...additionalRequiredSpans,
] as (typeof activeTrace)['definition']['requiredSpans']
}
if (additionalDebounceOnSpans?.length) {
activeTrace.definition.debounceOn = [
...(activeTrace.sourceDefinition.debounceOn ?? []),
...additionalDebounceOnSpans,
] as (typeof activeTrace)['definition']['debounceOn']
}

// 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>,
const OriginatedFromT extends string,
>(
definition: CompleteTraceDefinition<TracerScopeKeysT, AllPossibleScopesT>,
input: BaseStartTraceConfig,
): string {
definition: CompleteTraceDefinition<
TracerScopeKeysT,
AllPossibleScopesT,
OriginatedFromT
>,
input: BaseStartTraceConfig<OriginatedFromT>,
): string | undefined {
if (this.activeTrace) {
this.activeTrace.interrupt('another-trace-started')
this.activeTrace = undefined
Expand All @@ -222,13 +290,16 @@ export class TraceManager<

// Verify that the originatedFrom value is valid and has a corresponding timeout
if (!(input.originatedFrom in definition.variantsByOriginatedFrom)) {
throw new Error(
`Invalid originatedFrom value: ${
input.originatedFrom
}. Must be one of: ${Object.keys(
definition.variantsByOriginatedFrom,
).join(', ')}`,
this.reportErrorFn(
new Error(
`Invalid originatedFrom value: ${
input.originatedFrom
}. Must be one of: ${Object.keys(
definition.variantsByOriginatedFrom,
).join(', ')}`,
),
)
return undefined
}

const activeTraceContext: TraceContext<
Expand Down
37 changes: 36 additions & 1 deletion src/v3/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import type { SpanMatch, SpanMatchDefinition, SpanMatcherFn } from './matchSpan'
import type { SpanAndAnnotation } from './spanAnnotationTypes'
import type {
ActiveTraceInput,
Attributes,
BaseStartTraceConfig,
Span,
SpanStatus,
StartTraceConfig,
Expand Down Expand Up @@ -90,6 +92,27 @@ export interface TraceManagerConfig<
performanceEntryDeduplicationStrategy?: SpanDeduplicationStrategy<
Partial<AllPossibleScopesT>
>

reportErrorFn: (error: Error) => void
}

export interface TraceModifications<
TracerScopeKeysT extends KeysOfUnion<AllPossibleScopesT>,
AllPossibleScopesT,
OriginatedFromT extends string,
> {
scope: SelectScopeByKey<TracerScopeKeysT, AllPossibleScopesT>
attributes?: Attributes
additionalRequiredSpans?: SpanMatcherFn<
TracerScopeKeysT,
AllPossibleScopesT,
OriginatedFromT
>[]
additionalDebounceOnSpans?: SpanMatcherFn<
TracerScopeKeysT,
AllPossibleScopesT,
OriginatedFromT
>[]
}

export interface Tracer<
Expand All @@ -105,7 +128,19 @@ export interface Tracer<
SelectScopeByKey<TracerScopeKeysT, AllPossibleScopesT>,
OriginatedFromT
>,
) => string
) => string | undefined

provisionalStart: (
input: BaseStartTraceConfig<OriginatedFromT>,
) => string | undefined

initializeProvisional: (
mods: TraceModifications<
TracerScopeKeysT,
AllPossibleScopesT,
OriginatedFromT
>,
) => void

defineComputedSpan: (
computedSpanDefinition: ComputedSpanDefinitionInput<
Expand Down

0 comments on commit 2b84ef7

Please sign in to comment.