@cratis/chronicle 6.32.1 → 6.34.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ChronicleClient.ts +35 -33
- package/ChronicleOptions.ts +27 -1
- package/EventStore.ts +14 -12
- package/Metrics.ts +70 -96
- package/Tracing.ts +4 -2
- package/WellKnownTelemetryNames.ts +67 -0
- package/artifacts/validateArtifactSchemas.spec.ts +1 -1
- package/artifacts/withActivatedArtifact.ts +7 -5
- package/connection/ChronicleConnection.ts +18 -6
- package/connection/CompatibilityPreflight.ts +2 -1
- package/connection/KernelKeepAlive.ts +13 -7
- package/connection/TokenProvider.ts +3 -1
- package/connection/TraceContextMiddleware.ts +30 -0
- package/connection/for_ChronicleConnection/when_creating_clients/with_correlated_multi_host_callers.spec.ts +73 -0
- package/connection/for_OAuthTokenProvider/when_acquiring_tokens/with_http_instrumentation.spec.ts +102 -0
- package/connection/for_TraceContextMiddleware/when_calling/with_active_context.spec.ts +235 -0
- package/connection/for_TraceContextMiddleware/when_calling/with_grpc_instrumentation.spec.ts +102 -0
- package/correlation/CorrelationIdManager.ts +10 -0
- package/dist/ChronicleClient.d.ts.map +1 -1
- package/dist/ChronicleClient.js +35 -33
- package/dist/ChronicleClient.js.map +1 -1
- package/dist/ChronicleOptions.d.ts +8 -0
- package/dist/ChronicleOptions.d.ts.map +1 -1
- package/dist/ChronicleOptions.js +19 -1
- package/dist/ChronicleOptions.js.map +1 -1
- package/dist/EventStore.d.ts +3 -1
- package/dist/EventStore.d.ts.map +1 -1
- package/dist/EventStore.js +14 -12
- package/dist/EventStore.js.map +1 -1
- package/dist/Metrics.d.ts +23 -64
- package/dist/Metrics.d.ts.map +1 -1
- package/dist/Metrics.js +62 -92
- package/dist/Metrics.js.map +1 -1
- package/dist/Tracing.d.ts +1 -1
- package/dist/Tracing.d.ts.map +1 -1
- package/dist/Tracing.js +4 -2
- package/dist/Tracing.js.map +1 -1
- package/dist/WellKnownTelemetryNames.d.ts +65 -0
- package/dist/WellKnownTelemetryNames.d.ts.map +1 -0
- package/dist/WellKnownTelemetryNames.js +67 -0
- package/dist/WellKnownTelemetryNames.js.map +1 -0
- package/dist/artifacts/withActivatedArtifact.d.ts +2 -1
- package/dist/artifacts/withActivatedArtifact.d.ts.map +1 -1
- package/dist/artifacts/withActivatedArtifact.js +5 -4
- package/dist/artifacts/withActivatedArtifact.js.map +1 -1
- package/dist/connection/ChronicleConnection.d.ts +5 -0
- package/dist/connection/ChronicleConnection.d.ts.map +1 -1
- package/dist/connection/ChronicleConnection.js +13 -6
- package/dist/connection/ChronicleConnection.js.map +1 -1
- package/dist/connection/CompatibilityPreflight.d.ts.map +1 -1
- package/dist/connection/CompatibilityPreflight.js +2 -1
- package/dist/connection/CompatibilityPreflight.js.map +1 -1
- package/dist/connection/KernelKeepAlive.d.ts +3 -1
- package/dist/connection/KernelKeepAlive.d.ts.map +1 -1
- package/dist/connection/KernelKeepAlive.js +10 -7
- package/dist/connection/KernelKeepAlive.js.map +1 -1
- package/dist/connection/TokenProvider.d.ts.map +1 -1
- package/dist/connection/TokenProvider.js +3 -1
- package/dist/connection/TokenProvider.js.map +1 -1
- package/dist/connection/TraceContextMiddleware.d.ts +4 -0
- package/dist/connection/TraceContextMiddleware.d.ts.map +1 -0
- package/dist/connection/TraceContextMiddleware.js +30 -0
- package/dist/connection/TraceContextMiddleware.js.map +1 -0
- package/dist/correlation/CorrelationIdManager.d.ts +4 -0
- package/dist/correlation/CorrelationIdManager.d.ts.map +1 -1
- package/dist/correlation/CorrelationIdManager.js +8 -0
- package/dist/correlation/CorrelationIdManager.js.map +1 -1
- package/dist/eventSequences/EventLog.d.ts +2 -1
- package/dist/eventSequences/EventLog.d.ts.map +1 -1
- package/dist/eventSequences/EventLog.js +2 -2
- package/dist/eventSequences/EventLog.js.map +1 -1
- package/dist/eventSequences/EventSequence.d.ts +4 -1
- package/dist/eventSequences/EventSequence.d.ts.map +1 -1
- package/dist/eventSequences/EventSequence.js +63 -80
- package/dist/eventSequences/EventSequence.js.map +1 -1
- package/dist/eventStoreSubscriptions/EventStoreSubscriptions.d.ts.map +1 -1
- package/dist/eventStoreSubscriptions/EventStoreSubscriptions.js +3 -4
- package/dist/eventStoreSubscriptions/EventStoreSubscriptions.js.map +1 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/logging/ChronicleLogEntry.d.ts +11 -0
- package/dist/logging/ChronicleLogEntry.d.ts.map +1 -0
- package/dist/logging/ChronicleLogEntry.js +4 -0
- package/dist/logging/ChronicleLogEntry.js.map +1 -0
- package/dist/logging/ChronicleLogLevel.d.ts +9 -0
- package/dist/logging/ChronicleLogLevel.d.ts.map +1 -0
- package/dist/logging/ChronicleLogLevel.js +12 -0
- package/dist/logging/ChronicleLogLevel.js.map +1 -0
- package/dist/logging/DiagChronicleLogger.d.ts +7 -0
- package/dist/logging/DiagChronicleLogger.d.ts.map +1 -0
- package/dist/logging/DiagChronicleLogger.js +10 -0
- package/dist/logging/DiagChronicleLogger.js.map +1 -0
- package/dist/logging/IChronicleLogger.d.ts +7 -0
- package/dist/logging/IChronicleLogger.d.ts.map +1 -0
- package/dist/logging/IChronicleLogger.js +4 -0
- package/dist/logging/IChronicleLogger.js.map +1 -0
- package/dist/logging/createLogger.d.ts +10 -0
- package/dist/logging/createLogger.d.ts.map +1 -0
- package/dist/logging/createLogger.js +55 -0
- package/dist/logging/createLogger.js.map +1 -0
- package/dist/projections/Projections.d.ts.map +1 -1
- package/dist/projections/Projections.js +4 -3
- package/dist/projections/Projections.js.map +1 -1
- package/dist/reactors/Reactors.d.ts.map +1 -1
- package/dist/reactors/Reactors.js +26 -24
- package/dist/reactors/Reactors.js.map +1 -1
- package/dist/readModels/IReadModels.d.ts +14 -0
- package/dist/readModels/IReadModels.d.ts.map +1 -1
- package/dist/readModels/ReadModels.d.ts +6 -0
- package/dist/readModels/ReadModels.d.ts.map +1 -1
- package/dist/readModels/ReadModels.js +110 -0
- package/dist/readModels/ReadModels.js.map +1 -1
- package/dist/readModels/for_ReadModels/given/a_stored_read_model.d.ts +20 -0
- package/dist/readModels/for_ReadModels/given/a_stored_read_model.d.ts.map +1 -0
- package/dist/readModels/for_ReadModels/given/a_stored_read_model.js +79 -0
- package/dist/readModels/for_ReadModels/given/a_stored_read_model.js.map +1 -0
- package/dist/reducers/Reducers.d.ts.map +1 -1
- package/dist/reducers/Reducers.js +28 -25
- package/dist/reducers/Reducers.js.map +1 -1
- package/dist/seeding/EventSeeding.d.ts.map +1 -1
- package/dist/seeding/EventSeeding.js +4 -3
- package/dist/seeding/EventSeeding.js.map +1 -1
- package/dist/telemetry/ChronicleTelemetryOptions.d.ts +11 -0
- package/dist/telemetry/ChronicleTelemetryOptions.d.ts.map +1 -0
- package/dist/telemetry/ChronicleTelemetryOptions.js +4 -0
- package/dist/telemetry/ChronicleTelemetryOptions.js.map +1 -0
- package/dist/telemetry/observeOperation.d.ts +6 -0
- package/dist/telemetry/observeOperation.d.ts.map +1 -0
- package/dist/telemetry/observeOperation.js +23 -0
- package/dist/telemetry/observeOperation.js.map +1 -0
- package/dist/telemetry/runInBackgroundContext.d.ts +3 -0
- package/dist/telemetry/runInBackgroundContext.d.ts.map +1 -0
- package/dist/telemetry/runInBackgroundContext.js +9 -0
- package/dist/telemetry/runInBackgroundContext.js.map +1 -0
- package/dist/telemetry/spanAttributes.d.ts +14 -0
- package/dist/telemetry/spanAttributes.d.ts.map +1 -0
- package/dist/telemetry/spanAttributes.js +47 -0
- package/dist/telemetry/spanAttributes.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/eventSequences/EventLog.ts +4 -2
- package/eventSequences/EventSequence.ts +64 -81
- package/eventStoreSubscriptions/EventStoreSubscriptions.ts +5 -5
- package/for_ChronicleClient/when_starting_background_work/with_a_correlated_caller.spec.ts +150 -0
- package/for_ChronicleOptions/when_configuring_telemetry/with_event_source_identifiers.spec.ts +44 -0
- package/index.ts +7 -1
- package/logging/ChronicleLogEntry.ts +14 -0
- package/logging/ChronicleLogLevel.ts +11 -0
- package/logging/DiagChronicleLogger.ts +13 -0
- package/logging/IChronicleLogger.ts +10 -0
- package/logging/createLogger.ts +50 -0
- package/logging/for_ChronicleLogger/when_logging/with_client_owned_sinks.spec.ts +96 -0
- package/package.json +7 -1
- package/projections/Projections.ts +4 -3
- package/reactors/Reactors.ts +27 -25
- package/reactors/for_Reactors/when_delivering/with_activation.spec.ts +14 -14
- package/readModels/IReadModels.ts +15 -0
- package/readModels/ReadModels.ts +118 -1
- package/readModels/for_ReadModels/given/a_stored_read_model.ts +62 -0
- package/readModels/for_ReadModels/when_releasing_a_document/with_a_default_subject.spec.ts +39 -0
- package/readModels/for_ReadModels/when_releasing_a_document/with_a_frozen_document.spec.ts +28 -0
- package/readModels/for_ReadModels/when_releasing_a_document/with_a_malformed_response.spec.ts +24 -0
- package/readModels/for_ReadModels/when_releasing_a_document/with_a_reported_failure.spec.ts +38 -0
- package/readModels/for_ReadModels/when_releasing_a_document/with_an_empty_released_value.spec.ts +19 -0
- package/readModels/for_ReadModels/when_releasing_a_document/with_an_unknown_subject_property.spec.ts +42 -0
- package/readModels/for_ReadModels/when_releasing_a_document/with_bookkeeping_fields.spec.ts +27 -0
- package/readModels/for_ReadModels/when_releasing_a_document/with_conflicting_or_missing_properties.spec.ts +32 -0
- package/readModels/for_ReadModels/when_releasing_a_document/with_empty_subject_metadata.spec.ts +56 -0
- package/readModels/for_ReadModels/when_releasing_a_document/with_invalid_subject_metadata.spec.ts +27 -0
- package/readModels/for_ReadModels/when_releasing_a_document/with_mixed_subjects.spec.ts +38 -0
- package/readModels/for_ReadModels/when_releasing_a_document/with_property_subjects.spec.ts +52 -0
- package/readModels/for_ReadModels/when_releasing_a_document/with_whitespace_subjects.spec.ts +24 -0
- package/readModels/for_ReadModels/when_releasing_a_document/without_subjects.spec.ts +24 -0
- package/reducers/Reducers.ts +29 -26
- package/seeding/EventSeeding.ts +6 -4
- package/telemetry/ChronicleTelemetryOptions.ts +8 -0
- package/telemetry/for_ChronicleTelemetry/given/a_telemetry_session.fixture.ts +36 -0
- package/telemetry/for_ChronicleTelemetry/given/an_event_sequence.fixture.ts +29 -0
- package/telemetry/for_ChronicleTelemetry/when_appending/with_shared_and_legacy_attributes.spec.ts +58 -0
- package/telemetry/for_ChronicleTelemetry/when_appending/without_an_sdk.spec.ts +15 -0
- package/telemetry/for_ChronicleTelemetry/when_documenting/with_public_names.spec.ts +15 -0
- package/telemetry/for_ChronicleTelemetry/when_recording_failures/with_sensitive_exception_details.spec.ts +34 -0
- package/telemetry/for_ChronicleTelemetry/when_recording_identifiers/with_per_client_privacy.spec.ts +92 -0
- package/telemetry/for_ChronicleTelemetry/when_recording_metrics/with_legacy_overlap.spec.ts +40 -0
- package/telemetry/for_ChronicleTelemetry/when_recording_sequence_numbers/with_integer_precision.spec.ts +23 -0
- package/telemetry/observeOperation.ts +25 -0
- package/telemetry/runInBackgroundContext.ts +10 -0
- package/telemetry/spanAttributes.ts +49 -0
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
import { afterEach, beforeEach, chai, describe, it, vi } from 'vitest';
|
|
5
|
+
import { context, propagation, trace } from '@opentelemetry/api';
|
|
6
|
+
import { createServer, ServerError, Status, type ServiceImplementation } from 'nice-grpc';
|
|
7
|
+
import { Metadata } from 'nice-grpc-common';
|
|
8
|
+
import { ConnectionServiceDefinition, EventSequencesDefinition, EventStoresDefinition, ReactorsDefinition } from '@cratis/chronicle.contracts';
|
|
9
|
+
import { ChronicleConnection } from '../../ChronicleConnection.js';
|
|
10
|
+
import { OAuthTokenProvider } from '../../TokenProvider.js';
|
|
11
|
+
import { CorrelationId, correlationIdManager } from '../../../correlation/index.js';
|
|
12
|
+
import { telemetrySession } from '../../../telemetry/for_ChronicleTelemetry/given/a_telemetry_session.fixture.js';
|
|
13
|
+
|
|
14
|
+
const should = chai.should();
|
|
15
|
+
const telemetry = telemetrySession();
|
|
16
|
+
const correlation = 'f5a978af-cd69-4a98-9f1e-7f656cc8ba2a';
|
|
17
|
+
let server: ReturnType<typeof createServer>;
|
|
18
|
+
let connection: ChronicleConnection;
|
|
19
|
+
let received: Metadata[];
|
|
20
|
+
let original: Metadata;
|
|
21
|
+
let traceparent: string;
|
|
22
|
+
let address: string;
|
|
23
|
+
let rejectNext: boolean;
|
|
24
|
+
|
|
25
|
+
beforeEach(async () => {
|
|
26
|
+
received = [];
|
|
27
|
+
rejectNext = false;
|
|
28
|
+
server = createServer().use(async function* (call, callContext) {
|
|
29
|
+
received.push(Metadata(callContext.metadata));
|
|
30
|
+
return yield* call.next(call.request, callContext);
|
|
31
|
+
});
|
|
32
|
+
const unused = async () => { throw new Error('Unexpected RPC'); };
|
|
33
|
+
server.add(ConnectionServiceDefinition, {
|
|
34
|
+
...Object.fromEntries(Object.keys(ConnectionServiceDefinition.methods).map(name => [name, unused])),
|
|
35
|
+
checkCompatibility: async () => ({ IsCompatible: true, Incompatibilities: [], ServerVersion: 'test' }),
|
|
36
|
+
connectionKeepAlive: async () => ({})
|
|
37
|
+
} as ServiceImplementation<typeof ConnectionServiceDefinition>);
|
|
38
|
+
server.add(EventStoresDefinition, {
|
|
39
|
+
allEventStores: async () => {
|
|
40
|
+
if (rejectNext) {
|
|
41
|
+
rejectNext = false;
|
|
42
|
+
throw new ServerError(Status.UNAUTHENTICATED, 'Refresh required');
|
|
43
|
+
}
|
|
44
|
+
return { IsAuthorized: true, Data: [] };
|
|
45
|
+
},
|
|
46
|
+
ensureEventStore: unused,
|
|
47
|
+
observeEventStores: async function* () { yield { IsAuthorized: true, Data: [] }; }
|
|
48
|
+
} as ServiceImplementation<typeof EventStoresDefinition>);
|
|
49
|
+
server.add(EventSequencesDefinition, {
|
|
50
|
+
...Object.fromEntries(Object.keys(EventSequencesDefinition.methods).map(name => [name, unused])),
|
|
51
|
+
append: async () => ({ Response: { SequenceNumber: 0n } })
|
|
52
|
+
} as ServiceImplementation<typeof EventSequencesDefinition>);
|
|
53
|
+
server.add(ReactorsDefinition, {
|
|
54
|
+
...Object.fromEntries(Object.keys(ReactorsDefinition.methods).map(name => [name, unused])),
|
|
55
|
+
observe: async function* (requests) {
|
|
56
|
+
for await (const _request of requests) {
|
|
57
|
+
yield { Events: [], Partition: '', ReplayState: 0 };
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
} as ServiceImplementation<typeof ReactorsDefinition>);
|
|
62
|
+
const port = await server.listen('127.0.0.1:0');
|
|
63
|
+
address = `chronicle://127.0.0.1:${port}?disableTls=true`;
|
|
64
|
+
connection = new ChronicleConnection({ connectionString: `${address}&apiKey=api-secret` });
|
|
65
|
+
original = Metadata({ authorization: 'Bearer caller-token', 'x-custom': 'preserved',
|
|
66
|
+
'custom-bin': new Uint8Array([1, 2, 3]), baggage: 'email=private', traceparent: 'stale' });
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
afterEach(async () => {
|
|
70
|
+
connection?.dispose();
|
|
71
|
+
await server?.shutdown();
|
|
72
|
+
vi.restoreAllMocks();
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
async function inContext(action: (parent: string) => Promise<void>, correlationValue = correlation) {
|
|
76
|
+
const span = telemetry.traces.getTracer('host').startSpan('host');
|
|
77
|
+
const spanContext = span.spanContext();
|
|
78
|
+
const parent = `00-${spanContext.traceId}-${spanContext.spanId}-01`;
|
|
79
|
+
traceparent = parent;
|
|
80
|
+
const active = propagation.setBaggage(trace.setSpan(context.active(), span), propagation.createBaggage({
|
|
81
|
+
'cratis.correlation_id': { value: correlationValue }, email: { value: 'private@example.com' },
|
|
82
|
+
'cratis.event_source.id': { value: 'private-source' }, 'cratis.tenant.id': { value: 'private-tenant' }
|
|
83
|
+
}));
|
|
84
|
+
try {
|
|
85
|
+
await context.with(active, async () => {
|
|
86
|
+
await Promise.resolve();
|
|
87
|
+
await action(parent);
|
|
88
|
+
propagation.getBaggage(context.active())!.getEntry('email')!.value.should.equal('private@example.com');
|
|
89
|
+
});
|
|
90
|
+
} finally { span.end(); }
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
describe('when calling unary RPCs with active trace context and sensitive baggage', () => {
|
|
94
|
+
beforeEach(async () => {
|
|
95
|
+
await inContext(async () => {
|
|
96
|
+
await connection.connect();
|
|
97
|
+
await connection.eventStores.allEventStores({}, { metadata: original });
|
|
98
|
+
await connection.connections.connectionKeepAlive({ ConnectionId: 'test' });
|
|
99
|
+
await connection.reconnect();
|
|
100
|
+
await connection.eventStores.allEventStores({}, { metadata: original });
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
it('should detach connect and reconnect compatibility checks from caller context', () => {
|
|
104
|
+
received.should.have.lengthOf(5);
|
|
105
|
+
for (const index of [0, 3]) {
|
|
106
|
+
should.equal(received[index].get('traceparent'), undefined);
|
|
107
|
+
should.equal(received[index].get('baggage'), undefined);
|
|
108
|
+
should.equal(received[index].get('api-key'), 'api-secret');
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
it('should inject trace context and only correlation baggage on business calls and keep-alive', () => {
|
|
112
|
+
for (const index of [1, 2, 4]) {
|
|
113
|
+
should.equal(received[index].get('traceparent'), traceparent);
|
|
114
|
+
should.equal(received[index].get('baggage'), `cratis.correlation_id=${correlation}`);
|
|
115
|
+
should.equal(received[index].get('api-key'), 'api-secret');
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
it('should preserve authentication and unrelated string and binary metadata', () => {
|
|
119
|
+
const metadata = received[1];
|
|
120
|
+
should.equal(metadata.get('authorization'), 'Bearer caller-token');
|
|
121
|
+
should.equal(metadata.get('x-custom'), 'preserved');
|
|
122
|
+
Array.from(metadata.get('custom-bin')!).should.deep.equal([1, 2, 3]);
|
|
123
|
+
});
|
|
124
|
+
it('should not mutate the caller carrier', () => {
|
|
125
|
+
should.equal(original.get('traceparent'), 'stale');
|
|
126
|
+
should.equal(original.get('baggage'), 'email=private');
|
|
127
|
+
should.equal(original.get('api-key'), undefined);
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
describe('when calling an event sequence without a cached compatibility verdict', () => {
|
|
132
|
+
beforeEach(async () => {
|
|
133
|
+
await connection.resetChannel();
|
|
134
|
+
await correlationIdManager.run(new CorrelationId(correlation), () => inContext(async () => {
|
|
135
|
+
await connection.eventSequences.append({}, { metadata: original });
|
|
136
|
+
correlationIdManager.scoped!.toString().should.equal(correlation);
|
|
137
|
+
}));
|
|
138
|
+
});
|
|
139
|
+
it('should detach the shared compatibility check while preserving the business caller context', () => {
|
|
140
|
+
received.should.have.lengthOf(2);
|
|
141
|
+
should.equal(received[0].get('traceparent'), undefined);
|
|
142
|
+
should.equal(received[0].get('baggage'), undefined);
|
|
143
|
+
should.equal(received[1].get('traceparent'), traceparent);
|
|
144
|
+
should.equal(received[1].get('baggage'), `cratis.correlation_id=${correlation}`);
|
|
145
|
+
should.equal(received[1].get('authorization'), 'Bearer caller-token');
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
describe('when opening server and bidirectional streams', () => {
|
|
150
|
+
beforeEach(async () => {
|
|
151
|
+
await connection.connect();
|
|
152
|
+
received.length = 0;
|
|
153
|
+
await inContext(async () => {
|
|
154
|
+
for await (const _response of connection.eventStores.observeEventStores({}, { metadata: original })) { break; }
|
|
155
|
+
async function* requests() { yield { Content: undefined }; }
|
|
156
|
+
for await (const _response of connection.reactors.observe(requests(), { metadata: original })) { break; }
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
it('should propagate the stream-opening context and preserve authentication', () => {
|
|
160
|
+
received.should.have.lengthOf(2);
|
|
161
|
+
for (const metadata of received) {
|
|
162
|
+
should.equal(metadata.get('traceparent'), traceparent);
|
|
163
|
+
should.equal(metadata.get('baggage'), `cratis.correlation_id=${correlation}`);
|
|
164
|
+
should.equal(metadata.get('authorization'), 'Bearer caller-token');
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
describe('when authentication refresh retries a unary call', () => {
|
|
170
|
+
beforeEach(async () => {
|
|
171
|
+
connection.dispose();
|
|
172
|
+
vi.spyOn(OAuthTokenProvider.prototype, 'getAccessToken').mockResolvedValue('old-token');
|
|
173
|
+
vi.spyOn(OAuthTokenProvider.prototype, 'refresh').mockResolvedValue('refreshed-token');
|
|
174
|
+
connection = new ChronicleConnection({ connectionString: address });
|
|
175
|
+
await connection.connect();
|
|
176
|
+
received.length = 0;
|
|
177
|
+
rejectNext = true;
|
|
178
|
+
await inContext(async () => {
|
|
179
|
+
await connection.eventStores.allEventStores({}, { metadata: original });
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
it('should keep trace context and filtered baggage on both attempts with the refreshed authorization', () => {
|
|
183
|
+
received.should.have.lengthOf(2);
|
|
184
|
+
for (const metadata of received) {
|
|
185
|
+
should.equal(metadata.get('traceparent'), traceparent);
|
|
186
|
+
should.equal(metadata.get('baggage'), `cratis.correlation_id=${correlation}`);
|
|
187
|
+
}
|
|
188
|
+
should.equal(received[0].get('authorization'), 'Bearer old-token');
|
|
189
|
+
should.equal(received[1].get('authorization'), 'Bearer refreshed-token');
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
describe('when callers in two contexts use the same connection concurrently', () => {
|
|
194
|
+
const expected = new Map<string, { parent: string; correlationId: string }>();
|
|
195
|
+
beforeEach(async () => {
|
|
196
|
+
await connection.connect();
|
|
197
|
+
received.length = 0;
|
|
198
|
+
expected.clear();
|
|
199
|
+
const correlations = [correlation, '7144abf3-94bd-4429-93e5-3faf23f0bad1'];
|
|
200
|
+
let ready = 0;
|
|
201
|
+
let release!: () => void;
|
|
202
|
+
const bothReady = new Promise<void>(resolve => { release = resolve; });
|
|
203
|
+
await Promise.all(correlations.map((correlationId, index) =>
|
|
204
|
+
correlationIdManager.run(new CorrelationId(correlationId), () => inContext(async parent => {
|
|
205
|
+
const id = index.toString();
|
|
206
|
+
expected.set(id, { parent, correlationId });
|
|
207
|
+
if (++ready === 2) release();
|
|
208
|
+
await bothReady;
|
|
209
|
+
await connection.eventStores.allEventStores({}, { metadata: Metadata({ 'x-call': id }) });
|
|
210
|
+
correlationIdManager.scoped!.toString().should.equal(correlationId);
|
|
211
|
+
}, correlationId))));
|
|
212
|
+
});
|
|
213
|
+
it('should propagate each caller trace and business correlation without cross-contamination', () => {
|
|
214
|
+
received.should.have.lengthOf(2);
|
|
215
|
+
for (const metadata of received) {
|
|
216
|
+
const caller = expected.get(metadata.get('x-call')!)!;
|
|
217
|
+
should.equal(metadata.get('traceparent'), caller.parent);
|
|
218
|
+
should.equal(metadata.get('baggage'), `cratis.correlation_id=${caller.correlationId}`);
|
|
219
|
+
}
|
|
220
|
+
new Set(received.map(metadata => metadata.get('traceparent'))).size.should.equal(2);
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
describe('when calling without an active span or baggage', () => {
|
|
225
|
+
beforeEach(async () => {
|
|
226
|
+
await connection.connect();
|
|
227
|
+
received.length = 0;
|
|
228
|
+
await connection.eventStores.allEventStores({}, { metadata: original });
|
|
229
|
+
});
|
|
230
|
+
it('should remove stale propagation without losing other metadata', () => {
|
|
231
|
+
should.equal(received[0].get('traceparent'), undefined);
|
|
232
|
+
should.equal(received[0].get('baggage'), undefined);
|
|
233
|
+
should.equal(received[0].get('authorization'), 'Bearer caller-token');
|
|
234
|
+
});
|
|
235
|
+
});
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
import { afterAll, beforeAll, beforeEach, chai, describe, it } from 'vitest';
|
|
5
|
+
import { context, propagation, trace } from '@opentelemetry/api';
|
|
6
|
+
import { GrpcInstrumentation } from '@opentelemetry/instrumentation-grpc';
|
|
7
|
+
import type { ServiceImplementation } from 'nice-grpc';
|
|
8
|
+
import { Metadata } from 'nice-grpc-common';
|
|
9
|
+
import type { ChronicleConnection } from '../../ChronicleConnection.js';
|
|
10
|
+
import { telemetrySession } from '../../../telemetry/for_ChronicleTelemetry/given/a_telemetry_session.fixture.js';
|
|
11
|
+
|
|
12
|
+
const should = chai.should();
|
|
13
|
+
const telemetry = telemetrySession();
|
|
14
|
+
const instrumentation = new GrpcInstrumentation({ enabled: false });
|
|
15
|
+
const correlation = 'f5a978af-cd69-4a98-9f1e-7f656cc8ba2a';
|
|
16
|
+
let server: ReturnType<typeof import('nice-grpc').createServer>;
|
|
17
|
+
let connection: ChronicleConnection;
|
|
18
|
+
const received: Metadata[] = [];
|
|
19
|
+
let hostTraceId: string;
|
|
20
|
+
|
|
21
|
+
beforeAll(async () => {
|
|
22
|
+
instrumentation.setTracerProvider(telemetry.traces);
|
|
23
|
+
instrumentation.enable();
|
|
24
|
+
// Load grpc-js only after the real instrumentation's require hooks are installed.
|
|
25
|
+
const { createServer } = await import('nice-grpc');
|
|
26
|
+
const { ConnectionServiceDefinition, EventStoresDefinition, ReactorsDefinition } = await import('@cratis/chronicle.contracts');
|
|
27
|
+
const { ChronicleConnection } = await import('../../ChronicleConnection.js');
|
|
28
|
+
server = createServer().use(async function* (call, callContext) {
|
|
29
|
+
received.push(Metadata(callContext.metadata));
|
|
30
|
+
return yield* call.next(call.request, callContext);
|
|
31
|
+
});
|
|
32
|
+
const unused = async () => { throw new Error('Unexpected RPC'); };
|
|
33
|
+
server.add(ConnectionServiceDefinition, {
|
|
34
|
+
...Object.fromEntries(Object.keys(ConnectionServiceDefinition.methods).map(name => [name, unused])),
|
|
35
|
+
checkCompatibility: async () => ({ IsCompatible: true, Incompatibilities: [], ServerVersion: 'test' })
|
|
36
|
+
} as ServiceImplementation<typeof ConnectionServiceDefinition>);
|
|
37
|
+
server.add(EventStoresDefinition, {
|
|
38
|
+
allEventStores: async () => ({ IsAuthorized: true, Data: [] }),
|
|
39
|
+
ensureEventStore: unused,
|
|
40
|
+
observeEventStores: async function* () {
|
|
41
|
+
yield { IsAuthorized: true, Data: [] };
|
|
42
|
+
yield { IsAuthorized: true, Data: [] };
|
|
43
|
+
}
|
|
44
|
+
} as ServiceImplementation<typeof EventStoresDefinition>);
|
|
45
|
+
server.add(ReactorsDefinition, {
|
|
46
|
+
...Object.fromEntries(Object.keys(ReactorsDefinition.methods).map(name => [name, unused])),
|
|
47
|
+
observe: async function* (requests) {
|
|
48
|
+
for await (const _request of requests) {
|
|
49
|
+
yield { Events: [], Partition: '', ReplayState: 0 };
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
} as ServiceImplementation<typeof ReactorsDefinition>);
|
|
53
|
+
const port = await server.listen('127.0.0.1:0');
|
|
54
|
+
connection = new ChronicleConnection({ connectionString: `chronicle://127.0.0.1:${port}?disableTls=true&apiKey=test` });
|
|
55
|
+
await connection.connect();
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
afterAll(async () => {
|
|
59
|
+
connection?.dispose();
|
|
60
|
+
await server?.shutdown();
|
|
61
|
+
instrumentation.disable();
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
describe('when gRPC auto-instrumentation reinjects active context for unary and streaming calls', () => {
|
|
65
|
+
beforeEach(async () => {
|
|
66
|
+
received.length = 0;
|
|
67
|
+
const span = telemetry.traces.getTracer('host').startSpan('host');
|
|
68
|
+
hostTraceId = span.spanContext().traceId;
|
|
69
|
+
const active = propagation.setBaggage(trace.setSpan(context.active(), span), propagation.createBaggage({
|
|
70
|
+
'cratis.correlation_id': { value: correlation },
|
|
71
|
+
email: { value: 'private@example.com' },
|
|
72
|
+
'cratis.event_source.id': { value: 'private-source' },
|
|
73
|
+
'cratis.tenant.id': { value: 'private-tenant' }
|
|
74
|
+
}));
|
|
75
|
+
try {
|
|
76
|
+
await context.with(active, async () => {
|
|
77
|
+
await connection.eventStores.allEventStores({});
|
|
78
|
+
for await (const _response of connection.eventStores.observeEventStores({})) {
|
|
79
|
+
context.active().should.equal(active);
|
|
80
|
+
await Promise.resolve();
|
|
81
|
+
}
|
|
82
|
+
async function* requests() { yield { Content: undefined }; yield { Content: undefined }; }
|
|
83
|
+
for await (const _response of connection.reactors.observe(requests())) {
|
|
84
|
+
context.active().should.equal(active);
|
|
85
|
+
await Promise.resolve();
|
|
86
|
+
}
|
|
87
|
+
propagation.getBaggage(context.active())!.getEntry('email')!.value.should.equal('private@example.com');
|
|
88
|
+
});
|
|
89
|
+
} finally { span.end(); }
|
|
90
|
+
});
|
|
91
|
+
it('should never let auto-instrumentation restore forbidden baggage on the wire', () => {
|
|
92
|
+
received.should.have.lengthOf(3);
|
|
93
|
+
for (const metadata of received) {
|
|
94
|
+
should.equal(metadata.get('baggage'), `cratis.correlation_id=${correlation}`);
|
|
95
|
+
metadata.get('traceparent')!.should.contain(hostTraceId);
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
it('should actually execute the enabled gRPC instrumentation', () => {
|
|
99
|
+
telemetry.spans.getFinishedSpans().filter(span => span.instrumentationScope.name === '@opentelemetry/instrumentation-grpc')
|
|
100
|
+
.length.should.be.greaterThan(0);
|
|
101
|
+
});
|
|
102
|
+
});
|
|
@@ -18,11 +18,21 @@ export class CorrelationIdManager implements ICorrelationIdAccessor, ICorrelatio
|
|
|
18
18
|
return this._storage.getStore() ?? CorrelationId.create();
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
/** The explicitly scoped identifier, without generating an identifier when no scope exists. */
|
|
22
|
+
get scoped(): CorrelationId | undefined {
|
|
23
|
+
return this._storage.getStore();
|
|
24
|
+
}
|
|
25
|
+
|
|
21
26
|
/** Runs an async or synchronous operation with a correlation id, restoring the caller afterward. */
|
|
22
27
|
run<T>(correlationId: CorrelationId, callback: () => T): T {
|
|
23
28
|
return this._storage.run(correlationId, callback);
|
|
24
29
|
}
|
|
25
30
|
|
|
31
|
+
/** Runs work without inheriting a business correlation, restoring the caller afterward. */
|
|
32
|
+
runWithout<T>(callback: () => T): T {
|
|
33
|
+
return this._storage.exit(callback);
|
|
34
|
+
}
|
|
35
|
+
|
|
26
36
|
/** @inheritdoc */
|
|
27
37
|
setCurrent(correlationId: CorrelationId): void {
|
|
28
38
|
this._storage.enterWith(correlationId);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChronicleClient.d.ts","sourceRoot":"","sources":["../ChronicleClient.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ChronicleClient.d.ts","sourceRoot":"","sources":["../ChronicleClient.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAUzD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AACvE,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAQ/C;;;;;;;;;;;;GAYG;AACH,qBAAa,eAAgB,YAAW,gBAAgB;IAwBxC,QAAQ,CAAC,OAAO,EAAE,gBAAgB;IAvB9C,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,sBAAsB,CAAQ;IACtD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAU;IAE/C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAsB;IAClD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAsC;IAC9D,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA6B;IAExD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAkC;IAE1D,OAAO,CAAC,eAAe,CAAC,CAAiC;IACzD,OAAO,CAAC,iBAAiB,CAAC,CAAgB;IAC1C,OAAO,CAAC,mBAAmB,CAAC,CAAgB;IAC5C,OAAO,CAAC,mBAAmB,CAAC,CAAgB;IAC5C,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,yBAAyB,CAAC,CAAkB;IACpD,OAAO,CAAC,oBAAoB,CAAS;IACrC,OAAO,CAAC,kBAAkB,CAAC,CAAQ;IACnC,OAAO,CAAC,gCAAgC,CAAK;IAE7C;;;OAGG;IACH,YAAqB,OAAO,EAAE,gBAAgB,EAiD7C;IAED,kBAAkB;IACZ,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,uBAAuB,GAAG,OAAO,CAAC,WAAW,CAAC,CAuDrH;IAED,kBAAkB;IACZ,cAAc,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC,CAuBhD;IAED,kBAAkB;IAClB,OAAO,IAAI,IAAI,CAqBd;YAEa,gBAAgB;YA4ChB,eAAe;YAuBf,SAAS;IAmDvB;;;;OAIG;YACW,OAAO;YAcP,aAAa;IAyB3B,OAAO,CAAC,uBAAuB;IAW/B,OAAO,CAAC,2BAA2B;IAOnC,OAAO,CAAC,eAAe;IA2BvB,OAAO,CAAC,cAAc;IAQtB,OAAO,CAAC,uBAAuB;YAQjB,cAAc;YAkBd,oBAAoB;IAmClC,OAAO,CAAC,cAAc;IAUtB,OAAO,CAAC,0BAA0B;YAQpB,yBAAyB;CA4B1C"}
|
package/dist/ChronicleClient.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
// Copyright (c) Cratis. All rights reserved.
|
|
2
2
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
3
|
import * as os from 'os';
|
|
4
|
-
import {
|
|
4
|
+
import { createLogger } from './logging/createLogger.js';
|
|
5
|
+
import { setTelemetryAttribute, recordSafeException } from './telemetry/spanAttributes.js';
|
|
6
|
+
import { WellKnownTelemetryNames } from './WellKnownTelemetryNames.js';
|
|
5
7
|
import { SpanStatusCode } from '@opentelemetry/api';
|
|
6
8
|
import { ChronicleConnection } from './connection/index.js';
|
|
7
9
|
import { IncompatibleChronicleServer } from './connection/IncompatibleChronicleServer.js';
|
|
@@ -15,7 +17,8 @@ import { EventStore } from './EventStore.js';
|
|
|
15
17
|
import { EventStoreName } from './EventStoreName.js';
|
|
16
18
|
import { EventStoreNamespaceName } from './EventStoreNamespaceName.js';
|
|
17
19
|
import { ChronicleMetrics } from './Metrics.js';
|
|
18
|
-
import {
|
|
20
|
+
import { observeOperation } from './telemetry/observeOperation.js';
|
|
21
|
+
import { runInBackgroundContext } from './telemetry/runInBackgroundContext.js';
|
|
19
22
|
import { TypeDiscoverer } from './types/index.js';
|
|
20
23
|
import { takeUnregisteredModelBoundMappings } from './types/modelBoundPropertyMetadata.js';
|
|
21
24
|
import { reachableReadModelTypes } from './readModels/rootReadModelTypes.js';
|
|
@@ -39,9 +42,7 @@ export class ChronicleClient {
|
|
|
39
42
|
_connection;
|
|
40
43
|
_stores = new Map();
|
|
41
44
|
_lifecycle = new ConnectionLifecycle();
|
|
42
|
-
_logger
|
|
43
|
-
namespace: '@cratis/chronicle/ChronicleClient'
|
|
44
|
-
});
|
|
45
|
+
_logger;
|
|
45
46
|
_watchdogHandle;
|
|
46
47
|
_connectOperation;
|
|
47
48
|
_reconnectOperation;
|
|
@@ -57,6 +58,7 @@ export class ChronicleClient {
|
|
|
57
58
|
*/
|
|
58
59
|
constructor(options) {
|
|
59
60
|
this.options = options;
|
|
61
|
+
this._logger = createLogger('@cratis/chronicle/ChronicleClient', options.logger);
|
|
60
62
|
// When TLS is disabled, pass pre-built insecure credentials directly.
|
|
61
63
|
// @cratis/chronicle.contracts ≤15.24.3 always composes call credentials with
|
|
62
64
|
// channel credentials, which gRPC forbids for insecure channels. Passing
|
|
@@ -65,15 +67,17 @@ export class ChronicleClient {
|
|
|
65
67
|
const connectionOptions = options.connectionString.disableTls
|
|
66
68
|
? { connectionString: options.connectionString, credentials: options.connectionString.createCredentials() }
|
|
67
69
|
: { connectionString: options.connectionString };
|
|
68
|
-
this._connection = new ChronicleConnection(connectionOptions);
|
|
70
|
+
this._connection = new ChronicleConnection({ ...connectionOptions, logger: options.logger });
|
|
69
71
|
this._logger.info('Created Chronicle client', {
|
|
70
72
|
serverAddress: `${options.connectionString.serverAddress.host}:${options.connectionString.serverAddress.port}`,
|
|
71
73
|
disableTls: options.connectionString.disableTls
|
|
72
74
|
});
|
|
73
75
|
if (options.discoveryPatterns.length > 0) {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
this.
|
|
76
|
+
runInBackgroundContext(() => {
|
|
77
|
+
this._discoveryOperation = TypeDiscoverer.default.discover(options.discoveryPatterns);
|
|
78
|
+
this._discoveryOperation.catch(error => {
|
|
79
|
+
this._logger.error('Artifact file discovery failed', { error });
|
|
80
|
+
});
|
|
77
81
|
});
|
|
78
82
|
}
|
|
79
83
|
this._lifecycle.onConnected(async () => {
|
|
@@ -104,9 +108,9 @@ export class ChronicleClient {
|
|
|
104
108
|
: typeof namespace === 'string'
|
|
105
109
|
? new EventStoreNamespaceName(namespace)
|
|
106
110
|
: namespace;
|
|
107
|
-
return
|
|
108
|
-
span
|
|
109
|
-
span
|
|
111
|
+
return observeOperation(WellKnownTelemetryNames.spans.getEventStore, async (span) => {
|
|
112
|
+
setTelemetryAttribute(span, 'eventStore', storeName.value);
|
|
113
|
+
setTelemetryAttribute(span, 'namespace', namespaceName.value);
|
|
110
114
|
try {
|
|
111
115
|
const store = await this.withReconnect('get_event_store', async () => {
|
|
112
116
|
await this.ensureConnected();
|
|
@@ -123,7 +127,7 @@ export class ChronicleClient {
|
|
|
123
127
|
eventStore: storeName.value
|
|
124
128
|
});
|
|
125
129
|
ensureCommandSuccess('ensure event store', await this._connection.eventStores.ensureEventStore({ Name: storeName.value }));
|
|
126
|
-
const created = new EventStore(storeName, namespaceName, this._connection, this._lifecycle, this.options.defaultSinkTypeId, this.options.clientArtifactsProvider, this.options.reactorResultHandler, this.options.artifactActivator, this.options.readModelNamingPolicy);
|
|
130
|
+
const created = new EventStore(storeName, namespaceName, this._connection, this._lifecycle, this.options.defaultSinkTypeId, this.options.clientArtifactsProvider, this.options.reactorResultHandler, this.options.artifactActivator, this.options.readModelNamingPolicy, this.options.telemetry);
|
|
127
131
|
this._stores.set(key, created);
|
|
128
132
|
await this.registerArtifactsForStore(created, 'new-store');
|
|
129
133
|
return created;
|
|
@@ -136,12 +140,11 @@ export class ChronicleClient {
|
|
|
136
140
|
return store;
|
|
137
141
|
}
|
|
138
142
|
catch (error) {
|
|
139
|
-
span
|
|
140
|
-
span.recordException(error);
|
|
143
|
+
recordSafeException(span, error);
|
|
141
144
|
this._logger.error('Failed getting event store', {
|
|
142
145
|
eventStore: storeName.value,
|
|
143
146
|
namespace: namespaceName.value,
|
|
144
|
-
error
|
|
147
|
+
error
|
|
145
148
|
});
|
|
146
149
|
throw error;
|
|
147
150
|
}
|
|
@@ -152,7 +155,7 @@ export class ChronicleClient {
|
|
|
152
155
|
}
|
|
153
156
|
/** @inheritdoc */
|
|
154
157
|
async getEventStores() {
|
|
155
|
-
return
|
|
158
|
+
return observeOperation(WellKnownTelemetryNames.spans.getEventStores, async (span) => {
|
|
156
159
|
try {
|
|
157
160
|
const response = await this.withReconnect('get_event_stores', async () => {
|
|
158
161
|
await this.ensureConnected();
|
|
@@ -166,10 +169,9 @@ export class ChronicleClient {
|
|
|
166
169
|
return result;
|
|
167
170
|
}
|
|
168
171
|
catch (error) {
|
|
169
|
-
span
|
|
170
|
-
span.recordException(error);
|
|
172
|
+
recordSafeException(span, error);
|
|
171
173
|
this._logger.error('Failed retrieving event stores', {
|
|
172
|
-
error
|
|
174
|
+
error
|
|
173
175
|
});
|
|
174
176
|
throw error;
|
|
175
177
|
}
|
|
@@ -190,7 +192,7 @@ export class ChronicleClient {
|
|
|
190
192
|
if (this._lifecycle.isConnected) {
|
|
191
193
|
void this._lifecycle.disconnected(error => {
|
|
192
194
|
this._logger.error('Disconnected lifecycle callback failed during dispose', {
|
|
193
|
-
error
|
|
195
|
+
error
|
|
194
196
|
});
|
|
195
197
|
});
|
|
196
198
|
}
|
|
@@ -218,7 +220,7 @@ export class ChronicleClient {
|
|
|
218
220
|
await this.startKernelKeepAlive();
|
|
219
221
|
await this._lifecycle.connected(error => {
|
|
220
222
|
this._logger.error('Connected lifecycle callback failed', {
|
|
221
|
-
error
|
|
223
|
+
error
|
|
222
224
|
});
|
|
223
225
|
});
|
|
224
226
|
return;
|
|
@@ -249,7 +251,7 @@ export class ChronicleClient {
|
|
|
249
251
|
return;
|
|
250
252
|
}
|
|
251
253
|
if (!this._connectOperation) {
|
|
252
|
-
this._connectOperation = this.connectWithRetry().finally(() => {
|
|
254
|
+
this._connectOperation = runInBackgroundContext(() => this.connectWithRetry()).finally(() => {
|
|
253
255
|
this._connectOperation = undefined;
|
|
254
256
|
});
|
|
255
257
|
}
|
|
@@ -259,15 +261,15 @@ export class ChronicleClient {
|
|
|
259
261
|
if (this._connectionFailure)
|
|
260
262
|
throw this._connectionFailure;
|
|
261
263
|
if (!this._reconnectOperation) {
|
|
262
|
-
this._reconnectOperation = (async () => {
|
|
264
|
+
this._reconnectOperation = runInBackgroundContext(async () => {
|
|
263
265
|
this._logger.warn('Reconnecting to Chronicle kernel', {
|
|
264
266
|
reason,
|
|
265
|
-
error
|
|
267
|
+
error
|
|
266
268
|
});
|
|
267
269
|
if (this._lifecycle.isConnected) {
|
|
268
270
|
await this._lifecycle.disconnected(disconnectError => {
|
|
269
271
|
this._logger.error('Disconnected lifecycle callback failed', {
|
|
270
|
-
error:
|
|
272
|
+
error: disconnectError
|
|
271
273
|
});
|
|
272
274
|
});
|
|
273
275
|
}
|
|
@@ -282,7 +284,7 @@ export class ChronicleClient {
|
|
|
282
284
|
await this.startKernelKeepAlive();
|
|
283
285
|
await this._lifecycle.connected(connectedError => {
|
|
284
286
|
this._logger.error('Connected lifecycle callback failed after reconnect', {
|
|
285
|
-
error:
|
|
287
|
+
error: connectedError
|
|
286
288
|
});
|
|
287
289
|
});
|
|
288
290
|
return;
|
|
@@ -297,7 +299,7 @@ export class ChronicleClient {
|
|
|
297
299
|
await this.backOff(attempt, 'Reconnect attempt failed, retrying', reconnectError);
|
|
298
300
|
}
|
|
299
301
|
}
|
|
300
|
-
})
|
|
302
|
+
}).finally(() => {
|
|
301
303
|
this._reconnectOperation = undefined;
|
|
302
304
|
});
|
|
303
305
|
}
|
|
@@ -314,7 +316,7 @@ export class ChronicleClient {
|
|
|
314
316
|
this._logger.warn(message, {
|
|
315
317
|
attempt,
|
|
316
318
|
delayMs,
|
|
317
|
-
error
|
|
319
|
+
error
|
|
318
320
|
});
|
|
319
321
|
await new Promise(resolve => setTimeout(resolve, delayMs));
|
|
320
322
|
return delayMs;
|
|
@@ -394,9 +396,9 @@ export class ChronicleClient {
|
|
|
394
396
|
return String(error);
|
|
395
397
|
}
|
|
396
398
|
startConnectionWatchdog() {
|
|
397
|
-
this._watchdogHandle = setInterval(() => {
|
|
399
|
+
this._watchdogHandle = runInBackgroundContext(() => setInterval(() => {
|
|
398
400
|
void this.runHealthCheck().catch(error => this.backgroundConnectionFailed('watchdog-health-check', error));
|
|
399
|
-
}, ChronicleClient._healthCheckIntervalMs);
|
|
401
|
+
}, ChronicleClient._healthCheckIntervalMs));
|
|
400
402
|
this._watchdogHandle.unref?.();
|
|
401
403
|
}
|
|
402
404
|
async runHealthCheck() {
|
|
@@ -430,7 +432,7 @@ export class ChronicleClient {
|
|
|
430
432
|
return;
|
|
431
433
|
}
|
|
432
434
|
void this.reconnect(reason, error).catch(failure => this.backgroundConnectionFailed(reason, failure));
|
|
433
|
-
});
|
|
435
|
+
}, {}, this.options.logger);
|
|
434
436
|
await keepAlive.start({
|
|
435
437
|
ConnectionId: this._lifecycle.connectionId,
|
|
436
438
|
ClientVersion: clientVersion,
|
|
@@ -457,7 +459,7 @@ export class ChronicleClient {
|
|
|
457
459
|
this.failConnection(error instanceof Error ? error : new Error(String(error)));
|
|
458
460
|
this._logger.error('Background connection recovery failed; create a new client after correcting the server', {
|
|
459
461
|
reason,
|
|
460
|
-
error
|
|
462
|
+
error
|
|
461
463
|
});
|
|
462
464
|
}
|
|
463
465
|
async registerArtifactsForStore(store, reason) {
|