@fluojs/microservices 1.0.5 → 2.0.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/README.ko.md +20 -6
- package/README.md +20 -6
- package/dist/module.d.ts.map +1 -1
- package/dist/module.js +1 -0
- package/dist/service.d.ts +12 -3
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +92 -36
- package/dist/status.d.ts +3 -0
- package/dist/status.d.ts.map +1 -1
- package/dist/status.js +10 -2
- package/dist/transports/grpc-transport.d.ts +4 -2
- package/dist/transports/grpc-transport.d.ts.map +1 -1
- package/dist/transports/grpc-transport.js +77 -12
- package/dist/transports/kafka-transport.d.ts +9 -1
- package/dist/transports/kafka-transport.d.ts.map +1 -1
- package/dist/transports/kafka-transport.js +29 -5
- package/dist/transports/nats-transport.d.ts +2 -0
- package/dist/transports/nats-transport.d.ts.map +1 -1
- package/dist/transports/nats-transport.js +50 -27
- package/dist/transports/rabbitmq-transport.d.ts +9 -1
- package/dist/transports/rabbitmq-transport.d.ts.map +1 -1
- package/dist/transports/rabbitmq-transport.js +38 -13
- package/dist/transports/redis-streams-transport.d.ts +18 -0
- package/dist/transports/redis-streams-transport.d.ts.map +1 -1
- package/dist/transports/redis-streams-transport.js +31 -1
- package/dist/transports/redis-transport.d.ts +1 -1
- package/dist/transports/redis-transport.d.ts.map +1 -1
- package/dist/transports/redis-transport.js +32 -16
- package/dist/transports/tcp-transport.d.ts +1 -0
- package/dist/transports/tcp-transport.d.ts.map +1 -1
- package/dist/transports/tcp-transport.js +45 -44
- package/dist/types.d.ts +7 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +11 -7
|
@@ -16,8 +16,10 @@ const grpcKinds = {
|
|
|
16
16
|
* and exposes matching unary/server-stream/client-stream/bidi-stream client calls through one transport surface.
|
|
17
17
|
*/
|
|
18
18
|
export class GrpcMicroserviceTransport {
|
|
19
|
-
/** Indicates
|
|
20
|
-
ownsResources;
|
|
19
|
+
/** Indicates that gRPC closes the cached outbound clients it creates. */
|
|
20
|
+
ownsResources = true;
|
|
21
|
+
/** Reports server and cached outbound client ownership independently. */
|
|
22
|
+
resourceOwnership;
|
|
21
23
|
bidiStreamHandler;
|
|
22
24
|
clientStreamHandler;
|
|
23
25
|
closePromise;
|
|
@@ -46,7 +48,10 @@ export class GrpcMicroserviceTransport {
|
|
|
46
48
|
this.requestTimeoutMs = options.requestTimeoutMs ?? 3_000;
|
|
47
49
|
this.server = options.server;
|
|
48
50
|
this.ownsServer = !options.server;
|
|
49
|
-
this.
|
|
51
|
+
this.resourceOwnership = {
|
|
52
|
+
outboundClients: 'framework',
|
|
53
|
+
server: this.ownsServer ? 'framework' : 'caller'
|
|
54
|
+
};
|
|
50
55
|
}
|
|
51
56
|
setLogger(logger) {
|
|
52
57
|
this.logger = logger;
|
|
@@ -553,6 +558,8 @@ export class GrpcMicroserviceTransport {
|
|
|
553
558
|
let ended = false;
|
|
554
559
|
let cleanupAbortListener;
|
|
555
560
|
let settled = false;
|
|
561
|
+
let writerError;
|
|
562
|
+
let rejectResult;
|
|
556
563
|
const cleanup = () => {
|
|
557
564
|
cleanupAbortListener?.();
|
|
558
565
|
cleanupAbortListener = undefined;
|
|
@@ -563,6 +570,7 @@ export class GrpcMicroserviceTransport {
|
|
|
563
570
|
reject(new Error('gRPC client stream aborted before dispatch.'));
|
|
564
571
|
return;
|
|
565
572
|
}
|
|
573
|
+
rejectResult = reject;
|
|
566
574
|
const settle = complete => {
|
|
567
575
|
if (settled) {
|
|
568
576
|
return;
|
|
@@ -572,6 +580,12 @@ export class GrpcMicroserviceTransport {
|
|
|
572
580
|
complete();
|
|
573
581
|
};
|
|
574
582
|
callStream = method.call(runtime.client, metadata, (error, response) => {
|
|
583
|
+
// A local writer.error() failure is the authoritative cause: the transport-level
|
|
584
|
+
// status that follows an aborted call would otherwise mask it.
|
|
585
|
+
if (writerError) {
|
|
586
|
+
settle(() => reject(writerError));
|
|
587
|
+
return;
|
|
588
|
+
}
|
|
575
589
|
if (error) {
|
|
576
590
|
if (signal?.aborted) {
|
|
577
591
|
settle(() => reject(new Error('gRPC client stream aborted.')));
|
|
@@ -615,10 +629,19 @@ export class GrpcMicroserviceTransport {
|
|
|
615
629
|
}
|
|
616
630
|
},
|
|
617
631
|
error(err) {
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
632
|
+
if (ended) {
|
|
633
|
+
return;
|
|
634
|
+
}
|
|
635
|
+
ended = true;
|
|
636
|
+
writerError = err;
|
|
637
|
+
abortClientStreamCall(callStream, err);
|
|
638
|
+
|
|
639
|
+
// Runtimes without cancel()/destroy() cannot abort the call, so settle locally
|
|
640
|
+
// instead of leaving the caller with a silently successful stream.
|
|
641
|
+
if (!settled) {
|
|
642
|
+
settled = true;
|
|
643
|
+
cleanup();
|
|
644
|
+
rejectResult?.(err);
|
|
622
645
|
}
|
|
623
646
|
}
|
|
624
647
|
};
|
|
@@ -637,6 +660,9 @@ export class GrpcMicroserviceTransport {
|
|
|
637
660
|
const duplexStream = method.call(runtime.client, metadata);
|
|
638
661
|
let writerEnded = false;
|
|
639
662
|
let cleanupAbortListener;
|
|
663
|
+
const localFailure = {
|
|
664
|
+
error: undefined
|
|
665
|
+
};
|
|
640
666
|
if (signal) {
|
|
641
667
|
if (signal.aborted) {
|
|
642
668
|
duplexStream.cancel?.();
|
|
@@ -656,6 +682,7 @@ export class GrpcMicroserviceTransport {
|
|
|
656
682
|
}
|
|
657
683
|
const reader = grpcReadableToAsyncIterable(duplexStream, {
|
|
658
684
|
externalAbortCleanup: cleanupAbortListener,
|
|
685
|
+
localFailure,
|
|
659
686
|
signal
|
|
660
687
|
});
|
|
661
688
|
const writer = {
|
|
@@ -671,11 +698,14 @@ export class GrpcMicroserviceTransport {
|
|
|
671
698
|
}
|
|
672
699
|
},
|
|
673
700
|
error(err) {
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
writerEnded = true;
|
|
677
|
-
duplexStream.end();
|
|
701
|
+
if (writerEnded) {
|
|
702
|
+
return;
|
|
678
703
|
}
|
|
704
|
+
writerEnded = true;
|
|
705
|
+
// Recorded before cancelation so the reader reports the caller's cause rather than
|
|
706
|
+
// the CANCELLED status the aborted call raises.
|
|
707
|
+
localFailure.error = err;
|
|
708
|
+
abortDuplexStreamCall(duplexStream, err);
|
|
679
709
|
}
|
|
680
710
|
};
|
|
681
711
|
return {
|
|
@@ -948,7 +978,7 @@ function grpcReadableToAsyncIterable(stream, options = {}) {
|
|
|
948
978
|
}
|
|
949
979
|
});
|
|
950
980
|
stream.on('error', err => {
|
|
951
|
-
const streamError = signal?.aborted ? new Error(abortErrorMessage) : err;
|
|
981
|
+
const streamError = options.localFailure?.error ?? (signal?.aborted ? new Error(abortErrorMessage) : err);
|
|
952
982
|
if (!finish()) {
|
|
953
983
|
return;
|
|
954
984
|
}
|
|
@@ -1009,6 +1039,41 @@ function grpcReadableToAsyncIterable(stream, options = {}) {
|
|
|
1009
1039
|
}
|
|
1010
1040
|
};
|
|
1011
1041
|
}
|
|
1042
|
+
|
|
1043
|
+
/**
|
|
1044
|
+
* Aborts an outbound client-streaming call so the peer observes a failure instead of a clean EOF.
|
|
1045
|
+
*
|
|
1046
|
+
* `destroy(err)` is preferred because it carries the cause; `cancel()` is the grpc-js fallback.
|
|
1047
|
+
*/
|
|
1048
|
+
function abortClientStreamCall(stream, err) {
|
|
1049
|
+
if (!stream) {
|
|
1050
|
+
return;
|
|
1051
|
+
}
|
|
1052
|
+
if (stream.destroy) {
|
|
1053
|
+
stream.destroy(err);
|
|
1054
|
+
return;
|
|
1055
|
+
}
|
|
1056
|
+
if (stream.cancel) {
|
|
1057
|
+
stream.cancel();
|
|
1058
|
+
return;
|
|
1059
|
+
}
|
|
1060
|
+
stream.end();
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
/**
|
|
1064
|
+
* Aborts an outbound bidirectional-streaming call so the peer observes a failure instead of a clean EOF.
|
|
1065
|
+
*/
|
|
1066
|
+
function abortDuplexStreamCall(stream, err) {
|
|
1067
|
+
if (stream.destroy) {
|
|
1068
|
+
stream.destroy(err);
|
|
1069
|
+
return;
|
|
1070
|
+
}
|
|
1071
|
+
if (stream.cancel) {
|
|
1072
|
+
stream.cancel();
|
|
1073
|
+
return;
|
|
1074
|
+
}
|
|
1075
|
+
stream.end();
|
|
1076
|
+
}
|
|
1012
1077
|
function parseGrpcPattern(pattern) {
|
|
1013
1078
|
const segments = pattern.split('.');
|
|
1014
1079
|
if (segments.length !== 2 || segments[0]?.length === 0 || segments[1]?.length === 0) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { MicroserviceTransport, TransportHandler } from '../types.js';
|
|
1
|
+
import type { MicroserviceTransport, MicroserviceTransportLogger, TransportHandler } from '../types.js';
|
|
2
2
|
interface KafkaConsumerLike {
|
|
3
3
|
subscribe(topic: string, handler: (message: string) => Promise<void> | void): Promise<void>;
|
|
4
4
|
unsubscribe(topic: string): Promise<void>;
|
|
@@ -28,11 +28,19 @@ export declare class KafkaMicroserviceTransport implements MicroserviceTransport
|
|
|
28
28
|
private handler;
|
|
29
29
|
private listening;
|
|
30
30
|
private listenPromise;
|
|
31
|
+
private logger;
|
|
31
32
|
private readonly eventTopic;
|
|
32
33
|
private readonly messageTopic;
|
|
33
34
|
private readonly pending;
|
|
34
35
|
private readonly requestTimeoutMs;
|
|
35
36
|
private readonly responseTopic;
|
|
37
|
+
private logEventHandlerFailure;
|
|
38
|
+
/**
|
|
39
|
+
* Registers the runtime logger that receives inbound event handler failures.
|
|
40
|
+
*
|
|
41
|
+
* @param logger Logger used to report event handler failures.
|
|
42
|
+
*/
|
|
43
|
+
setLogger(logger: MicroserviceTransportLogger): void;
|
|
36
44
|
/**
|
|
37
45
|
* Creates a Kafka transport with explicit producer and consumer collaborators.
|
|
38
46
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"kafka-transport.d.ts","sourceRoot":"","sources":["../../src/transports/kafka-transport.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"kafka-transport.d.ts","sourceRoot":"","sources":["../../src/transports/kafka-transport.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,2BAA2B,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAGxG,UAAU,iBAAiB;IACzB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5F,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3C;AAED,UAAU,iBAAiB;IACzB,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACxD;AAgBD,gEAAgE;AAChE,MAAM,WAAW,iCAAiC;IAChD,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;GAKG;AACH,qBAAa,0BAA2B,YAAW,qBAAqB;IAmC1D,OAAO,CAAC,QAAQ,CAAC,OAAO;IAlCpC,OAAO,CAAC,YAAY,CAA4B;IAChD,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,OAAO,CAA+B;IAC9C,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,aAAa,CAA4B;IACjD,OAAO,CAAC,MAAM,CAA0C;IACxD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqC;IAC7D,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IAEvC,OAAO,CAAC,sBAAsB;IAQ9B;;;;OAIG;IACH,SAAS,CAAC,MAAM,EAAE,2BAA2B,GAAG,IAAI;IAIpD;;;;OAIG;gBAC0B,OAAO,EAAE,iCAAiC;IAOvE;;;;;OAKG;IACG,MAAM,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IAkDtD;;;;;;;OAOG;IACG,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC;IAsFrF;;;;;;OAMG;IACG,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAc5D;;;;OAIG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YA6Cd,kBAAkB;YAuBlB,oBAAoB;YA2CpB,qBAAqB;IAqBnC,OAAO,CAAC,YAAY;CAqCrB"}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { logTransportEventHandlerFailure } from './event-handler-logger.js';
|
|
2
|
+
|
|
1
3
|
/** Options for configuring the Kafka microservice transport. */
|
|
2
4
|
|
|
3
5
|
/**
|
|
@@ -12,11 +14,28 @@ export class KafkaMicroserviceTransport {
|
|
|
12
14
|
handler;
|
|
13
15
|
listening = false;
|
|
14
16
|
listenPromise;
|
|
17
|
+
logger;
|
|
15
18
|
eventTopic;
|
|
16
19
|
messageTopic;
|
|
17
20
|
pending = new Map();
|
|
18
21
|
requestTimeoutMs;
|
|
19
22
|
responseTopic;
|
|
23
|
+
logEventHandlerFailure(error) {
|
|
24
|
+
try {
|
|
25
|
+
logTransportEventHandlerFailure(this.logger, 'KafkaMicroserviceTransport', error);
|
|
26
|
+
} catch {
|
|
27
|
+
// A failing logger must not mask the event failure the broker still has to observe.
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Registers the runtime logger that receives inbound event handler failures.
|
|
33
|
+
*
|
|
34
|
+
* @param logger Logger used to report event handler failures.
|
|
35
|
+
*/
|
|
36
|
+
setLogger(logger) {
|
|
37
|
+
this.logger = logger;
|
|
38
|
+
}
|
|
20
39
|
|
|
21
40
|
/**
|
|
22
41
|
* Creates a Kafka transport with explicit producer and consumer collaborators.
|
|
@@ -226,11 +245,16 @@ export class KafkaMicroserviceTransport {
|
|
|
226
245
|
if (!message || message.kind !== 'event') {
|
|
227
246
|
return;
|
|
228
247
|
}
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
248
|
+
try {
|
|
249
|
+
await this.handler({
|
|
250
|
+
kind: 'event',
|
|
251
|
+
pattern: message.pattern,
|
|
252
|
+
payload: message.payload
|
|
253
|
+
});
|
|
254
|
+
} catch (error) {
|
|
255
|
+
this.logEventHandlerFailure(error);
|
|
256
|
+
throw error;
|
|
257
|
+
}
|
|
234
258
|
}
|
|
235
259
|
async handleInboundRequest(rawMessage) {
|
|
236
260
|
if (!this.handler) {
|
|
@@ -47,6 +47,8 @@ export declare class NatsMicroserviceTransport implements MicroserviceTransport
|
|
|
47
47
|
private subscriptions;
|
|
48
48
|
private logEventHandlerFailure;
|
|
49
49
|
private handleEventMessageSafely;
|
|
50
|
+
private handleRequestMessageSafely;
|
|
51
|
+
private logRequestCallbackFailure;
|
|
50
52
|
/**
|
|
51
53
|
* Creates a NATS transport using a client and codec supplied by the application.
|
|
52
54
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nats-transport.d.ts","sourceRoot":"","sources":["../../src/transports/nats-transport.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,2BAA2B,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAGxG,UAAU,eAAe;IACvB,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,OAAO,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,CAAC;CACjC;AAED,UAAU,aAAa;IACrB,MAAM,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAAC;IACjC,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,CAAC;CACnC;AAED,UAAU,oBAAoB;IAC5B,WAAW,IAAI,IAAI,CAAC;CACrB;AAED,UAAU,QAAQ;IAChB,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,GAAG,IAAI,CAAC;IACpD,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,UAAU,CAAA;KAAE,CAAC,CAAC;IAC7G,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,GAAG,oBAAoB,CAAC;CAC/F;AAED,+DAA+D;AAC/D,MAAM,WAAW,gCAAgC;IAC/C,MAAM,EAAE,QAAQ,CAAC;IACjB,KAAK,EAAE,aAAa,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAkBD;;;;;GAKG;AACH,qBAAa,yBAA0B,YAAW,qBAAqB;
|
|
1
|
+
{"version":3,"file":"nats-transport.d.ts","sourceRoot":"","sources":["../../src/transports/nats-transport.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,2BAA2B,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAGxG,UAAU,eAAe;IACvB,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,OAAO,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,CAAC;CACjC;AAED,UAAU,aAAa;IACrB,MAAM,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAAC;IACjC,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,CAAC;CACnC;AAED,UAAU,oBAAoB;IAC5B,WAAW,IAAI,IAAI,CAAC;CACrB;AAED,UAAU,QAAQ;IAChB,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,GAAG,IAAI,CAAC;IACpD,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,UAAU,CAAA;KAAE,CAAC,CAAC;IAC7G,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,GAAG,oBAAoB,CAAC;CAC/F;AAED,+DAA+D;AAC/D,MAAM,WAAW,gCAAgC;IAC/C,MAAM,EAAE,QAAQ,CAAC;IACjB,KAAK,EAAE,aAAa,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAkBD;;;;;GAKG;AACH,qBAAa,yBAA0B,YAAW,qBAAqB;IA+CzD,OAAO,CAAC,QAAQ,CAAC,OAAO;IA9CpC,OAAO,CAAC,YAAY,CAA4B;IAChD,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,OAAO,CAA+B;IAC9C,OAAO,CAAC,MAAM,CAA0C;IACxD,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqC;IAC7D,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,aAAa,CAA8B;IAEnD,OAAO,CAAC,sBAAsB;IAI9B,OAAO,CAAC,wBAAwB;IAMhC,OAAO,CAAC,0BAA0B;IAMlC,OAAO,CAAC,yBAAyB;IAcjC;;;;OAIG;gBAC0B,OAAO,EAAE,gCAAgC;IAMtE,SAAS,CAAC,MAAM,EAAE,2BAA2B,GAAG,IAAI;IAIpD;;;;;OAKG;IACG,MAAM,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IA4CtD;;;;;;;OAOG;IACG,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC;IA+FrF;;;;;;OAMG;IACG,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAc5D;;;;OAIG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YA+Cd,kBAAkB;YAsBlB,oBAAoB;IA8BlC,OAAO,CAAC,MAAM;IAId,OAAO,CAAC,MAAM;CAGf"}
|
|
@@ -27,6 +27,22 @@ export class NatsMicroserviceTransport {
|
|
|
27
27
|
this.logEventHandlerFailure(error);
|
|
28
28
|
});
|
|
29
29
|
}
|
|
30
|
+
handleRequestMessageSafely(message) {
|
|
31
|
+
void this.handleRequestMessage(message).catch(error => {
|
|
32
|
+
this.logRequestCallbackFailure(error);
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
logRequestCallbackFailure(error) {
|
|
36
|
+
const logger = this.logger;
|
|
37
|
+
if (!logger) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
try {
|
|
41
|
+
logger.error('Request callback failed.', error, 'NatsMicroserviceTransport');
|
|
42
|
+
} catch (loggerError) {
|
|
43
|
+
void loggerError;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
30
46
|
|
|
31
47
|
/**
|
|
32
48
|
* Creates a NATS transport using a client and codec supplied by the application.
|
|
@@ -54,6 +70,9 @@ export class NatsMicroserviceTransport {
|
|
|
54
70
|
if (this.closePromise) {
|
|
55
71
|
throw new Error('NATS microservice transport is closing. Wait for close() to complete before listen().');
|
|
56
72
|
}
|
|
73
|
+
if (this.subscriptions.length > 0) {
|
|
74
|
+
throw new Error('NATS subscription cleanup is incomplete. Call close() again before listen().');
|
|
75
|
+
}
|
|
57
76
|
this.closing = false;
|
|
58
77
|
}
|
|
59
78
|
this.handler = handler;
|
|
@@ -65,7 +84,7 @@ export class NatsMicroserviceTransport {
|
|
|
65
84
|
this.handleEventMessageSafely(message);
|
|
66
85
|
}));
|
|
67
86
|
this.subscriptions.push(this.options.client.subscribe(this.messageSubject, message => {
|
|
68
|
-
|
|
87
|
+
this.handleRequestMessageSafely(message);
|
|
69
88
|
}));
|
|
70
89
|
} catch (error) {
|
|
71
90
|
for (const subscription of this.subscriptions.reverse()) {
|
|
@@ -198,23 +217,27 @@ export class NatsMicroserviceTransport {
|
|
|
198
217
|
}
|
|
199
218
|
this.closing = true;
|
|
200
219
|
this.closePromise = (async () => {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
220
|
+
const cleanupErrors = [];
|
|
221
|
+
const retainedSubscriptions = [];
|
|
222
|
+
for (const subscription of this.subscriptions) {
|
|
223
|
+
try {
|
|
204
224
|
subscription.unsubscribe();
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
} finally {
|
|
209
|
-
this.subscriptions = [];
|
|
210
|
-
this.listening = false;
|
|
211
|
-
this.handler = undefined;
|
|
212
|
-
for (const pending of [...this.pending.values()]) {
|
|
213
|
-
pending.reject(new Error('NATS microservice transport closed before response.'));
|
|
225
|
+
} catch (error) {
|
|
226
|
+
cleanupErrors.push(error);
|
|
227
|
+
retainedSubscriptions.push(subscription);
|
|
214
228
|
}
|
|
215
229
|
}
|
|
216
|
-
|
|
217
|
-
|
|
230
|
+
this.subscriptions = retainedSubscriptions;
|
|
231
|
+
this.listening = false;
|
|
232
|
+
this.handler = undefined;
|
|
233
|
+
for (const pending of [...this.pending.values()]) {
|
|
234
|
+
pending.reject(new Error('NATS microservice transport closed before response.'));
|
|
235
|
+
}
|
|
236
|
+
if (cleanupErrors.length === 1) {
|
|
237
|
+
throw cleanupErrors[0];
|
|
238
|
+
}
|
|
239
|
+
if (cleanupErrors.length > 1) {
|
|
240
|
+
throw new AggregateError(cleanupErrors, 'NATS subscription cleanup failed for multiple subscriptions.');
|
|
218
241
|
}
|
|
219
242
|
})();
|
|
220
243
|
try {
|
|
@@ -249,21 +272,21 @@ export class NatsMicroserviceTransport {
|
|
|
249
272
|
if (packet.kind !== 'message') {
|
|
250
273
|
return;
|
|
251
274
|
}
|
|
275
|
+
let response;
|
|
252
276
|
try {
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
}));
|
|
277
|
+
response = {
|
|
278
|
+
payload: await this.handler({
|
|
279
|
+
kind: 'message',
|
|
280
|
+
pattern: packet.pattern,
|
|
281
|
+
payload: packet.payload
|
|
282
|
+
})
|
|
283
|
+
};
|
|
261
284
|
} catch (error) {
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
}));
|
|
285
|
+
response = {
|
|
286
|
+
error: error instanceof Error ? error.message : 'Unhandled microservice error'
|
|
287
|
+
};
|
|
266
288
|
}
|
|
289
|
+
message.respond(this.encode(response));
|
|
267
290
|
}
|
|
268
291
|
decode(data) {
|
|
269
292
|
return JSON.parse(this.options.codec.decode(data));
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { MicroserviceTransport, TransportHandler } from '../types.js';
|
|
1
|
+
import type { MicroserviceTransport, MicroserviceTransportLogger, TransportHandler } from '../types.js';
|
|
2
2
|
interface RabbitMqConsumerLike {
|
|
3
3
|
consume(queue: string, handler: (message: string) => Promise<void> | void): Promise<void>;
|
|
4
4
|
cancel(queue: string): Promise<void>;
|
|
@@ -28,12 +28,20 @@ export declare class RabbitMqMicroserviceTransport implements MicroserviceTransp
|
|
|
28
28
|
private handler;
|
|
29
29
|
private listening;
|
|
30
30
|
private listenPromise;
|
|
31
|
+
private logger;
|
|
31
32
|
private readonly eventQueue;
|
|
32
33
|
private readonly messageQueue;
|
|
33
34
|
private readonly pending;
|
|
34
35
|
private readonly requestTimeoutMs;
|
|
35
36
|
private readonly responseQueue;
|
|
36
37
|
private readonly subscribedQueues;
|
|
38
|
+
private logEventHandlerFailure;
|
|
39
|
+
/**
|
|
40
|
+
* Registers the runtime logger that receives inbound event handler failures.
|
|
41
|
+
*
|
|
42
|
+
* @param logger Logger used to report event handler failures.
|
|
43
|
+
*/
|
|
44
|
+
setLogger(logger: MicroserviceTransportLogger): void;
|
|
37
45
|
/**
|
|
38
46
|
* Creates a RabbitMQ transport with explicit consumer and publisher collaborators.
|
|
39
47
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rabbitmq-transport.d.ts","sourceRoot":"","sources":["../../src/transports/rabbitmq-transport.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"rabbitmq-transport.d.ts","sourceRoot":"","sources":["../../src/transports/rabbitmq-transport.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,2BAA2B,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAGxG,UAAU,oBAAoB;IAC5B,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1F,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACtC;AAED,UAAU,qBAAqB;IAC7B,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACxD;AAWD,mEAAmE;AACnE,MAAM,WAAW,oCAAoC;IACnD,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,qBAAqB,CAAC;IACjC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;GAKG;AACH,qBAAa,6BAA8B,YAAW,qBAAqB;IAwC7D,OAAO,CAAC,QAAQ,CAAC,OAAO;IAvCpC,OAAO,CAAC,YAAY,CAA4B;IAChD,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,OAAO,CAA+B;IAC9C,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,aAAa,CAA4B;IACjD,OAAO,CAAC,MAAM,CAA0C;IACxD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAInB;IACL,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAqB;IAEtD,OAAO,CAAC,sBAAsB;IAQ9B;;;;OAIG;IACH,SAAS,CAAC,MAAM,EAAE,2BAA2B,GAAG,IAAI;IAIpD;;;;OAIG;gBAC0B,OAAO,EAAE,oCAAoC;IAO1E;;;;;OAKG;IACG,MAAM,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IAqDtD;;;;;;;OAOG;IACG,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC;IA6GrF;;;;;;OAMG;IACG,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAc5D;;;;OAIG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YAkDd,oBAAoB;YAuCpB,aAAa;IAsC3B,OAAO,CAAC,cAAc;IAqBtB,OAAO,CAAC,qBAAqB;CAO9B"}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { logTransportEventHandlerFailure } from './event-handler-logger.js';
|
|
2
|
+
|
|
1
3
|
/** Options for configuring the RabbitMQ microservice transport. */
|
|
2
4
|
|
|
3
5
|
/**
|
|
@@ -12,12 +14,29 @@ export class RabbitMqMicroserviceTransport {
|
|
|
12
14
|
handler;
|
|
13
15
|
listening = false;
|
|
14
16
|
listenPromise;
|
|
17
|
+
logger;
|
|
15
18
|
eventQueue;
|
|
16
19
|
messageQueue;
|
|
17
20
|
pending = new Map();
|
|
18
21
|
requestTimeoutMs;
|
|
19
22
|
responseQueue;
|
|
20
23
|
subscribedQueues = new Set();
|
|
24
|
+
logEventHandlerFailure(error) {
|
|
25
|
+
try {
|
|
26
|
+
logTransportEventHandlerFailure(this.logger, 'RabbitMqMicroserviceTransport', error);
|
|
27
|
+
} catch {
|
|
28
|
+
// A failing logger must not mask the event failure the broker still has to observe.
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Registers the runtime logger that receives inbound event handler failures.
|
|
34
|
+
*
|
|
35
|
+
* @param logger Logger used to report event handler failures.
|
|
36
|
+
*/
|
|
37
|
+
setLogger(logger) {
|
|
38
|
+
this.logger = logger;
|
|
39
|
+
}
|
|
21
40
|
|
|
22
41
|
/**
|
|
23
42
|
* Creates a RabbitMQ transport with explicit consumer and publisher collaborators.
|
|
@@ -43,6 +62,9 @@ export class RabbitMqMicroserviceTransport {
|
|
|
43
62
|
if (this.closePromise) {
|
|
44
63
|
throw new Error('RabbitMqMicroserviceTransport is closing. Wait for close() to complete before listen().');
|
|
45
64
|
}
|
|
65
|
+
if (this.subscribedQueues.size > 0) {
|
|
66
|
+
throw new Error('RabbitMQ consumer cleanup is incomplete. Call close() again before listen().');
|
|
67
|
+
}
|
|
46
68
|
this.closing = false;
|
|
47
69
|
}
|
|
48
70
|
this.handler = handler;
|
|
@@ -220,17 +242,15 @@ export class RabbitMqMicroserviceTransport {
|
|
|
220
242
|
}
|
|
221
243
|
}
|
|
222
244
|
try {
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
}
|
|
245
|
+
for (const queue of [...this.subscribedQueues]) {
|
|
246
|
+
try {
|
|
247
|
+
await this.options.consumer.cancel(queue);
|
|
248
|
+
this.subscribedQueues.delete(queue);
|
|
249
|
+
} catch (error) {
|
|
250
|
+
closeError ??= error;
|
|
230
251
|
}
|
|
231
252
|
}
|
|
232
253
|
} finally {
|
|
233
|
-
this.subscribedQueues.clear();
|
|
234
254
|
this.rejectPendingRequests(new Error('RabbitMQ microservice transport closed before response.'));
|
|
235
255
|
this.listening = false;
|
|
236
256
|
this.handler = undefined;
|
|
@@ -263,11 +283,16 @@ export class RabbitMqMicroserviceTransport {
|
|
|
263
283
|
return;
|
|
264
284
|
}
|
|
265
285
|
if (message.kind === 'event') {
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
286
|
+
try {
|
|
287
|
+
await this.handler({
|
|
288
|
+
kind: 'event',
|
|
289
|
+
pattern: message.pattern,
|
|
290
|
+
payload: message.payload
|
|
291
|
+
});
|
|
292
|
+
} catch (error) {
|
|
293
|
+
this.logEventHandlerFailure(error);
|
|
294
|
+
throw error;
|
|
295
|
+
}
|
|
271
296
|
return;
|
|
272
297
|
}
|
|
273
298
|
if (message.kind === 'message' && message.requestId) {
|
|
@@ -15,6 +15,12 @@ export interface RedisStreamClientLike {
|
|
|
15
15
|
blockMs?: number;
|
|
16
16
|
count?: number;
|
|
17
17
|
}): Promise<readonly StreamReadGroupResult[] | null>;
|
|
18
|
+
xautoclaim?(stream: string, group: string, consumer: string, minIdleTimeMs: number, startId: string, options?: {
|
|
19
|
+
count?: number;
|
|
20
|
+
}): Promise<{
|
|
21
|
+
readonly entries: readonly StreamReadGroupResult[];
|
|
22
|
+
readonly nextStartId: string;
|
|
23
|
+
}>;
|
|
18
24
|
xack(stream: string, group: string, id: string): Promise<void>;
|
|
19
25
|
get?(key: string): Promise<string | null>;
|
|
20
26
|
incr?(key: string): Promise<number>;
|
|
@@ -45,6 +51,14 @@ export interface RedisStreamsMicroserviceTransportOptions {
|
|
|
45
51
|
responseRetentionMaxLen?: number;
|
|
46
52
|
consumerGroup?: string;
|
|
47
53
|
namespace?: string;
|
|
54
|
+
/**
|
|
55
|
+
* Minimum pending-entry idle time before `XAUTOCLAIM` recovery in the shared request group
|
|
56
|
+
* or this listener's instance-scoped event group.
|
|
57
|
+
*
|
|
58
|
+
* Defaults to `60_000`; set a non-positive value to disable recovery. The reader client must
|
|
59
|
+
* implement {@link RedisStreamClientLike.xautoclaim} for recovery to run.
|
|
60
|
+
*/
|
|
61
|
+
pendingReclaimIdleMs?: number;
|
|
48
62
|
requestTimeoutMs?: number;
|
|
49
63
|
pollBlockMs?: number;
|
|
50
64
|
}
|
|
@@ -66,9 +80,12 @@ export declare class RedisStreamsMicroserviceTransport implements MicroserviceTr
|
|
|
66
80
|
private messageGroupLeaseRegistered;
|
|
67
81
|
private ownsMessageGroup;
|
|
68
82
|
private readonly pending;
|
|
83
|
+
private readonly pendingReclaimFailureGroups;
|
|
84
|
+
private readonly pendingReclaimStartIds;
|
|
69
85
|
private pollPromises;
|
|
70
86
|
private readonly namespace;
|
|
71
87
|
private readonly consumerGroup;
|
|
88
|
+
private readonly pendingReclaimIdleMs;
|
|
72
89
|
private readonly requestTimeoutMs;
|
|
73
90
|
private readonly pollBlockMs;
|
|
74
91
|
private readonly messageRetentionMaxLen;
|
|
@@ -115,6 +132,7 @@ export declare class RedisStreamsMicroserviceTransport implements MicroserviceTr
|
|
|
115
132
|
private registerMessageGroupLease;
|
|
116
133
|
private releaseMessageGroupLease;
|
|
117
134
|
private pollStream;
|
|
135
|
+
private reclaimPendingEntries;
|
|
118
136
|
private handleStreamEntry;
|
|
119
137
|
private handleInboundRequest;
|
|
120
138
|
private publishFrame;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"redis-streams-transport.d.ts","sourceRoot":"","sources":["../../src/transports/redis-streams-transport.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,2BAA2B,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAGxG,UAAU,qBAAqB;IAC7B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CACnD;AAED,wEAAwE;AACxE,MAAM,WAAW,uBAAuB;IACtC,8EAA8E;IAC9E,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;CACrC;AAED,mGAAmG;AACnG,MAAM,WAAW,qBAAqB;IACpC,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC,EAAE,uBAAuB,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACzG,UAAU,CACR,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,SAAS,MAAM,EAAE,EAC1B,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAC7C,OAAO,CAAC,SAAS,qBAAqB,EAAE,GAAG,IAAI,CAAC,CAAC;IACpD,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/D,GAAG,CAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC1C,IAAI,CAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACpC,IAAI,CAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACpC,GAAG,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACnD,IAAI,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjD,GAAG,CAAC,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/F,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7D;AAED,wEAAwE;AACxE,MAAM,WAAW,wCAAwC;IACvD,YAAY,EAAE,qBAAqB,CAAC;IACpC,YAAY,EAAE,qBAAqB,CAAC;IACpC;;;;OAIG;IACH,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,oFAAoF;IACpF,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAsBD;;;;;GAKG;AACH,qBAAa,iCAAkC,YAAW,qBAAqB;
|
|
1
|
+
{"version":3,"file":"redis-streams-transport.d.ts","sourceRoot":"","sources":["../../src/transports/redis-streams-transport.ts"],"names":[],"mappings":"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"}
|
|
@@ -29,9 +29,12 @@ export class RedisStreamsMicroserviceTransport {
|
|
|
29
29
|
messageGroupLeaseRegistered = false;
|
|
30
30
|
ownsMessageGroup = false;
|
|
31
31
|
pending = new Map();
|
|
32
|
+
pendingReclaimFailureGroups = new Set();
|
|
33
|
+
pendingReclaimStartIds = new Map();
|
|
32
34
|
pollPromises = [];
|
|
33
35
|
namespace;
|
|
34
36
|
consumerGroup;
|
|
37
|
+
pendingReclaimIdleMs;
|
|
35
38
|
requestTimeoutMs;
|
|
36
39
|
pollBlockMs;
|
|
37
40
|
messageRetentionMaxLen;
|
|
@@ -48,6 +51,7 @@ export class RedisStreamsMicroserviceTransport {
|
|
|
48
51
|
this.consumerId = crypto.randomUUID();
|
|
49
52
|
this.namespace = options.namespace ?? 'fluo:streams';
|
|
50
53
|
this.consumerGroup = options.consumerGroup ?? 'fluo-handlers';
|
|
54
|
+
this.pendingReclaimIdleMs = options.pendingReclaimIdleMs ?? 60_000;
|
|
51
55
|
this.requestTimeoutMs = options.requestTimeoutMs ?? 3_000;
|
|
52
56
|
this.pollBlockMs = options.pollBlockMs ?? 500;
|
|
53
57
|
this.messageRetentionMaxLen = options.messageRetentionMaxLen;
|
|
@@ -264,6 +268,8 @@ export class RedisStreamsMicroserviceTransport {
|
|
|
264
268
|
this.handler = undefined;
|
|
265
269
|
this.messageGroupLeaseRegistered = false;
|
|
266
270
|
this.ownsMessageGroup = false;
|
|
271
|
+
this.pendingReclaimFailureGroups.clear();
|
|
272
|
+
this.pendingReclaimStartIds.clear();
|
|
267
273
|
this.pollPromises = [];
|
|
268
274
|
for (const pending of [...this.pending.values()]) {
|
|
269
275
|
pending.reject(new Error('Redis Streams microservice transport closed before response.'));
|
|
@@ -342,7 +348,8 @@ export class RedisStreamsMicroserviceTransport {
|
|
|
342
348
|
async pollStream(stream, group) {
|
|
343
349
|
while (!this.closing) {
|
|
344
350
|
try {
|
|
345
|
-
const
|
|
351
|
+
const reclaimedEntries = await this.reclaimPendingEntries(stream, group);
|
|
352
|
+
const entries = reclaimedEntries.length > 0 ? reclaimedEntries : await this.options.readerClient.xreadgroup(group, this.consumerId, [stream], {
|
|
346
353
|
blockMs: this.pollBlockMs,
|
|
347
354
|
count: 10
|
|
348
355
|
});
|
|
@@ -369,6 +376,29 @@ export class RedisStreamsMicroserviceTransport {
|
|
|
369
376
|
}
|
|
370
377
|
}
|
|
371
378
|
}
|
|
379
|
+
async reclaimPendingEntries(stream, group) {
|
|
380
|
+
const {
|
|
381
|
+
readerClient
|
|
382
|
+
} = this.options;
|
|
383
|
+
if (stream === this.responseStream || this.pendingReclaimIdleMs <= 0 || !readerClient.xautoclaim) {
|
|
384
|
+
return [];
|
|
385
|
+
}
|
|
386
|
+
const groupKey = `${stream}::${group}`;
|
|
387
|
+
try {
|
|
388
|
+
const result = await readerClient.xautoclaim(stream, group, this.consumerId, this.pendingReclaimIdleMs, this.pendingReclaimStartIds.get(groupKey) ?? '0-0', {
|
|
389
|
+
count: 10
|
|
390
|
+
});
|
|
391
|
+
this.pendingReclaimFailureGroups.delete(groupKey);
|
|
392
|
+
this.pendingReclaimStartIds.set(groupKey, result.nextStartId);
|
|
393
|
+
return result.entries;
|
|
394
|
+
} catch (error) {
|
|
395
|
+
if (!this.pendingReclaimFailureGroups.has(groupKey)) {
|
|
396
|
+
this.logger?.error('Redis Streams pending reclaim failed; continuing normal consumption.', error, 'RedisStreamsMicroserviceTransport');
|
|
397
|
+
this.pendingReclaimFailureGroups.add(groupKey);
|
|
398
|
+
}
|
|
399
|
+
return [];
|
|
400
|
+
}
|
|
401
|
+
}
|
|
372
402
|
async handleStreamEntry(stream, message) {
|
|
373
403
|
if (stream === this.messageStream) {
|
|
374
404
|
await this.handleInboundRequest(message);
|