@beignet/provider-event-bus-redis 0.0.37 → 0.0.39

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/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # @beignet/provider-event-bus-redis
2
2
 
3
+ ## 0.0.39
4
+
5
+ ### Patch Changes
6
+
7
+ - 019db95: Report Redis publish, connection, and subscription failures consistently,
8
+ retry desired subscriptions after transient command failures, preserve visible
9
+ connection-error fallback logging, isolate rejected error callbacks, and
10
+ document the provider's proven reconnect and deployment semantics.
11
+
12
+ ## 0.0.38
13
+
14
+ ### Patch Changes
15
+
16
+ - 1081ac6: Harden HTTP response handling, synchronous upload cleanup, CLI path and registry diagnostics, provider context preservation, Redis subscription observability, and provider documentation.
17
+ - d23b0b3: Continue versioned W3C trace context through outbox, event bus, BullMQ, and
18
+ Inngest boundaries without making telemetry metadata part of delivery success.
19
+
3
20
  ## 0.0.37
4
21
 
5
22
  ## 0.0.36
package/README.md CHANGED
@@ -34,7 +34,7 @@ beignet providers add event-bus-redis
34
34
  ```typescript
35
35
  import { createNextServer, createNextServerLoader } from "@beignet/next";
36
36
  import { createRedisEventBusProvider } from "@beignet/provider-event-bus-redis";
37
- import { appPorts } from "@/infra/app-ports";
37
+ import { initialPorts } from "@/infra/port-wiring";
38
38
  import { routes } from "@/server/routes";
39
39
 
40
40
  // Set environment variables:
@@ -43,7 +43,7 @@ import { routes } from "@/server/routes";
43
43
 
44
44
  export const getServer = createNextServerLoader(() =>
45
45
  createNextServer({
46
- ports: appPorts,
46
+ ports: initialPorts,
47
47
  providers: [createRedisEventBusProvider()],
48
48
  context: ({ ports }) => ({
49
49
  ports,
@@ -96,7 +96,32 @@ During startup the provider creates separate publisher and subscriber clients,
96
96
  connects both eagerly, and fails boot with a redacted connection error when
97
97
  Redis is unreachable. The default initial connection retry strategy stops
98
98
  after `REDIS_EVENT_BUS_CONNECT_MAX_ATTEMPTS`; after a successful connection,
99
- ioredis reconnects with capped exponential backoff.
99
+ ioredis reconnects with capped exponential backoff and resubscribes to the
100
+ channels owned by that subscriber client. Events published while the
101
+ subscriber is disconnected are still lost because Redis Pub/Sub has no replay.
102
+ If an initial `SUBSCRIBE` command fails after ioredis exhausts its command
103
+ retries, the adapter retries that desired subscription with capped backoff for
104
+ as long as at least one local handler remains registered.
105
+
106
+ ### Deployment topology
107
+
108
+ The environment-backed provider targets a standalone Redis URL or a managed
109
+ Redis service that exposes one Redis-compatible endpoint. It opens two TCP
110
+ connections per app process: one publisher and one subscriber. Include both in
111
+ the process connection budget.
112
+
113
+ Every long-lived process that should receive events must install the provider
114
+ and register its listeners. Ephemeral request runtimes can publish events, but
115
+ they are not reliable subscriber hosts because Redis only delivers while the
116
+ connection is online. Run subscriptions in a long-lived web or worker process.
117
+
118
+ Sentinel and Redis Cluster require constructor options that cannot be expressed
119
+ by `REDIS_EVENT_BUS_URL`. Apps using those topologies should create their own
120
+ ioredis clients and pass them to `createRedisEventBus(...)`. Beignet's live
121
+ suite does not currently prove Sentinel or Cluster failover semantics, so test
122
+ subscription recovery against the exact managed topology before relying on it.
123
+ HTTP-only Redis APIs are not compatible because Pub/Sub requires a persistent
124
+ Redis connection.
100
125
 
101
126
  ## Direct adapter
102
127
 
@@ -123,13 +148,31 @@ missed during startup races.
123
148
  - `publish(...)` publishes one Redis Pub/Sub message on
124
149
  `<channelPrefix><event.name>`.
125
150
  - Only subscribers that are online and currently subscribed receive the event.
151
+ - ioredis resubscribes after a transient reconnect, but messages published
152
+ during the disconnected interval are not recovered.
126
153
  - Payloads are JSON encoded and delivered to handlers without persisting the
127
154
  message.
155
+ - When a tracing port is available, the envelope includes Beignet's versioned
156
+ trace carrier and subscribers receive it as publish metadata. Registered
157
+ listeners continue the producer trace. Legacy messages without a carrier and
158
+ messages with malformed trace metadata still deliver their payload.
128
159
  - Handler errors are reported through `onHandlerError` when provided and are
129
160
  also recorded as provider instrumentation events; they do not reject the
130
161
  original publisher.
131
162
  - Invalid messages and subscription errors are reported through
132
- `onSubscriberError` when provided.
163
+ `onSubscriberError` when provided and recorded as
164
+ `eventBus.message.failed` or `eventBus.subscription.failed` provider
165
+ instrumentation events. Failed subscriptions retry with capped backoff while
166
+ their local handlers remain registered; removing the last handler cancels a
167
+ pending retry.
168
+ - Failed `publish(...)` calls reject with the Redis error and record an
169
+ `eventBus.publish.failed` instrumentation event. The provider does not retry
170
+ the publish after the configured ioredis command retries are exhausted.
171
+ - When the `eventBus` instrumentation watcher is enabled, publisher and
172
+ subscriber connection errors from env-backed clients are recorded as
173
+ `eventBus.connection.failed`. When instrumentation is absent or disabled,
174
+ Beignet leaves ioredis's error fallback intact so connection failures remain
175
+ visible.
133
176
  - The provider does not retry handlers, store attempts, replay missed events,
134
177
  or dead-letter failures.
135
178
 
@@ -150,12 +193,15 @@ ctx.ports.redisEventBus.channelPrefix;
150
193
  ```
