@livestore/sync-cf 0.4.0 → 0.5.0-dev.1
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/README.md +5 -5
- package/dist/.tsbuildinfo +1 -1
- package/dist/cf-worker/do/durable-object.d.ts.map +1 -1
- package/dist/cf-worker/do/durable-object.js +53 -36
- package/dist/cf-worker/do/durable-object.js.map +1 -1
- package/dist/cf-worker/do/layer.d.ts +22 -18
- package/dist/cf-worker/do/layer.d.ts.map +1 -1
- package/dist/cf-worker/do/layer.js +45 -35
- package/dist/cf-worker/do/layer.js.map +1 -1
- package/dist/cf-worker/do/observability.d.ts +23 -0
- package/dist/cf-worker/do/observability.d.ts.map +1 -0
- package/dist/cf-worker/do/observability.js +52 -0
- package/dist/cf-worker/do/observability.js.map +1 -0
- package/dist/cf-worker/do/observability.test.d.ts +2 -0
- package/dist/cf-worker/do/observability.test.d.ts.map +1 -0
- package/dist/cf-worker/do/observability.test.js +204 -0
- package/dist/cf-worker/do/observability.test.js.map +1 -0
- package/dist/cf-worker/do/pull.d.ts +3 -3
- package/dist/cf-worker/do/pull.d.ts.map +1 -1
- package/dist/cf-worker/do/pull.js +19 -20
- package/dist/cf-worker/do/pull.js.map +1 -1
- package/dist/cf-worker/do/push.d.ts +16 -3
- package/dist/cf-worker/do/push.d.ts.map +1 -1
- package/dist/cf-worker/do/push.js +74 -53
- package/dist/cf-worker/do/push.js.map +1 -1
- package/dist/cf-worker/do/push.test.d.ts +2 -0
- package/dist/cf-worker/do/push.test.d.ts.map +1 -0
- package/dist/cf-worker/do/push.test.js +30 -0
- package/dist/cf-worker/do/push.test.js.map +1 -0
- package/dist/cf-worker/do/sqlite.d.ts +34 -46
- package/dist/cf-worker/do/sqlite.d.ts.map +1 -1
- package/dist/cf-worker/do/sqlite.js +2 -2
- package/dist/cf-worker/do/sqlite.js.map +1 -1
- package/dist/cf-worker/do/sync-storage.d.ts +7 -6
- package/dist/cf-worker/do/sync-storage.d.ts.map +1 -1
- package/dist/cf-worker/do/sync-storage.js +29 -17
- package/dist/cf-worker/do/sync-storage.js.map +1 -1
- package/dist/cf-worker/do/transport/do-rpc-server.d.ts +17 -2
- package/dist/cf-worker/do/transport/do-rpc-server.d.ts.map +1 -1
- package/dist/cf-worker/do/transport/do-rpc-server.js +41 -23
- package/dist/cf-worker/do/transport/do-rpc-server.js.map +1 -1
- package/dist/cf-worker/do/transport/do-rpc-server.test.d.ts +2 -0
- package/dist/cf-worker/do/transport/do-rpc-server.test.d.ts.map +1 -0
- package/dist/cf-worker/do/transport/do-rpc-server.test.js +28 -0
- package/dist/cf-worker/do/transport/do-rpc-server.test.js.map +1 -0
- package/dist/cf-worker/do/transport/http-rpc-server.d.ts +2 -2
- package/dist/cf-worker/do/transport/http-rpc-server.d.ts.map +1 -1
- package/dist/cf-worker/do/transport/http-rpc-server.js +12 -12
- package/dist/cf-worker/do/transport/http-rpc-server.js.map +1 -1
- package/dist/cf-worker/do/transport/ws-rpc-server.d.ts +9 -2
- package/dist/cf-worker/do/transport/ws-rpc-server.d.ts.map +1 -1
- package/dist/cf-worker/do/transport/ws-rpc-server.js +37 -29
- package/dist/cf-worker/do/transport/ws-rpc-server.js.map +1 -1
- package/dist/cf-worker/mod.js +3 -3
- package/dist/cf-worker/shared.d.ts +37 -29
- package/dist/cf-worker/shared.d.ts.map +1 -1
- package/dist/cf-worker/shared.js +14 -12
- package/dist/cf-worker/shared.js.map +1 -1
- package/dist/cf-worker/worker.d.ts +9 -9
- package/dist/cf-worker/worker.d.ts.map +1 -1
- package/dist/cf-worker/worker.js +17 -15
- package/dist/cf-worker/worker.js.map +1 -1
- package/dist/client/mod.js +3 -3
- package/dist/client/transport/do-rpc-client.d.ts +18 -6
- package/dist/client/transport/do-rpc-client.d.ts.map +1 -1
- package/dist/client/transport/do-rpc-client.js +112 -38
- package/dist/client/transport/do-rpc-client.js.map +1 -1
- package/dist/client/transport/do-rpc-client.test.d.ts +2 -0
- package/dist/client/transport/do-rpc-client.test.d.ts.map +1 -0
- package/dist/client/transport/do-rpc-client.test.js +268 -0
- package/dist/client/transport/do-rpc-client.test.js.map +1 -0
- package/dist/client/transport/http-rpc-client.d.ts +3 -3
- package/dist/client/transport/http-rpc-client.d.ts.map +1 -1
- package/dist/client/transport/http-rpc-client.js +48 -29
- package/dist/client/transport/http-rpc-client.js.map +1 -1
- package/dist/client/transport/rpc-client-error.d.ts +5 -0
- package/dist/client/transport/rpc-client-error.d.ts.map +1 -0
- package/dist/client/transport/rpc-client-error.js +8 -0
- package/dist/client/transport/rpc-client-error.js.map +1 -0
- package/dist/client/transport/rpc-client-error.test.d.ts +2 -0
- package/dist/client/transport/rpc-client-error.test.d.ts.map +1 -0
- package/dist/client/transport/rpc-client-error.test.js +33 -0
- package/dist/client/transport/rpc-client-error.test.js.map +1 -0
- package/dist/client/transport/ws-rpc-client.d.ts +3 -3
- package/dist/client/transport/ws-rpc-client.d.ts.map +1 -1
- package/dist/client/transport/ws-rpc-client.js +33 -23
- package/dist/client/transport/ws-rpc-client.js.map +1 -1
- package/dist/common/do-rpc-schema.d.ts +53 -41
- package/dist/common/do-rpc-schema.d.ts.map +1 -1
- package/dist/common/do-rpc-schema.js +13 -4
- package/dist/common/do-rpc-schema.js.map +1 -1
- package/dist/common/http-rpc-schema.d.ts +38 -39
- package/dist/common/http-rpc-schema.d.ts.map +1 -1
- package/dist/common/http-rpc-schema.js +6 -6
- package/dist/common/http-rpc-schema.js.map +1 -1
- package/dist/common/mod.d.ts +5 -5
- package/dist/common/mod.d.ts.map +1 -1
- package/dist/common/mod.js +7 -7
- package/dist/common/mod.js.map +1 -1
- package/dist/common/sync-message-types.d.ts +116 -133
- package/dist/common/sync-message-types.d.ts.map +1 -1
- package/dist/common/sync-message-types.js +26 -14
- package/dist/common/sync-message-types.js.map +1 -1
- package/dist/common/ws-rpc-schema.d.ts +33 -34
- package/dist/common/ws-rpc-schema.d.ts.map +1 -1
- package/dist/common/ws-rpc-schema.js +5 -5
- package/dist/common/ws-rpc-schema.js.map +1 -1
- package/package.json +42 -58
- package/src/cf-worker/do/durable-object.ts +70 -64
- package/src/cf-worker/do/layer.ts +142 -99
- package/src/cf-worker/do/observability.test.ts +337 -0
- package/src/cf-worker/do/observability.ts +121 -0
- package/src/cf-worker/do/pull.ts +28 -28
- package/src/cf-worker/do/push.test.ts +40 -0
- package/src/cf-worker/do/push.ts +139 -115
- package/src/cf-worker/do/sqlite.ts +1 -1
- package/src/cf-worker/do/sync-storage.ts +34 -21
- package/src/cf-worker/do/transport/do-rpc-server.test.ts +32 -0
- package/src/cf-worker/do/transport/do-rpc-server.ts +46 -35
- package/src/cf-worker/do/transport/http-rpc-server.ts +7 -10
- package/src/cf-worker/do/transport/ws-rpc-server.ts +52 -28
- package/src/cf-worker/shared.ts +29 -17
- package/src/cf-worker/worker.ts +21 -22
- package/src/client/transport/do-rpc-client.test.ts +411 -0
- package/src/client/transport/do-rpc-client.ts +175 -51
- package/src/client/transport/http-rpc-client.ts +66 -45
- package/src/client/transport/rpc-client-error.test.ts +41 -0
- package/src/client/transport/rpc-client-error.ts +9 -0
- package/src/client/transport/ws-rpc-client.ts +40 -32
- package/src/common/do-rpc-schema.ts +13 -3
- package/src/common/http-rpc-schema.ts +5 -5
- package/src/common/mod.ts +4 -4
- package/src/common/sync-message-types.ts +16 -16
- package/src/common/ws-rpc-schema.ts +4 -4
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { ROOT_CONTEXT, SpanStatusCode, trace } from '@opentelemetry/api';
|
|
2
|
+
import { BackendIdMismatchError, UnknownError } from '@livestore/common';
|
|
3
|
+
import { layerProtocolDurableObject, WsContext } from '@livestore/common-cf';
|
|
4
|
+
import { makeNoopSpan, makeNoopTracer } from '@livestore/utils';
|
|
5
|
+
import { Vitest } from '@livestore/utils-dev/node-vitest';
|
|
6
|
+
import { Deferred, Effect, FetchHttpClient, Fiber, Layer, Option, OtelTracer, RpcClient, RpcTest, Schema, Stream, TestClock, Tracer, } from '@livestore/utils/effect';
|
|
7
|
+
import { SyncDoRpc } from '../../common/do-rpc-schema.js';
|
|
8
|
+
import { SyncWsRpc } from '../../common/ws-rpc-schema.js';
|
|
9
|
+
import { WebSocketAttachmentSchema } from '../shared.js';
|
|
10
|
+
import { makeObservability, rpcSpanOptions, withRpcSpan, withRpcStreamSpan } from './observability.js';
|
|
11
|
+
import { createDoRpcHandler } from './transport/do-rpc-server.js';
|
|
12
|
+
import { makeRpcHandlers } from './transport/ws-rpc-server.js';
|
|
13
|
+
Vitest.describe('sync-cf telemetry ownership', () => {
|
|
14
|
+
Vitest.afterEach(() => Vitest.vi.restoreAllMocks());
|
|
15
|
+
Vitest.effect('runs sync effects and streams without exporting when telemetry is omitted', () => Effect.gen(function* () {
|
|
16
|
+
const fetch = Vitest.vi.fn(async () => new Response('{}'));
|
|
17
|
+
const observability = makeObservability(undefined);
|
|
18
|
+
const result = yield* Effect.succeed('ack').pipe(Effect.withSpan('push'), observability.effect, Effect.provideService(FetchHttpClient.Fetch, fetch));
|
|
19
|
+
const history = yield* Stream.make('event').pipe(Stream.withSpan('pull'), observability.stream, Stream.runCollect, Effect.provideService(FetchHttpClient.Fetch, fetch));
|
|
20
|
+
yield* TestClock.adjust('4 seconds');
|
|
21
|
+
Vitest.expect(result).toBe('ack');
|
|
22
|
+
Vitest.expect(history).toEqual(['event']);
|
|
23
|
+
Vitest.expect(fetch).not.toHaveBeenCalled();
|
|
24
|
+
}));
|
|
25
|
+
Vitest.effect('builds and finalizes a supplied layer independently for overlapping operations', () => Effect.gen(function* () {
|
|
26
|
+
const firstStarted = yield* Deferred.make();
|
|
27
|
+
const secondStarted = yield* Deferred.make();
|
|
28
|
+
const firstGate = yield* Deferred.make();
|
|
29
|
+
const secondGate = yield* Deferred.make();
|
|
30
|
+
const firstReleased = yield* Deferred.make();
|
|
31
|
+
const secondReleased = yield* Deferred.make();
|
|
32
|
+
const builds = [];
|
|
33
|
+
const releases = [];
|
|
34
|
+
const layer = Layer.effectDiscard(Effect.acquireRelease(Effect.currentSpan.pipe(Effect.orDie, Effect.tap((span) => Effect.sync(() => builds.push(span.name)))), (span) => Effect.sync(() => releases.push(span.name)).pipe(Effect.andThen(Deferred.succeed(span.name === 'first' ? firstReleased : secondReleased, undefined)))));
|
|
35
|
+
const observability = makeObservability(layer);
|
|
36
|
+
const first = yield* Deferred.succeed(firstStarted, undefined).pipe(Effect.andThen(Deferred.await(firstGate)), observability.effect, Effect.withSpan('first'), Effect.forkChild);
|
|
37
|
+
yield* Deferred.await(firstStarted);
|
|
38
|
+
const second = yield* Deferred.succeed(secondStarted, undefined).pipe(Effect.andThen(Deferred.await(secondGate)), observability.effect, Effect.withSpan('second'), Effect.forkChild);
|
|
39
|
+
yield* Deferred.await(secondStarted);
|
|
40
|
+
Vitest.expect(builds).toEqual(['first', 'second']);
|
|
41
|
+
Vitest.expect(releases).toEqual([]);
|
|
42
|
+
yield* Deferred.succeed(firstGate, undefined);
|
|
43
|
+
yield* Fiber.join(first);
|
|
44
|
+
yield* Deferred.await(firstReleased);
|
|
45
|
+
Vitest.expect(releases).toEqual(['first']);
|
|
46
|
+
Vitest.expect(second.pollUnsafe()).toBeUndefined();
|
|
47
|
+
yield* Deferred.succeed(secondGate, undefined);
|
|
48
|
+
yield* Fiber.join(second);
|
|
49
|
+
yield* Deferred.await(secondReleased);
|
|
50
|
+
Vitest.expect(releases).toEqual(['first', 'second']);
|
|
51
|
+
}));
|
|
52
|
+
Vitest.effect('finalizes finite history while the live subscription is still open', () => Effect.gen(function* () {
|
|
53
|
+
const { layer, ended } = makeRecordingTracer();
|
|
54
|
+
const finalized = yield* Deferred.make();
|
|
55
|
+
const live = yield* Deferred.make();
|
|
56
|
+
const observability = makeObservability(Layer.mergeAll(layer, Layer.effectDiscard(Effect.addFinalizer(() => Deferred.succeed(finalized, [...ended])))));
|
|
57
|
+
const fiber = yield* Stream.make('history').pipe(Stream.withSpan('pull-history'), observability.stream, Stream.concat(Stream.fromEffect(Deferred.succeed(live, undefined)).pipe(Stream.concat(Stream.never))), Stream.runDrain, Effect.forkChild);
|
|
58
|
+
yield* Effect.addFinalizer(() => Fiber.interrupt(fiber));
|
|
59
|
+
yield* Deferred.await(live);
|
|
60
|
+
Vitest.expect(yield* Deferred.await(finalized)).toContain('pull-history');
|
|
61
|
+
Vitest.expect(fiber.pollUnsafe()).toBeUndefined();
|
|
62
|
+
}));
|
|
63
|
+
Vitest.effect('ends WebSocket telemetry before the production handler enters its live tail', () => Effect.gen(function* () {
|
|
64
|
+
const { layer, ended } = makeRecordingTracer();
|
|
65
|
+
const finalized = yield* Deferred.make();
|
|
66
|
+
const observability = makeObservability(Layer.mergeAll(layer, Layer.effectDiscard(Effect.addFinalizer(() => Deferred.succeed(finalized, undefined)))));
|
|
67
|
+
const input = makeDoInput();
|
|
68
|
+
const handlers = makeRpcHandlers({ ...input, observability }).pipe(Layer.provide(Layer.succeed(WsContext, WsContext.of({ ws: makeWebSocket() }))));
|
|
69
|
+
const client = yield* RpcTest.makeClient(SyncWsRpc).pipe(Effect.provide(handlers));
|
|
70
|
+
const pullFiber = yield* client['SyncWsRpc.Pull']({
|
|
71
|
+
storeId: 'store',
|
|
72
|
+
cursor: Option.none(),
|
|
73
|
+
live: true,
|
|
74
|
+
}).pipe(Stream.runDrain, Effect.forkChild);
|
|
75
|
+
yield* Effect.addFinalizer(() => Fiber.interrupt(pullFiber));
|
|
76
|
+
yield* Deferred.await(finalized).pipe(Effect.timeout('1 second'));
|
|
77
|
+
Vitest.expect(ended).toContain('RpcServer.SyncWsRpc.Pull');
|
|
78
|
+
Vitest.expect(pullFiber.pollUnsafe()).toBeUndefined();
|
|
79
|
+
}));
|
|
80
|
+
Vitest.effect('installs telemetry inside the Durable Object RPC stream runtime', () => Effect.gen(function* () {
|
|
81
|
+
const { layer, ended } = makeRecordingTracer();
|
|
82
|
+
const observability = makeObservability(layer);
|
|
83
|
+
const input = makeDoInput();
|
|
84
|
+
const protocol = layerProtocolDurableObject({
|
|
85
|
+
callerContext: { bindingName: 'TEST', durableObjectId: 'client' },
|
|
86
|
+
callRpc: (payload) => createDoRpcHandler({
|
|
87
|
+
// oxlint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- Effect RPC owns this ArrayBuffer-backed payload.
|
|
88
|
+
payload: payload,
|
|
89
|
+
input,
|
|
90
|
+
observability,
|
|
91
|
+
}).pipe(Effect.runPromise),
|
|
92
|
+
});
|
|
93
|
+
const responses = yield* Effect.gen(function* () {
|
|
94
|
+
const client = yield* RpcClient.make(SyncDoRpc);
|
|
95
|
+
return yield* client['SyncDoRpc.Pull']({ storeId: 'store', cursor: Option.none() }).pipe(Stream.runCollect);
|
|
96
|
+
}).pipe(Effect.provide(protocol));
|
|
97
|
+
Vitest.expect(responses).toHaveLength(1);
|
|
98
|
+
Vitest.expect(ended).toContain('RpcServer.SyncDoRpc.Pull');
|
|
99
|
+
}));
|
|
100
|
+
Vitest.effect('attaches a finite span to a sampled caller across an unexported RPC envelope', () => Effect.gen(function* () {
|
|
101
|
+
const { layer, spans, ended } = makeRecordingTracer();
|
|
102
|
+
const observability = makeObservability(layer);
|
|
103
|
+
const caller = Tracer.externalSpan({ traceId: '1'.repeat(32), spanId: '2222222222222222', sampled: true });
|
|
104
|
+
yield* Effect.flatMap(rpcSpanOptions, (options) => withRpcSpan(Effect.void.pipe(Effect.withSpan('push')), 'finite-rpc', options)).pipe(observability.effect, Effect.withSpan('unexported-rpc-envelope'), Effect.withParentSpan(caller));
|
|
105
|
+
Vitest.expect(spans.get('finite-rpc')?.parentId).toBe(caller.spanId);
|
|
106
|
+
Vitest.expect(spans.get('push')?.parentId).toBe(spans.get('finite-rpc')?.spanId);
|
|
107
|
+
Vitest.expect(ended).toEqual(['push', 'finite-rpc']);
|
|
108
|
+
}));
|
|
109
|
+
Vitest.effect('starts a backend root when the RPC caller is unsampled', () => Effect.gen(function* () {
|
|
110
|
+
const { layer, spans, ended } = makeRecordingTracer();
|
|
111
|
+
const observability = makeObservability(layer);
|
|
112
|
+
const caller = Tracer.externalSpan({ traceId: '1'.repeat(32), spanId: '2222222222222222', sampled: false });
|
|
113
|
+
yield* Effect.flatMap(rpcSpanOptions, (options) => withRpcSpan(Effect.void, 'finite-rpc', options)).pipe(observability.effect, Effect.withSpan('unexported-rpc-envelope'), Effect.withParentSpan(caller));
|
|
114
|
+
Vitest.expect(spans.get('finite-rpc')?.parentId).toBeUndefined();
|
|
115
|
+
Vitest.expect(ended).toEqual(['finite-rpc']);
|
|
116
|
+
}));
|
|
117
|
+
Vitest.effect('keeps anticipated RPC recovery failures out of boundary error telemetry', () => Effect.gen(function* () {
|
|
118
|
+
const { layer, statuses } = makeRecordingTracer();
|
|
119
|
+
const observability = makeObservability(layer);
|
|
120
|
+
const expected = new BackendIdMismatchError({ expected: 'backend-a', received: 'backend-b' });
|
|
121
|
+
const unexpected = new UnknownError({ cause: new Error('boom') });
|
|
122
|
+
const effectError = yield* withRpcSpan(Effect.fail(expected), 'expected-effect').pipe(observability.effect, Effect.flip);
|
|
123
|
+
const streamError = yield* Stream.fail(expected).pipe((_) => withRpcStreamSpan(_, 'expected-stream'), observability.stream, Stream.runDrain, Effect.flip);
|
|
124
|
+
const unexpectedError = yield* withRpcSpan(Effect.fail(unexpected), 'unexpected-effect').pipe(observability.effect, Effect.flip);
|
|
125
|
+
Vitest.expect(effectError).toBe(expected);
|
|
126
|
+
Vitest.expect(streamError).toBe(expected);
|
|
127
|
+
Vitest.expect(unexpectedError).toBe(unexpected);
|
|
128
|
+
Vitest.expect(statuses.get('expected-effect')).toBe(SpanStatusCode.OK);
|
|
129
|
+
Vitest.expect(statuses.get('expected-stream')).toBe(SpanStatusCode.OK);
|
|
130
|
+
Vitest.expect(statuses.get('unexpected-effect')).toBe(SpanStatusCode.ERROR);
|
|
131
|
+
}));
|
|
132
|
+
Vitest.effect('forwards the operation exit to supplied layer finalizers', () => Effect.gen(function* () {
|
|
133
|
+
const released = yield* Deferred.make();
|
|
134
|
+
const expected = new BackendIdMismatchError({ expected: 'backend-a', received: 'backend-b' });
|
|
135
|
+
const observability = makeObservability(Layer.effectDiscard(Effect.acquireRelease(Effect.void, (_, exit) => Deferred.succeed(released, exit._tag).pipe(Effect.asVoid))));
|
|
136
|
+
const error = yield* Effect.fail(expected).pipe(observability.effect, Effect.flip);
|
|
137
|
+
Vitest.expect(error).toBe(expected);
|
|
138
|
+
Vitest.expect(yield* Deferred.await(released)).toBe('Failure');
|
|
139
|
+
}));
|
|
140
|
+
Vitest.live('acknowledges before the owned OTLP collector responds', () => Effect.gen(function* () {
|
|
141
|
+
const started = Promise.withResolvers();
|
|
142
|
+
const gate = Promise.withResolvers();
|
|
143
|
+
const acknowledged = yield* Deferred.make();
|
|
144
|
+
const fetch = Vitest.vi.fn(() => {
|
|
145
|
+
started.resolve();
|
|
146
|
+
return gate.promise.then(() => new Response('{}', { headers: { 'content-type': 'application/json' } }));
|
|
147
|
+
});
|
|
148
|
+
yield* Effect.addFinalizer(() => Effect.sync(() => gate.resolve()));
|
|
149
|
+
const observability = makeObservability({ baseUrl: 'http://collector.invalid' });
|
|
150
|
+
yield* Effect.void.pipe(Effect.withSpan('push'), observability.effect, Effect.andThen(Deferred.succeed(acknowledged, undefined)), Effect.provideService(FetchHttpClient.Fetch, fetch), Effect.forkChild);
|
|
151
|
+
yield* Effect.promise(() => started.promise);
|
|
152
|
+
yield* Effect.yieldNow;
|
|
153
|
+
Vitest.expect(yield* Deferred.isDone(acknowledged)).toBe(true);
|
|
154
|
+
}));
|
|
155
|
+
});
|
|
156
|
+
/** Record the public OTel boundary; span IDs are compared relationally, not fixed to creation order. */
|
|
157
|
+
const makeRecordingTracer = () => {
|
|
158
|
+
const tracer = makeNoopTracer();
|
|
159
|
+
const ended = [];
|
|
160
|
+
const statuses = new Map();
|
|
161
|
+
const spans = new Map();
|
|
162
|
+
let nextId = 0;
|
|
163
|
+
Vitest.vi.spyOn(tracer, 'startSpan').mockImplementation((name, _options, context) => {
|
|
164
|
+
const spanId = (++nextId).toString(16).padStart(16, '0');
|
|
165
|
+
spans.set(name, { spanId, parentId: trace.getSpanContext(context ?? ROOT_CONTEXT)?.spanId });
|
|
166
|
+
const noopSpan = makeNoopSpan();
|
|
167
|
+
return {
|
|
168
|
+
...noopSpan,
|
|
169
|
+
spanContext: () => ({ traceId: '1'.repeat(32), spanId, traceFlags: 1 }),
|
|
170
|
+
setStatus: (status) => {
|
|
171
|
+
statuses.set(name, status.code);
|
|
172
|
+
return noopSpan;
|
|
173
|
+
},
|
|
174
|
+
end: () => {
|
|
175
|
+
ended.push(name);
|
|
176
|
+
},
|
|
177
|
+
};
|
|
178
|
+
});
|
|
179
|
+
const layer = OtelTracer.layerWithoutOtelTracer.pipe(Layer.provide(Layer.succeed(OtelTracer.OtelTracer, tracer)));
|
|
180
|
+
return { layer, ended, spans, statuses };
|
|
181
|
+
};
|
|
182
|
+
/** Minimal empty SQLite-backed Durable Object boundary for transport composition tests. */
|
|
183
|
+
const makeDoInput = () => {
|
|
184
|
+
const emptyCursor = Object.assign([], { toArray: () => [] });
|
|
185
|
+
const doSelf = {
|
|
186
|
+
env: {},
|
|
187
|
+
ctx: {
|
|
188
|
+
storage: {
|
|
189
|
+
sql: { exec: () => emptyCursor },
|
|
190
|
+
kv: { get: () => undefined, put: () => undefined, delete: () => undefined },
|
|
191
|
+
deleteAll: async () => undefined,
|
|
192
|
+
},
|
|
193
|
+
},
|
|
194
|
+
};
|
|
195
|
+
// oxlint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- The test intentionally stubs the narrow Worker APIs reached by an empty pull.
|
|
196
|
+
return { doSelf: doSelf, doOptions: undefined };
|
|
197
|
+
};
|
|
198
|
+
const makeWebSocket = () => {
|
|
199
|
+
const attachment = Schema.encodeSync(WebSocketAttachmentSchema)({ storeId: 'store', pullRequestIds: [] });
|
|
200
|
+
const ws = { deserializeAttachment: () => attachment, close: Vitest.vi.fn() };
|
|
201
|
+
// oxlint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- The handler only reads the attachment and closes invalid sockets.
|
|
202
|
+
return ws;
|
|
203
|
+
};
|
|
204
|
+
//# sourceMappingURL=observability.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"observability.test.js","sourceRoot":"","sources":["../../../src/cf-worker/do/observability.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,10 +2,10 @@ import { BackendIdMismatchError, UnknownError } from '@livestore/common';
|
|
|
2
2
|
import { Schema, Stream } from '@livestore/utils/effect';
|
|
3
3
|
import { SyncMessage } from '../../common/mod.ts';
|
|
4
4
|
import type { ForwardedHeaders } from '../shared.ts';
|
|
5
|
-
import
|
|
5
|
+
import * as DoCtx from './layer.ts';
|
|
6
6
|
export declare const makeEndingPullStream: ({ req, payload, headers, }: {
|
|
7
7
|
req: SyncMessage.PullRequest;
|
|
8
|
-
payload: Schema.
|
|
8
|
+
payload: Schema.Json | undefined;
|
|
9
9
|
headers: ForwardedHeaders | undefined;
|
|
10
|
-
}) => Stream.Stream<SyncMessage.PullResponse, UnknownError | BackendIdMismatchError, DoCtx>;
|
|
10
|
+
}) => Stream.Stream<SyncMessage.PullResponse, UnknownError | BackendIdMismatchError, DoCtx.DoCtx>;
|
|
11
11
|
//# sourceMappingURL=pull.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pull.d.ts","sourceRoot":"","sources":["../../../src/cf-worker/do/pull.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAe,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAErF,OAAO,
|
|
1
|
+
{"version":3,"file":"pull.d.ts","sourceRoot":"","sources":["../../../src/cf-worker/do/pull.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAe,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAErF,OAAO,EAAkB,MAAM,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAA;AAGxE,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AACjD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AACpD,OAAO,KAAK,KAAK,MAAM,YAAY,CAAA;AAanC,eAAO,MAAM,oBAAoB,+BAI9B;IACD,GAAG,EAAE,WAAW,CAAC,WAAW,CAAA;IAC5B,OAAO,EAAE,MAAM,CAAC,IAAI,GAAG,SAAS,CAAA;IAChC,OAAO,EAAE,gBAAgB,GAAG,SAAS,CAAA;CACtC,KAAG,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,YAAY,EAAE,YAAY,GAAG,sBAAsB,EAAE,KAAK,CAAC,KAAK,CAiE3F,CAAA"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { BackendIdMismatchError, SyncBackend, UnknownError } from '@livestore/common';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { MAX_PULL_EVENTS_PER_MESSAGE, MAX_WS_MESSAGE_BYTES } from
|
|
5
|
-
import { SyncMessage } from
|
|
6
|
-
import
|
|
2
|
+
import { splitArrayBySize } from '@livestore/common/sync';
|
|
3
|
+
import { Effect, Option, Schema, Stream } from '@livestore/utils/effect';
|
|
4
|
+
import { MAX_PULL_EVENTS_PER_MESSAGE, MAX_WS_MESSAGE_BYTES } from '../../common/constants.js';
|
|
5
|
+
import { SyncMessage } from '../../common/mod.js';
|
|
6
|
+
import * as DoCtx from './layer.js';
|
|
7
7
|
const encodePullResponse = Schema.encodeSync(SyncMessage.PullResponse);
|
|
8
8
|
// Notes on stream handling:
|
|
9
9
|
// We're intentionally closing the stream once we've read all existing events
|
|
@@ -15,9 +15,9 @@ const encodePullResponse = Schema.encodeSync(SyncMessage.PullResponse);
|
|
|
15
15
|
// - Further chunks will be emitted manually in `push.ts`
|
|
16
16
|
// - If the client sends a `Interrupt` RPC message, TODO
|
|
17
17
|
export const makeEndingPullStream = ({ req, payload, headers, }) => Effect.gen(function* () {
|
|
18
|
-
const { doOptions, backendId, storeId, storage } = yield* DoCtx;
|
|
18
|
+
const { doOptions, backendId, storeId, storage } = yield* DoCtx.DoCtx;
|
|
19
19
|
if (doOptions?.onPull !== undefined) {
|
|
20
|
-
yield* Effect.
|
|
20
|
+
yield* Effect.trySyncOrPromiseOrEffect(() => doOptions.onPull(req, {
|
|
21
21
|
storeId,
|
|
22
22
|
...(payload !== undefined ? { payload } : {}),
|
|
23
23
|
...(headers !== undefined ? { headers } : {}),
|
|
@@ -27,27 +27,26 @@ export const makeEndingPullStream = ({ req, payload, headers, }) => Effect.gen(f
|
|
|
27
27
|
return yield* new BackendIdMismatchError({ expected: backendId, received: req.cursor.value.backendId });
|
|
28
28
|
}
|
|
29
29
|
const { stream: storedEvents, total } = yield* storage.getEvents(Option.getOrUndefined(req.cursor)?.eventSequenceNumber);
|
|
30
|
-
return storedEvents.pipe(Stream.
|
|
30
|
+
return storedEvents.pipe(Stream.mapArrayEffect(splitArrayBySize({
|
|
31
31
|
maxItems: MAX_PULL_EVENTS_PER_MESSAGE,
|
|
32
32
|
maxBytes: MAX_WS_MESSAGE_BYTES,
|
|
33
33
|
encode: (batch) => encodePullResponse(SyncMessage.PullResponse.make({ batch, pageInfo: SyncBackend.pageInfoNoMore, backendId })),
|
|
34
|
-
})), Stream.mapAccum(total, (remaining,
|
|
35
|
-
const
|
|
36
|
-
const nextRemaining = Math.max(0, remaining - asArray.length);
|
|
34
|
+
})), Stream.mapAccum(() => total, (remaining, array) => {
|
|
35
|
+
const nextRemaining = Math.max(0, remaining - array.length);
|
|
37
36
|
return [
|
|
38
37
|
nextRemaining,
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
38
|
+
[
|
|
39
|
+
SyncMessage.PullResponse.make({
|
|
40
|
+
batch: array,
|
|
41
|
+
pageInfo: nextRemaining > 0 ? SyncBackend.pageInfoMoreKnown(nextRemaining) : SyncBackend.pageInfoNoMore,
|
|
42
|
+
backendId,
|
|
43
|
+
}),
|
|
44
|
+
],
|
|
44
45
|
];
|
|
45
46
|
}), Stream.tap(Effect.fn(function* (res) {
|
|
46
47
|
if (doOptions?.onPullRes !== undefined) {
|
|
47
|
-
yield* Effect.
|
|
48
|
+
yield* Effect.trySyncOrPromiseOrEffect(() => doOptions.onPullRes(res)).pipe(UnknownError.mapToUnknownError);
|
|
48
49
|
}
|
|
49
50
|
})), Stream.emitIfEmpty(SyncMessage.emptyPullResponse(backendId)));
|
|
50
|
-
}).pipe(Stream.unwrap, Stream.mapError((cause) => cause._tag === 'BackendIdMismatchError' || cause._tag === 'UnknownError'
|
|
51
|
-
? cause
|
|
52
|
-
: new UnknownError({ cause })), Stream.withSpan('cloudflare-provider:pull'));
|
|
51
|
+
}).pipe(Stream.unwrap, Stream.mapError((cause) => cause._tag === 'BackendIdMismatchError' || cause._tag === 'UnknownError' ? cause : new UnknownError({ cause })), Stream.withSpan('cloudflare-provider:pull'));
|
|
53
52
|
//# sourceMappingURL=pull.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pull.js","sourceRoot":"","sources":["../../../src/cf-worker/do/pull.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AACrF,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AACzD,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"pull.js","sourceRoot":"","sources":["../../../src/cf-worker/do/pull.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,13 +3,26 @@ import { type CfTypes } from '@livestore/common-cf';
|
|
|
3
3
|
import { Effect, Schema } from '@livestore/utils/effect';
|
|
4
4
|
import { SyncMessage } from '../../common/mod.ts';
|
|
5
5
|
import { type Env, type ForwardedHeaders, type MakeDurableObjectClassOptions, type StoreId } from '../shared.ts';
|
|
6
|
-
import
|
|
6
|
+
import * as DoCtx from './layer.ts';
|
|
7
7
|
export declare const makePush: ({ payload, headers, options, storeId, ctx, env, }: {
|
|
8
|
-
payload: Schema.
|
|
8
|
+
payload: Schema.Json | undefined;
|
|
9
9
|
headers: ForwardedHeaders | undefined;
|
|
10
10
|
options: MakeDurableObjectClassOptions | undefined;
|
|
11
11
|
storeId: StoreId;
|
|
12
12
|
ctx: CfTypes.DurableObjectState;
|
|
13
13
|
env: Env;
|
|
14
|
-
}) => (pushRequest: Omit<SyncMessage.PushRequest,
|
|
14
|
+
}) => (pushRequest: Omit<SyncMessage.PushRequest, '_tag'>) => Effect.Effect<{}, BackendIdMismatchError | ServerAheadError | UnknownError, DoCtx.DoCtx>;
|
|
15
|
+
/**
|
|
16
|
+
* Serializes the complete push admission lifecycle, including publication to pull subscribers.
|
|
17
|
+
*
|
|
18
|
+
* This complements `blockConcurrencyWhile`: that Cloudflare primitive makes the durable state transition
|
|
19
|
+
* atomic with respect to other Durable Object requests, whereas this semaphore preserves push order after
|
|
20
|
+
* the storage gate is released and broadcasting begins. Keeping broadcast outside `blockConcurrencyWhile`
|
|
21
|
+
* avoids holding Cloudflare's concurrency gate across network I/O.
|
|
22
|
+
*
|
|
23
|
+
* Once admitted, the effect is uninterruptible so cancellation cannot leave a committed head without its
|
|
24
|
+
* matching pull response. Such a gap would make later pushers receive `ServerAhead` while waiting for a
|
|
25
|
+
* response that was never published.
|
|
26
|
+
*/
|
|
27
|
+
export declare const runSerializedPushAdmission: <A, E, R>(semaphore: DoCtx.Service['pushSemaphore'], admission: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>;
|
|
15
28
|
//# sourceMappingURL=push.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"push.d.ts","sourceRoot":"","sources":["../../../src/cf-worker/do/push.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"push.d.ts","sourceRoot":"","sources":["../../../src/cf-worker/do/push.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,gBAAgB,EAAe,YAAY,EAAE,MAAM,mBAAmB,CAAA;AACvG,OAAO,EAAE,KAAK,OAAO,EAAsB,MAAM,sBAAsB,CAAA;AAEvE,OAAO,EAAE,MAAM,EAAyD,MAAM,EAAE,MAAM,yBAAyB,CAAA;AAG/G,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AACjD,OAAO,EACL,KAAK,GAAG,EACR,KAAK,gBAAgB,EACrB,KAAK,6BAA6B,EAGlC,KAAK,OAAO,EAEb,MAAM,cAAc,CAAA;AACrB,OAAO,KAAK,KAAK,MAAM,YAAY,CAAA;AAQnC,eAAO,MAAM,QAAQ,sDAQhB;IACD,OAAO,EAAE,MAAM,CAAC,IAAI,GAAG,SAAS,CAAA;IAChC,OAAO,EAAE,gBAAgB,GAAG,SAAS,CAAA;IACrC,OAAO,EAAE,6BAA6B,GAAG,SAAS,CAAA;IAClD,OAAO,EAAE,OAAO,CAAA;IAChB,GAAG,EAAE,OAAO,CAAC,kBAAkB,CAAA;IAC/B,GAAG,EAAE,GAAG,CAAA;CACT,mBACa,IAAI,CAAC,WAAW,CAAC,WAAW,EAAE,MAAM,CAAC,6FA2KhD,CAAA;AAEL;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,0BAA0B,GAAI,CAAC,EAAE,CAAC,EAAE,CAAC,aACrC,KAAK,CAAC,OAAO,CAAC,eAAe,CAAC,aAC9B,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,KAChC,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAqE,CAAA"}
|
|
@@ -1,21 +1,22 @@
|
|
|
1
|
-
import { BackendIdMismatchError, ServerAheadError, SyncBackend, UnknownError
|
|
1
|
+
import { BackendIdMismatchError, ServerAheadError, SyncBackend, UnknownError } from '@livestore/common';
|
|
2
2
|
import { emitStreamResponse } from '@livestore/common-cf';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { MAX_PUSH_EVENTS_PER_REQUEST, MAX_WS_MESSAGE_BYTES } from
|
|
6
|
-
import { SyncMessage } from
|
|
7
|
-
import { WebSocketAttachmentSchema, } from
|
|
8
|
-
import
|
|
9
|
-
const
|
|
10
|
-
const
|
|
3
|
+
import { splitArrayBySize } from '@livestore/common/sync';
|
|
4
|
+
import { Effect, Option, ReadonlyArray as EffectArray, Schema } from '@livestore/utils/effect';
|
|
5
|
+
import { MAX_PUSH_EVENTS_PER_REQUEST, MAX_WS_MESSAGE_BYTES } from '../../common/constants.js';
|
|
6
|
+
import { SyncMessage } from '../../common/mod.js';
|
|
7
|
+
import { rpcSubscriptionKeyPrefix, WebSocketAttachmentSchema, } from '../shared.js';
|
|
8
|
+
import * as DoCtx from './layer.js';
|
|
9
|
+
const pullResponseJsonSchema = Schema.toCodecJson(SyncMessage.PullResponse);
|
|
10
|
+
const encodePullResponse = Schema.encodeSync(pullResponseJsonSchema);
|
|
11
|
+
const jsonStringify = Schema.encodeSync(Schema.fromJsonString(Schema.Unknown));
|
|
11
12
|
export const makePush = ({ payload, headers, options, storeId, ctx, env, }) => (pushRequest) => Effect.gen(function* () {
|
|
12
13
|
// yield* Effect.log(`Pushing ${decodedMessage.batch.length} events`, decodedMessage.batch)
|
|
13
|
-
const { backendId, storage, currentHeadRef, updateCurrentHead,
|
|
14
|
+
const { backendId, storage, currentHeadRef, updateCurrentHead, pushSemaphore } = yield* DoCtx.DoCtx;
|
|
14
15
|
if (pushRequest.batch.length === 0) {
|
|
15
16
|
return SyncMessage.PushAck.make({});
|
|
16
17
|
}
|
|
17
18
|
if (options?.onPush !== undefined) {
|
|
18
|
-
yield* Effect.
|
|
19
|
+
yield* Effect.trySyncOrPromiseOrEffect(() => options.onPush(pushRequest, {
|
|
19
20
|
storeId,
|
|
20
21
|
...(payload !== undefined ? { payload } : {}),
|
|
21
22
|
...(headers !== undefined ? { headers } : {}),
|
|
@@ -24,32 +25,32 @@ export const makePush = ({ payload, headers, options, storeId, ctx, env, }) => (
|
|
|
24
25
|
if (pushRequest.backendId._tag === 'Some' && pushRequest.backendId.value !== backendId) {
|
|
25
26
|
return yield* new BackendIdMismatchError({ expected: backendId, received: pushRequest.backendId.value });
|
|
26
27
|
}
|
|
27
|
-
//
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
//
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
//
|
|
36
|
-
//
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}).pipe(blockConcurrencyWhile(ctx));
|
|
48
|
-
// Run in background but already return the push ack to the client
|
|
49
|
-
yield* Effect.gen(function* () {
|
|
28
|
+
// A push is one ordered admit → persist → publish operation. `blockConcurrencyWhile` below protects
|
|
29
|
+
// the Durable Object state transition, while this semaphore keeps later pushes from overtaking this
|
|
30
|
+
// push during broadcasting without holding Cloudflare's concurrency gate across network I/O.
|
|
31
|
+
yield* runSerializedPushAdmission(pushSemaphore, Effect.gen(function* () {
|
|
32
|
+
// Keep the head check, durable append, and in-memory head update atomic with respect to other
|
|
33
|
+
// Durable Object requests. Broadcasting intentionally happens after this gate is released.
|
|
34
|
+
const { createdAt } = yield* Effect.gen(function* () {
|
|
35
|
+
const currentHead = currentHeadRef.current;
|
|
36
|
+
// TODO handle clientId unique conflict
|
|
37
|
+
// Validate the batch
|
|
38
|
+
const firstEventParent = pushRequest.batch[0].parentSeqNum;
|
|
39
|
+
if (firstEventParent !== currentHead) {
|
|
40
|
+
return yield* new ServerAheadError({ minimumExpectedNum: currentHead, providedNum: firstEventParent });
|
|
41
|
+
}
|
|
42
|
+
const createdAt = new Date().toISOString();
|
|
43
|
+
// TODO possibly model this as a queue in order to speed up subsequent pushes
|
|
44
|
+
yield* storage.appendEvents(pushRequest.batch, createdAt);
|
|
45
|
+
updateCurrentHead(pushRequest.batch.at(-1).seqNum);
|
|
46
|
+
return { createdAt };
|
|
47
|
+
}).pipe(blockConcurrencyWhile(ctx));
|
|
50
48
|
const connectedClients = ctx.getWebSockets();
|
|
51
49
|
// Preparing chunks of responses to make sure we don't exceed the WS message size limit.
|
|
52
|
-
|
|
50
|
+
if (EffectArray.isReadonlyArrayNonEmpty(pushRequest.batch) === false) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const responses = yield* splitArrayBySize({
|
|
53
54
|
maxItems: MAX_PUSH_EVENTS_PER_REQUEST,
|
|
54
55
|
maxBytes: MAX_WS_MESSAGE_BYTES,
|
|
55
56
|
encode: (items) => encodePullResponse(SyncMessage.PullResponse.make({
|
|
@@ -60,8 +61,8 @@ export const makePush = ({ payload, headers, options, storeId, ctx, env, }) => (
|
|
|
60
61
|
pageInfo: SyncBackend.pageInfoNoMore,
|
|
61
62
|
backendId,
|
|
62
63
|
})),
|
|
63
|
-
})
|
|
64
|
-
const batchWithMetadata =
|
|
64
|
+
})(pushRequest.batch).pipe(Effect.map((eventBatch) => eventBatch.map((events) => {
|
|
65
|
+
const batchWithMetadata = events.map((eventEncoded) => ({
|
|
65
66
|
eventEncoded,
|
|
66
67
|
metadata: Option.some(SyncMessage.SyncMetadata.make({ createdAt })),
|
|
67
68
|
}));
|
|
@@ -72,7 +73,7 @@ export const makePush = ({ payload, headers, options, storeId, ctx, env, }) => (
|
|
|
72
73
|
});
|
|
73
74
|
return {
|
|
74
75
|
response,
|
|
75
|
-
encoded:
|
|
76
|
+
encoded: encodePullResponse(response),
|
|
76
77
|
};
|
|
77
78
|
})));
|
|
78
79
|
// Dual broadcasting: WebSocket + RPC clients
|
|
@@ -81,11 +82,11 @@ export const makePush = ({ payload, headers, options, storeId, ctx, env, }) => (
|
|
|
81
82
|
for (const { response, encoded } of responses) {
|
|
82
83
|
// Only calling once for now.
|
|
83
84
|
if (options?.onPullRes !== undefined) {
|
|
84
|
-
yield* Effect.
|
|
85
|
+
yield* Effect.trySyncOrPromiseOrEffect(() => options.onPullRes(response)).pipe(UnknownError.mapToUnknownError);
|
|
85
86
|
}
|
|
86
87
|
// NOTE we're also sending the pullRes chunk to the pushing ws client as confirmation
|
|
87
88
|
for (const conn of connectedClients) {
|
|
88
|
-
const attachment = yield* Schema.
|
|
89
|
+
const attachment = yield* Schema.decodeEffect(WebSocketAttachmentSchema)(conn.deserializeAttachment());
|
|
89
90
|
// We're doing something a bit "advanced" here as we're directly emitting Effect RPC-compatible
|
|
90
91
|
// response messsages on the Effect RPC-managed websocket connection to the WS client.
|
|
91
92
|
// For this we need to get the RPC `requestId` from the WebSocket attachment.
|
|
@@ -101,38 +102,58 @@ export const makePush = ({ payload, headers, options, storeId, ctx, env, }) => (
|
|
|
101
102
|
}
|
|
102
103
|
yield* Effect.logDebug(`Broadcasted to ${connectedClients.length} WebSocket clients`);
|
|
103
104
|
}
|
|
104
|
-
//
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
105
|
+
// Subscribers live in the DO's KV storage (single source of truth), so fan-out survives reconstruction.
|
|
106
|
+
// emitStreamResponse reconstructs each client stub from its callerContext.
|
|
107
|
+
const rpcSubscriptions = Array.from(ctx.storage.kv.list({ prefix: rpcSubscriptionKeyPrefix }));
|
|
108
|
+
if (rpcSubscriptions.length > 0) {
|
|
109
|
+
for (const [, subscription] of rpcSubscriptions) {
|
|
110
|
+
for (const { response } of responses) {
|
|
108
111
|
yield* emitStreamResponse({
|
|
109
112
|
callerContext: subscription.callerContext,
|
|
110
113
|
env,
|
|
111
114
|
requestId: subscription.requestId,
|
|
112
|
-
|
|
115
|
+
storeId: subscription.storeId,
|
|
116
|
+
schema: SyncMessage.PullResponse,
|
|
117
|
+
values: [response],
|
|
113
118
|
}).pipe(Effect.tapCauseLogPretty, Effect.exit);
|
|
114
119
|
}
|
|
115
120
|
}
|
|
116
|
-
yield* Effect.logDebug(`Broadcasted to ${rpcSubscriptions.
|
|
121
|
+
yield* Effect.logDebug(`Broadcasted to ${rpcSubscriptions.length} RPC clients`);
|
|
117
122
|
}
|
|
118
|
-
}).pipe(Effect.tapCauseLogPretty, Effect.withSpan('push-rpc-broadcast')
|
|
119
|
-
|
|
120
|
-
//
|
|
121
|
-
yield* Effect.yieldNow();
|
|
123
|
+
}).pipe(Effect.tapCauseLogPretty, Effect.withSpan('push-rpc-broadcast')));
|
|
124
|
+
// Acknowledge only after the committed batch has been published, so a client cannot observe an
|
|
125
|
+
// advanced server head while the corresponding pull response is still waiting in a detached fiber.
|
|
122
126
|
return SyncMessage.PushAck.make({});
|
|
123
127
|
}).pipe(Effect.tap(Effect.fn(function* (message) {
|
|
124
128
|
if (options?.onPushRes !== undefined) {
|
|
125
|
-
yield* Effect.
|
|
129
|
+
yield* Effect.trySyncOrPromiseOrEffect(() => options.onPushRes(message)).pipe(UnknownError.mapToUnknownError);
|
|
126
130
|
}
|
|
127
131
|
})), Effect.mapError((cause) => cause._tag === 'BackendIdMismatchError' || cause._tag === 'ServerAheadError' || cause._tag === 'UnknownError'
|
|
128
132
|
? cause
|
|
129
133
|
: new UnknownError({ cause })), Effect.withSpan('sync-cf:do:push', { attributes: { storeId, batchSize: pushRequest.batch.length } }));
|
|
130
134
|
/**
|
|
135
|
+
* Serializes the complete push admission lifecycle, including publication to pull subscribers.
|
|
136
|
+
*
|
|
137
|
+
* This complements `blockConcurrencyWhile`: that Cloudflare primitive makes the durable state transition
|
|
138
|
+
* atomic with respect to other Durable Object requests, whereas this semaphore preserves push order after
|
|
139
|
+
* the storage gate is released and broadcasting begins. Keeping broadcast outside `blockConcurrencyWhile`
|
|
140
|
+
* avoids holding Cloudflare's concurrency gate across network I/O.
|
|
141
|
+
*
|
|
142
|
+
* Once admitted, the effect is uninterruptible so cancellation cannot leave a committed head without its
|
|
143
|
+
* matching pull response. Such a gap would make later pushers receive `ServerAhead` while waiting for a
|
|
144
|
+
* response that was never published.
|
|
145
|
+
*/
|
|
146
|
+
export const runSerializedPushAdmission = (semaphore, admission) => semaphore.withPermits(1)(admission.pipe(Effect.uninterruptible));
|
|
147
|
+
/**
|
|
148
|
+
* Runs the storage-backed state transition behind Cloudflare's Durable Object concurrency gate.
|
|
149
|
+
* This scope should remain limited to local persistence and head mutation; ordered publication is handled
|
|
150
|
+
* separately by `runSerializedPushAdmission` so outbound work does not block unrelated Durable Object events.
|
|
151
|
+
*
|
|
131
152
|
* @see https://developers.cloudflare.com/durable-objects/api/state/#blockconcurrencywhile
|
|
132
153
|
*/
|
|
133
154
|
const blockConcurrencyWhile = (ctx) => (eff) => Effect.gen(function* () {
|
|
134
|
-
const
|
|
135
|
-
const exit = yield* Effect.promise(() => ctx.blockConcurrencyWhile(() => eff.pipe(Effect.
|
|
155
|
+
const services = yield* Effect.context();
|
|
156
|
+
const exit = yield* Effect.promise(() => ctx.blockConcurrencyWhile(() => eff.pipe(Effect.runPromiseExitWith(services))));
|
|
136
157
|
return yield* exit;
|
|
137
158
|
});
|
|
138
159
|
//# sourceMappingURL=push.js.map
|