151
194
 
152
195
  `checkHealth()` sends cheap `PING` commands to both Redis clients and returns
153
- metadata suitable for app-owned readiness endpoints.
196
+ metadata suitable for app-owned readiness endpoints. It proves both
197
+ connections can reach Redis; it does not prove that every expected listener
198
+ has registered its channel subscription.
154
199
 
155
200
  ## Instrumentation
156
201
 
157
- The provider records publish events under the `eventBus` watcher. Received
158
- messages and handler failures are recorded as custom provider events. Payloads
202
+ The provider records successful publish events under the `eventBus` watcher.
203
+ Received messages, failed publishes, connection failures, subscription
204
+ failures, and handler failures are recorded as custom provider events. Payloads
159
205
  are not recorded.
160
206
 
161
207
  ## Testing
@@ -166,9 +212,11 @@ the behavior under test specifically depends on cross-process delivery or
166
212
  Redis connection behavior.
167
213
 
168
214
  This package includes opt-in live Redis integration tests for cross-instance
169
- delivery, unsubscribe behavior, readiness, instrumentation, and failure
170
- callbacks. The default `bun run test` command runs the hermetic unit suite only
171
- so the live tests cannot share process state with the mocked `ioredis` tests.
215
+ delivery, reconnect and automatic resubscription, expected loss while offline,
216
+ active-subscription cleanup during shutdown, readiness, instrumentation, and
217
+ failure callbacks. The default `bun run test` command runs the hermetic unit
218
+ suite only so the live tests cannot share process state with the mocked
219
+ `ioredis` tests.
172
220
  Set `REDIS_EVENT_BUS_TEST_URL` or the shared `REDIS_TEST_URL` before running
173
221
  the dedicated live test command:
174
222
 
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAEV,YAAY,EAEb,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,KAAK,uBAAuB,EAG5B,KAAK,6BAA6B,EAClC,KAAK,eAAe,EACrB,MAAM,yBAAyB,CAAC;AAoCjC;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,cAAc,EAAE,MAAM,CAAC;IACvB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,uBAAuB,EAAE,MAAM,CAAC;IAChC,oBAAoB,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,4BAA4B;IAC3C,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;IACpE,IAAI,CAAC,IAAI,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;CACnC;AAED,MAAM,WAAW,6BAA6B;IAC5C,SAAS,CAAC,GAAG,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;IAC7D,WAAW,CAAC,GAAG,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;IAC/D,EAAE,CACA,KAAK,EAAE,SAAS,EAChB,OAAO,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,GAClD,OAAO,CAAC;IACX,GAAG,CAAC,CACF,KAAK,EAAE,SAAS,EAChB,OAAO,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,GAClD,OAAO,CAAC;IACX,cAAc,CAAC,CACb,KAAK,EAAE,SAAS,EAChB,OAAO,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,GAClD,OAAO,CAAC;IACX,IAAI,CAAC,IAAI,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;CACnC;AAED,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE;QACR,OAAO,EAAE,iBAAiB,CAAC;QAC3B,aAAa,EAAE,MAAM,CAAC;QACtB,iBAAiB,CAAC,EAAE,MAAM,CAAC;QAC3B,kBAAkB,CAAC,EAAE,MAAM,CAAC;KAC7B,CAAC;CACH;AAED,MAAM,WAAW,wBAAwB;IACvC,SAAS,EAAE,4BAA4B,CAAC;IACxC,UAAU,EAAE,6BAA6B,CAAC;IAC1C,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,IAAI,OAAO,CAAC,mBAAmB,CAAC,CAAC;CAC7C;AAED,MAAM,WAAW,0BAA0B;IACzC,QAAQ,EAAE,YAAY,CAAC;IACvB,aAAa,EAAE,wBAAwB,CAAC;CACzC;AAED;;GAEG;AACH,MAAM,MAAM,qBAAqB,GAAG,eAAe,CACjD,OAAO,EACP,uBAAuB,CAAC,mBAAmB,CAAC,EAC5C,IAAI,CAAC,0BAA0B,EAAE,MAAM,0BAA0B,CAAC,CACnE,CAAC;AAEF,MAAM,WAAW,0BAA0B;IACzC,SAAS,EAAE,4BAA4B,CAAC;IACxC,UAAU,EAAE,6BAA6B,CAAC;IAC1C,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,eAAe,CAAC,EAAE,6BAA6B,CAAC;IAChD,cAAc,CAAC,EAAE,CACf,KAAK,EAAE,OAAO,EACd,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,OAAO,KACb,IAAI,CAAC;IACV,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CAC9C;AAED,MAAM,WAAW,kCAAkC;IACjD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;IACjD,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AA+ZD,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,0BAA0B,GAClC,YAAY,CAEd;AAED,wBAAgB,2BAA2B,CACzC,OAAO,GAAE,kCAAuC,GAC/C,qBAAqB,CAoKvB"}
package/dist/index.js CHANGED
@@ -5,6 +5,7 @@
5
5
  * cross-process, best-effort domain event delivery.
6
6
  */
7
7
  import { createProvider, createProviderInstrumentation, } from "@beignet/core/providers";
8
+ import { captureTraceCarrier, parseTraceCarrier, } from "@beignet/core/tracing";
8
9
  import { Redis } from "ioredis";
9
10
  import { z } from "zod";
10
11
  const DEFAULT_CONNECT_TIMEOUT_MS = 5000;
@@ -12,6 +13,8 @@ const DEFAULT_MAX_RETRIES_PER_REQUEST = 2;
12
13
  const DEFAULT_CONNECT_MAX_ATTEMPTS = 3;
13
14
  const DEFAULT_CHANNEL_PREFIX = "beignet:event-bus:";
14
15
  const RECONNECT_BACKOFF_CAP_MS = 2000;
16
+ const SUBSCRIPTION_RETRY_BASE_MS = 100;
17
+ const SUBSCRIPTION_RETRY_CAP_MS = 2000;
15
18
  const NumberString = z.union([
16
19
  z
17
20
  .string()
@@ -49,6 +52,9 @@ function redactConnectionUrl(url) {
49
52
  function reconnectBackoff(attempt) {
50
53
  return Math.min(2 ** attempt * 100, RECONNECT_BACKOFF_CAP_MS);
51
54
  }
55
+ function subscriptionRetryBackoff(attempt) {
56
+ return Math.min(SUBSCRIPTION_RETRY_BASE_MS * 2 ** Math.max(0, attempt - 1), SUBSCRIPTION_RETRY_CAP_MS);
57
+ }
52
58
  function createRetryStrategy(connectMaxAttempts) {
53
59
  let connectedOnce = false;
54
60
  return {
@@ -105,20 +111,36 @@ async function checkRedisEventBusHealth(publisher, subscriber, channelPrefix) {
105
111
  };
106
112
  }
107
113
  }
108
- export function createRedisEventBus(options) {
114
+ function createRedisEventBusRuntime(options) {
109
115
  const channelPrefix = options.channelPrefix ?? DEFAULT_CHANNEL_PREFIX;
110
116
  const handlers = new Map();
111
- const subscribedChannels = new Set();
117
+ const subscribedChannels = new Map();
118
+ const subscriptionRetryAttempts = new Map();
119
+ const subscriptionRetryTimers = new Map();
120
+ let stopped = false;
112
121
  const instrumentation = createProviderInstrumentation(options.instrumentation, {
113
122
  providerName: "event-bus-redis",
114
123
  watcher: "eventBus",
115
124
  });
116
- const reportSubscriberError = (error) => {
125
+ const reportSubscriberError = (error, details) => {
126
+ instrumentation.custom({
127
+ name: details.operation === "message"
128
+ ? "eventBus.message.failed"
129
+ : "eventBus.subscription.failed",
130
+ label: details.operation === "message"
131
+ ? "Event message failed"
132
+ : "Event subscription failed",
133
+ summary: details.eventName ?? details.channel ?? "Redis event bus subscriber",
134
+ details: {
135
+ ...details,
136
+ error: errorMessage(error),
137
+ },
138
+ });
117
139
  try {
118
- options.onSubscriberError?.(error);
140
+ Promise.resolve(options.onSubscriberError?.(error)).catch(() => undefined);
119
141
  }
120
142
  catch {
121
- // Error callbacks should not create unhandled rejections.
143
+ // Error callbacks should not replace or create another failure.
122
144
  }
123
145
  };
124
146
  const reportHandlerError = (error, eventName, payload) => {
@@ -132,10 +154,10 @@ export function createRedisEventBus(options) {
132
154
  },
133
155
  });
134
156
  try {
135
- options.onHandlerError?.(error, eventName, payload);
157
+ Promise.resolve(options.onHandlerError?.(error, eventName, payload)).catch(() => undefined);
136
158
  }
137
159
  catch {
138
- // Error callbacks should not create unhandled rejections.
160
+ // Error callbacks should not replace or create another failure.
139
161
  }
140
162
  };
141
163
  const messageHandler = (channel, message) => {
@@ -146,6 +168,7 @@ export function createRedisEventBus(options) {
146
168
  if (!eventHandlers || eventHandlers.size === 0)
147
169
  return;
148
170
  let payload;
171
+ let trace;
149
172
  try {
150
173
  const parsed = safeJsonParse(message);
151
174
  if (typeof parsed !== "object" ||
@@ -153,10 +176,16 @@ export function createRedisEventBus(options) {
153
176
  !("payload" in parsed)) {
154
177
  throw new Error("Redis event bus message is missing a payload field.");
155
178
  }
156
- payload = parsed.payload;
179
+ const envelope = parsed;
180
+ payload = envelope.payload;
181
+ trace = parseTraceCarrier(envelope.trace);
157
182
  }
158
183
  catch (error) {
159
- reportSubscriberError(error);
184
+ reportSubscriberError(error, {
185
+ operation: "message",
186
+ channel,
187
+ eventName,
188
+ });
160
189
  return;
161
190
  }
162
191
  instrumentation.custom({
@@ -167,7 +196,7 @@ export function createRedisEventBus(options) {
167
196
  });
168
197
  for (const handler of [...eventHandlers]) {
169
198
  try {
170
- Promise.resolve(handler(payload)).catch((error) => {
199
+ Promise.resolve(handler(payload, trace ? { trace } : undefined)).catch((error) => {
171
200
  reportHandlerError(error, eventName, payload);
172
201
  });
173
202
  }
@@ -177,29 +206,106 @@ export function createRedisEventBus(options) {
177
206
  }
178
207
  };
179
208
  options.subscriber.on("message", messageHandler);
209
+ const scheduleSubscriptionRetry = (eventName) => {
210
+ const channel = channelForEvent(channelPrefix, eventName);
211
+ if (stopped ||
212
+ subscriptionRetryTimers.has(channel) ||
213
+ !handlers.get(eventName)?.size) {
214
+ return;
215
+ }
216
+ const retryAttempt = (subscriptionRetryAttempts.get(channel) ?? 0) + 1;
217
+ subscriptionRetryAttempts.set(channel, retryAttempt);
218
+ const timer = setTimeout(() => {
219
+ subscriptionRetryTimers.delete(channel);
220
+ ensureSubscribed(eventName);
221
+ }, subscriptionRetryBackoff(retryAttempt));
222
+ timer.unref?.();
223
+ subscriptionRetryTimers.set(channel, timer);
224
+ };
180
225
  const ensureSubscribed = (eventName) => {
181
226
  const channel = channelForEvent(channelPrefix, eventName);
182
- if (subscribedChannels.has(channel))
227
+ if (stopped ||
228
+ subscribedChannels.has(channel) ||
229
+ subscriptionRetryTimers.has(channel)) {
183
230
  return;
184
- subscribedChannels.add(channel);
185
- Promise.resolve(options.subscriber.subscribe(channel)).catch((error) => {
186
- subscribedChannels.delete(channel);
187
- reportSubscriberError(error);
188
- });
231
+ }
232
+ const attempt = Symbol(channel);
233
+ subscribedChannels.set(channel, attempt);
234
+ const onFailure = (error) => {
235
+ if (subscribedChannels.get(channel) === attempt) {
236
+ subscribedChannels.delete(channel);
237
+ scheduleSubscriptionRetry(eventName);
238
+ }
239
+ reportSubscriberError(error, {
240
+ operation: "subscribe",
241
+ channel,
242
+ eventName,
243
+ });
244
+ };
245
+ try {
246
+ Promise.resolve(options.subscriber.subscribe(channel)).then(() => {
247
+ if (subscribedChannels.get(channel) === attempt) {
248
+ subscriptionRetryAttempts.delete(channel);
249
+ }
250
+ }, onFailure);
251
+ }
252
+ catch (error) {
253
+ onFailure(error);
254
+ }
189
255
  };
190
256
  const unsubscribeChannel = (eventName) => {
191
257
  const channel = channelForEvent(channelPrefix, eventName);
192
- if (!subscribedChannels.delete(channel))
258
+ const retryTimer = subscriptionRetryTimers.get(channel);
259
+ if (retryTimer) {
260
+ clearTimeout(retryTimer);
261
+ subscriptionRetryTimers.delete(channel);
262
+ }
263
+ subscriptionRetryAttempts.delete(channel);
264
+ if (!subscribedChannels.has(channel))
193
265
  return;
194
- Promise.resolve(options.subscriber.unsubscribe(channel)).catch(reportSubscriberError);
195
- };
196
- return {
197
- async publish(event, payload) {
198
- instrumentation.record({
199
- type: "eventBus",
200
- eventName: event.name,
266
+ subscribedChannels.delete(channel);
267
+ const onFailure = (error) => {
268
+ reportSubscriberError(error, {
269
+ operation: "unsubscribe",
270
+ channel,
271
+ eventName,
201
272
  });
202
- await options.publisher.publish(channelForEvent(channelPrefix, event.name), JSON.stringify({ eventName: event.name, payload }));
273
+ };
274
+ try {
275
+ Promise.resolve(options.subscriber.unsubscribe(channel)).catch(onFailure);
276
+ }
277
+ catch (error) {
278
+ onFailure(error);
279
+ }
280
+ };
281
+ const eventBus = {
282
+ async publish(event, payload, publishOptions) {
283
+ const trace = parseTraceCarrier(publishOptions?.trace) ??
284
+ captureTraceCarrier(options.instrumentation);
285
+ try {
286
+ await options.publisher.publish(channelForEvent(channelPrefix, event.name), JSON.stringify({
287
+ eventName: event.name,
288
+ payload,
289
+ ...(trace ? { trace } : {}),
290
+ }));
291
+ instrumentation.record({
292
+ type: "eventBus",
293
+ eventName: event.name,
294
+ });
295
+ }
296
+ catch (error) {
297
+ instrumentation.custom({
298
+ name: "eventBus.publish.failed",
299
+ label: "Event publish failed",
300
+ summary: `Publish failed for "${event.name}"`,
301
+ details: {
302
+ operation: "publish",
303
+ eventName: event.name,
304
+ error: errorMessage(error),
305
+ },
306
+ });
307
+ throw error;
308
+ }
203
309
  },
204
310
  subscribe(event, handler) {
205
311
  let eventHandlers = handlers.get(event.name);
@@ -221,6 +327,28 @@ export function createRedisEventBus(options) {
221
327
  };
222
328
  },
223
329
  };
330
+ return {
331
+ eventBus,
332
+ stop() {
333
+ stopped = true;
334
+ for (const timer of subscriptionRetryTimers.values()) {
335
+ clearTimeout(timer);
336
+ }
337
+ subscriptionRetryTimers.clear();
338
+ subscriptionRetryAttempts.clear();
339
+ subscribedChannels.clear();
340
+ handlers.clear();
341
+ if (options.subscriber.off) {
342
+ options.subscriber.off("message", messageHandler);
343
+ }
344
+ else {
345
+ options.subscriber.removeListener?.("message", messageHandler);
346
+ }
347
+ },
348
+ };
349
+ }
350
+ export function createRedisEventBus(options) {
351
+ return createRedisEventBusRuntime(options).eventBus;
224
352
  }
225
353
  export function createRedisEventBusProvider(options = {}) {
226
354
  const providerMetadata = {
@@ -279,6 +407,39 @@ export function createRedisEventBusProvider(options = {}) {
279
407
  ...redisOptions,
280
408
  retryStrategy: options.retryStrategy ?? subscriberRetry.strategy,
281
409
  });
410
+ const connectionInstrumentation = createProviderInstrumentation(ports, {
411
+ providerName: "event-bus-redis",
412
+ watcher: "eventBus",
413
+ });
414
+ const reportPublisherConnectionError = (error) => {
415
+ connectionInstrumentation.custom({
416
+ name: "eventBus.connection.failed",
417
+ label: "Event bus connection failed",
418
+ summary: "Redis event bus publisher connection failed",
419
+ details: {
420
+ operation: "connection",
421
+ role: "publisher",
422
+ error: errorMessage(error),
423
+ },
424
+ });
425
+ };
426
+ const reportSubscriberConnectionError = (error) => {
427
+ connectionInstrumentation.custom({
428
+ name: "eventBus.connection.failed",
429
+ label: "Event bus connection failed",
430
+ summary: "Redis event bus subscriber connection failed",
431
+ details: {
432
+ operation: "connection",
433
+ role: "subscriber",
434
+ error: errorMessage(error),
435
+ },
436
+ });
437
+ };
438
+ const observeConnectionErrors = connectionInstrumentation.isEnabled();
439
+ if (observeConnectionErrors) {
440
+ publisher.on("error", reportPublisherConnectionError);
441
+ subscriber.on("error", reportSubscriberConnectionError);
442
+ }
282
443
  try {
283
444
  await publisher.connect();
284
445
  publisherRetry.markConnected();
@@ -286,16 +447,21 @@ export function createRedisEventBusProvider(options = {}) {
286
447
  subscriberRetry.markConnected();
287
448
  }
288
449
  catch (error) {
450
+ if (observeConnectionErrors) {
451
+ publisher.off("error", reportPublisherConnectionError);
452
+ subscriber.off("error", reportSubscriberConnectionError);
453
+ }
289
454
  publisher.disconnect();
290
455
  subscriber.disconnect();
291
456
  throw new Error(`[createRedisEventBusProvider] Failed to connect to Redis at ${redactConnectionUrl(config.URL)}: ${errorMessage(error)}`);
292
457
  }
293
- const eventBus = createRedisEventBus({
458
+ const eventBusRuntime = createRedisEventBusRuntime({
294
459
  publisher,
295
460
  subscriber,
296
461
  channelPrefix: configuredChannelPrefix,
297
462
  instrumentation: ports,
298
463
  });
464
+ const eventBus = eventBusRuntime.eventBus;
299
465
  return {
300
466
  ports: {
301
467
  eventBus,
@@ -307,6 +473,7 @@ export function createRedisEventBusProvider(options = {}) {
307
473
  },
308
474
  },
309
475
  async stop() {
476
+ eventBusRuntime.stop();
310
477
  try {
311
478
  await subscriber.quit();
312
479
  }
@@ -319,6 +486,10 @@ export function createRedisEventBusProvider(options = {}) {
319
486
  catch {
320
487
  publisher.disconnect();
321
488
  }
489
+ if (observeConnectionErrors) {
490
+ publisher.off("error", reportPublisherConnectionError);
491
+ subscriber.off("error", reportSubscriberConnectionError);
492
+ }
322
493
  },
323
494
  };
324
495
  },
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@beignet/provider-event-bus-redis",
3
- "version": "0.0.37",
3
+ "version": "0.0.39",
4
4
  "type": "module",
5
5
  "description": "Redis Pub/Sub event bus provider for Beignet - implements EventBusPort for cross-process best-effort delivery",
6
6
  "main": "./dist/index.js",
package/src/index.ts CHANGED
@@ -17,6 +17,11 @@ import {
17
17
  type ProviderInstrumentationTarget,
18
18
  type ServiceProvider,
19
19
  } from "@beignet/core/providers";
20
+ import {
21
+ captureTraceCarrier,
22
+ parseTraceCarrier,
23
+ type TraceCarrier,
24
+ } from "@beignet/core/tracing";
20
25
  import { Redis } from "ioredis";
21
26
  import { z } from "zod";
22
27
 
@@ -25,6 +30,8 @@ const DEFAULT_MAX_RETRIES_PER_REQUEST = 2;
25
30
  const DEFAULT_CONNECT_MAX_ATTEMPTS = 3;
26
31
  const DEFAULT_CHANNEL_PREFIX = "beignet:event-bus:";
27
32
  const RECONNECT_BACKOFF_CAP_MS = 2000;
33
+ const SUBSCRIPTION_RETRY_BASE_MS = 100;
34
+ const SUBSCRIPTION_RETRY_CAP_MS = 2000;
28
35
 
29
36
  const NumberString = z.union([
30
37
  z
@@ -154,6 +161,13 @@ function reconnectBackoff(attempt: number): number {
154
161
  return Math.min(2 ** attempt * 100, RECONNECT_BACKOFF_CAP_MS);
155
162
  }
156
163
 
164
+ function subscriptionRetryBackoff(attempt: number): number {
165
+ return Math.min(
166
+ SUBSCRIPTION_RETRY_BASE_MS * 2 ** Math.max(0, attempt - 1),
167
+ SUBSCRIPTION_RETRY_CAP_MS,
168
+ );
169
+ }
170
+
157
171
  function createRetryStrategy(connectMaxAttempts: number): {
158
172
  strategy: (attempt: number) => number | null;
159
173
  markConnected: () => void;
@@ -224,15 +238,31 @@ async function checkRedisEventBusHealth(
224
238
  }
225
239
  }
226
240
 
227
- export function createRedisEventBus(
241
+ interface RedisEventBusRuntime {
242
+ eventBus: EventBusPort;
243
+ stop(): void;
244
+ }
245
+
246
+ function createRedisEventBusRuntime(
228
247
  options: CreateRedisEventBusOptions,
229
- ): EventBusPort {
248
+ ): RedisEventBusRuntime {
230
249
  const channelPrefix = options.channelPrefix ?? DEFAULT_CHANNEL_PREFIX;
231
250
  const handlers = new Map<
232
251
  string,
233
- Set<(payload: unknown) => void | Promise<void>>
252
+ Set<
253
+ (
254
+ payload: unknown,
255
+ options?: { trace?: TraceCarrier },
256
+ ) => void | Promise<void>
257
+ >
258
+ >();
259
+ const subscribedChannels = new Map<string, symbol>();
260
+ const subscriptionRetryAttempts = new Map<string, number>();
261
+ const subscriptionRetryTimers = new Map<
262
+ string,
263
+ ReturnType<typeof setTimeout>
234
264
  >();
235
- const subscribedChannels = new Set<string>();
265
+ let stopped = false;
236
266
  const instrumentation = createProviderInstrumentation(
237
267
  options.instrumentation,
238
268
  {
@@ -241,11 +271,37 @@ export function createRedisEventBus(
241
271
  },
242
272
  );
243
273
 
244
- const reportSubscriberError = (error: unknown) => {
274
+ const reportSubscriberError = (
275
+ error: unknown,
276
+ details: {
277
+ operation: "message" | "subscribe" | "unsubscribe";
278
+ channel?: string;
279
+ eventName?: string;
280
+ },
281
+ ) => {
282
+ instrumentation.custom({
283
+ name:
284
+ details.operation === "message"
285
+ ? "eventBus.message.failed"
286
+ : "eventBus.subscription.failed",
287
+ label:
288
+ details.operation === "message"
289
+ ? "Event message failed"
290
+ : "Event subscription failed",
291
+ summary:
292
+ details.eventName ?? details.channel ?? "Redis event bus subscriber",
293
+ details: {
294
+ ...details,
295
+ error: errorMessage(error),
296
+ },
297
+ });
298
+
245
299
  try {
246
- options.onSubscriberError?.(error);
300
+ Promise.resolve(options.onSubscriberError?.(error)).catch(
301
+ () => undefined,
302
+ );
247
303
  } catch {
248
- // Error callbacks should not create unhandled rejections.
304
+ // Error callbacks should not replace or create another failure.
249
305
  }
250
306
  };
251
307
 
@@ -265,9 +321,11 @@ export function createRedisEventBus(
265
321
  });
266
322
 
267
323
  try {
268
- options.onHandlerError?.(error, eventName, payload);
324
+ Promise.resolve(
325
+ options.onHandlerError?.(error, eventName, payload),
326
+ ).catch(() => undefined);
269
327
  } catch {
270
- // Error callbacks should not create unhandled rejections.
328
+ // Error callbacks should not replace or create another failure.
271
329
  }
272
330
  };
273
331
 
@@ -279,6 +337,7 @@ export function createRedisEventBus(
279
337
  if (!eventHandlers || eventHandlers.size === 0) return;
280
338
 
281
339
  let payload: unknown;
340
+ let trace: TraceCarrier | undefined;
282
341
  try {
283
342
  const parsed = safeJsonParse(message);
284
343
  if (
@@ -288,9 +347,15 @@ export function createRedisEventBus(
288
347
  ) {
289
348
  throw new Error("Redis event bus message is missing a payload field.");
290
349
  }
291
- payload = (parsed as { payload: unknown }).payload;
350
+ const envelope = parsed as { payload: unknown; trace?: unknown };
351
+ payload = envelope.payload;
352
+ trace = parseTraceCarrier(envelope.trace);
292
353
  } catch (error) {
293
- reportSubscriberError(error);
354
+ reportSubscriberError(error, {
355
+ operation: "message",
356
+ channel,
357
+ eventName,
358
+ });
294
359
  return;
295
360
  }
296
361
 
@@ -303,9 +368,11 @@ export function createRedisEventBus(
303
368
 
304
369
  for (const handler of [...eventHandlers]) {
305
370
  try {
306
- Promise.resolve(handler(payload)).catch((error: unknown) => {
307
- reportHandlerError(error, eventName, payload);
308
- });
371
+ Promise.resolve(handler(payload, trace ? { trace } : undefined)).catch(
372
+ (error: unknown) => {
373
+ reportHandlerError(error, eventName, payload);
374
+ },
375
+ );
309
376
  } catch (error) {
310
377
  reportHandlerError(error, eventName, payload);
311
378
  }
@@ -314,40 +381,126 @@ export function createRedisEventBus(
314
381
 
315
382
  options.subscriber.on("message", messageHandler);
316
383
 
317
- const ensureSubscribed = (eventName: string) => {
384
+ const scheduleSubscriptionRetry = (eventName: string) => {
318
385
  const channel = channelForEvent(channelPrefix, eventName);
319
- if (subscribedChannels.has(channel)) return;
320
- subscribedChannels.add(channel);
321
- Promise.resolve(options.subscriber.subscribe(channel)).catch((error) => {
322
- subscribedChannels.delete(channel);
323
- reportSubscriberError(error);
324
- });
386
+ if (
387
+ stopped ||
388
+ subscriptionRetryTimers.has(channel) ||
389
+ !handlers.get(eventName)?.size
390
+ ) {
391
+ return;
392
+ }
393
+
394
+ const retryAttempt = (subscriptionRetryAttempts.get(channel) ?? 0) + 1;
395
+ subscriptionRetryAttempts.set(channel, retryAttempt);
396
+ const timer = setTimeout(() => {
397
+ subscriptionRetryTimers.delete(channel);
398
+ ensureSubscribed(eventName);
399
+ }, subscriptionRetryBackoff(retryAttempt));
400
+ timer.unref?.();
401
+ subscriptionRetryTimers.set(channel, timer);
325
402
  };
326
403
 
327
- const unsubscribeChannel = (eventName: string) => {
404
+ const ensureSubscribed = (eventName: string) => {
328
405
  const channel = channelForEvent(channelPrefix, eventName);
329
- if (!subscribedChannels.delete(channel)) return;
330
- Promise.resolve(options.subscriber.unsubscribe(channel)).catch(
331
- reportSubscriberError,
332
- );
406
+ if (
407
+ stopped ||
408
+ subscribedChannels.has(channel) ||
409
+ subscriptionRetryTimers.has(channel)
410
+ ) {
411
+ return;
412
+ }
413
+ const attempt = Symbol(channel);
414
+ subscribedChannels.set(channel, attempt);
415
+
416
+ const onFailure = (error: unknown) => {
417
+ if (subscribedChannels.get(channel) === attempt) {
418
+ subscribedChannels.delete(channel);
419
+ scheduleSubscriptionRetry(eventName);
420
+ }
421
+ reportSubscriberError(error, {
422
+ operation: "subscribe",
423
+ channel,
424
+ eventName,
425
+ });
426
+ };
427
+
428
+ try {
429
+ Promise.resolve(options.subscriber.subscribe(channel)).then(() => {
430
+ if (subscribedChannels.get(channel) === attempt) {
431
+ subscriptionRetryAttempts.delete(channel);
432
+ }
433
+ }, onFailure);
434
+ } catch (error) {
435
+ onFailure(error);
436
+ }
333
437
  };
334
438
 
335
- return {
336
- async publish(event, payload) {
337
- instrumentation.record({
338
- type: "eventBus",
339
- eventName: event.name,
439
+ const unsubscribeChannel = (eventName: string) => {
440
+ const channel = channelForEvent(channelPrefix, eventName);
441
+ const retryTimer = subscriptionRetryTimers.get(channel);
442
+ if (retryTimer) {
443
+ clearTimeout(retryTimer);
444
+ subscriptionRetryTimers.delete(channel);
445
+ }
446
+ subscriptionRetryAttempts.delete(channel);
447
+ if (!subscribedChannels.has(channel)) return;
448
+ subscribedChannels.delete(channel);
449
+
450
+ const onFailure = (error: unknown) => {
451
+ reportSubscriberError(error, {
452
+ operation: "unsubscribe",
453
+ channel,
454
+ eventName,
340
455
  });
456
+ };
341
457
 
342
- await options.publisher.publish(
343
- channelForEvent(channelPrefix, event.name),
344
- JSON.stringify({ eventName: event.name, payload }),
345
- );
458
+ try {
459
+ Promise.resolve(options.subscriber.unsubscribe(channel)).catch(onFailure);
460
+ } catch (error) {
461
+ onFailure(error);
462
+ }
463
+ };
464
+
465
+ const eventBus: EventBusPort = {
466
+ async publish(event, payload, publishOptions) {
467
+ const trace =
468
+ parseTraceCarrier(publishOptions?.trace) ??
469
+ captureTraceCarrier(options.instrumentation);
470
+ try {
471
+ await options.publisher.publish(
472
+ channelForEvent(channelPrefix, event.name),
473
+ JSON.stringify({
474
+ eventName: event.name,
475
+ payload,
476
+ ...(trace ? { trace } : {}),
477
+ }),
478
+ );
479
+ instrumentation.record({
480
+ type: "eventBus",
481
+ eventName: event.name,
482
+ });
483
+ } catch (error) {
484
+ instrumentation.custom({
485
+ name: "eventBus.publish.failed",
486
+ label: "Event publish failed",
487
+ summary: `Publish failed for "${event.name}"`,
488
+ details: {
489
+ operation: "publish",
490
+ eventName: event.name,
491
+ error: errorMessage(error),
492
+ },
493
+ });
494
+ throw error;
495
+ }
346
496
  },
347
497
 
348
498
  subscribe<E extends DomainEventDef>(
349
499
  event: E,
350
- handler: (payload: InferEventPayload<E>) => void | Promise<void>,
500
+ handler: (
501
+ payload: InferEventPayload<E>,
502
+ options?: { trace?: TraceCarrier },
503
+ ) => void | Promise<void>,
351
504
  ) {
352
505
  let eventHandlers = handlers.get(event.name);
353
506
  if (!eventHandlers) {
@@ -355,14 +508,22 @@ export function createRedisEventBus(
355
508
  handlers.set(event.name, eventHandlers);
356
509
  }
357
510
 
358
- eventHandlers.add(handler as (payload: unknown) => void | Promise<void>);
511
+ eventHandlers.add(
512
+ handler as unknown as (
513
+ payload: unknown,
514
+ options?: { trace?: TraceCarrier },
515
+ ) => void | Promise<void>,
516
+ );
359
517
  ensureSubscribed(event.name);
360
518
 
361
519
  return () => {
362
520
  const currentHandlers = handlers.get(event.name);
363
521
  if (!currentHandlers) return;
364
522
  currentHandlers.delete(
365
- handler as (payload: unknown) => void | Promise<void>,
523
+ handler as unknown as (
524
+ payload: unknown,
525
+ options?: { trace?: TraceCarrier },
526
+ ) => void | Promise<void>,
366
527
  );
367
528
  if (currentHandlers.size === 0) {
368
529
  handlers.delete(event.name);
@@ -371,6 +532,31 @@ export function createRedisEventBus(
371
532
  };
372
533
  },
373
534
  };
535
+
536
+ return {
537
+ eventBus,
538
+ stop() {
539
+ stopped = true;
540
+ for (const timer of subscriptionRetryTimers.values()) {
541
+ clearTimeout(timer);
542
+ }
543
+ subscriptionRetryTimers.clear();
544
+ subscriptionRetryAttempts.clear();
545
+ subscribedChannels.clear();
546
+ handlers.clear();
547
+ if (options.subscriber.off) {
548
+ options.subscriber.off("message", messageHandler);
549
+ } else {
550
+ options.subscriber.removeListener?.("message", messageHandler);
551
+ }
552
+ },
553
+ };
554
+ }
555
+
556
+ export function createRedisEventBus(
557
+ options: CreateRedisEventBusOptions,
558
+ ): EventBusPort {
559
+ return createRedisEventBusRuntime(options).eventBus;
374
560
  }
375
561
 
376
562
  export function createRedisEventBusProvider(
@@ -443,6 +629,39 @@ export function createRedisEventBusProvider(
443
629
  ...redisOptions,
444
630
  retryStrategy: options.retryStrategy ?? subscriberRetry.strategy,
445
631
  });
632
+ const connectionInstrumentation = createProviderInstrumentation(ports, {
633
+ providerName: "event-bus-redis",
634
+ watcher: "eventBus",
635
+ });
636
+ const reportPublisherConnectionError = (error: unknown) => {
637
+ connectionInstrumentation.custom({
638
+ name: "eventBus.connection.failed",
639
+ label: "Event bus connection failed",
640
+ summary: "Redis event bus publisher connection failed",
641
+ details: {
642
+ operation: "connection",
643
+ role: "publisher",
644
+ error: errorMessage(error),
645
+ },
646
+ });
647
+ };
648
+ const reportSubscriberConnectionError = (error: unknown) => {
649
+ connectionInstrumentation.custom({
650
+ name: "eventBus.connection.failed",
651
+ label: "Event bus connection failed",
652
+ summary: "Redis event bus subscriber connection failed",
653
+ details: {
654
+ operation: "connection",
655
+ role: "subscriber",
656
+ error: errorMessage(error),
657
+ },
658
+ });
659
+ };
660
+ const observeConnectionErrors = connectionInstrumentation.isEnabled();
661
+ if (observeConnectionErrors) {
662
+ publisher.on("error", reportPublisherConnectionError);
663
+ subscriber.on("error", reportSubscriberConnectionError);
664
+ }
446
665
 
447
666
  try {
448
667
  await publisher.connect();
@@ -450,6 +669,10 @@ export function createRedisEventBusProvider(
450
669
  await subscriber.connect();
451
670
  subscriberRetry.markConnected();
452
671
  } catch (error) {
672
+ if (observeConnectionErrors) {
673
+ publisher.off("error", reportPublisherConnectionError);
674
+ subscriber.off("error", reportSubscriberConnectionError);
675
+ }
453
676
  publisher.disconnect();
454
677
  subscriber.disconnect();
455
678
  throw new Error(
@@ -459,12 +682,13 @@ export function createRedisEventBusProvider(
459
682
  );
460
683
  }
461
684
 
462
- const eventBus = createRedisEventBus({
685
+ const eventBusRuntime = createRedisEventBusRuntime({
463
686
  publisher,
464
687
  subscriber,
465
688
  channelPrefix: configuredChannelPrefix,
466
689
  instrumentation: ports,
467
690
  });
691
+ const eventBus = eventBusRuntime.eventBus;
468
692
 
469
693
  return {
470
694
  ports: {
@@ -482,6 +706,7 @@ export function createRedisEventBusProvider(
482
706
  },
483
707
  } satisfies RedisEventBusProviderPorts,
484
708
  async stop() {
709
+ eventBusRuntime.stop();
485
710
  try {
486
711
  await subscriber.quit();
487
712
  } catch {
@@ -492,6 +717,10 @@ export function createRedisEventBusProvider(
492
717
  } catch {
493
718
  publisher.disconnect();
494
719
  }
720
+ if (observeConnectionErrors) {
721
+ publisher.off("error", reportPublisherConnectionError);
722
+ subscriber.off("error", reportSubscriberConnectionError);
723
+ }
495
724
  },
496
725
  };
497
726
  },