pp-command-bus 1.5.0 → 2.0.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 +400 -1219
- package/dist/command-bus/command-bus.spec.js +138 -359
- package/dist/command-bus/command-bus.spec.js.map +1 -1
- package/dist/command-bus/command.d.ts +3 -4
- package/dist/command-bus/command.js +3 -32
- package/dist/command-bus/command.js.map +1 -1
- package/dist/command-bus/config/command-bus-config.d.ts +80 -21
- package/dist/command-bus/config/command-bus-config.js +104 -58
- package/dist/command-bus/config/command-bus-config.js.map +1 -1
- package/dist/command-bus/config/command-bus-config.spec.js +174 -100
- package/dist/command-bus/config/command-bus-config.spec.js.map +1 -1
- package/dist/command-bus/index.d.ts +41 -50
- package/dist/command-bus/index.js +143 -127
- package/dist/command-bus/index.js.map +1 -1
- package/dist/command-bus/interceptors/index.d.ts +5 -0
- package/dist/command-bus/interceptors/index.js +22 -0
- package/dist/command-bus/interceptors/index.js.map +1 -0
- package/dist/command-bus/interceptors/interceptor.interface.d.ts +35 -0
- package/dist/command-bus/interceptors/interceptor.interface.js +3 -0
- package/dist/command-bus/interceptors/interceptor.interface.js.map +1 -0
- package/dist/command-bus/interceptors/performance-interceptor.d.ts +24 -0
- package/dist/command-bus/interceptors/performance-interceptor.js +86 -0
- package/dist/command-bus/interceptors/performance-interceptor.js.map +1 -0
- package/dist/command-bus/interceptors/performance-interceptor.spec.js +124 -0
- package/dist/command-bus/interceptors/performance-interceptor.spec.js.map +1 -0
- package/dist/command-bus/logging/command-logger.d.ts +2 -0
- package/dist/command-bus/logging/command-logger.js +7 -0
- package/dist/command-bus/logging/command-logger.js.map +1 -1
- package/dist/command-bus/logging/command-logger.spec.js +36 -0
- package/dist/command-bus/logging/command-logger.spec.js.map +1 -1
- package/dist/command-bus/serialization/index.d.ts +6 -0
- package/dist/command-bus/serialization/index.js +9 -0
- package/dist/command-bus/serialization/index.js.map +1 -0
- package/dist/command-bus/serialization/msgpack-serializer.d.ts +26 -0
- package/dist/command-bus/serialization/msgpack-serializer.js +70 -0
- package/dist/command-bus/serialization/msgpack-serializer.js.map +1 -0
- package/dist/command-bus/serialization/msgpack-serializer.spec.js +223 -0
- package/dist/command-bus/serialization/msgpack-serializer.spec.js.map +1 -0
- package/dist/command-bus/serialization/serializer.interface.d.ts +21 -0
- package/dist/command-bus/serialization/serializer.interface.js +3 -0
- package/dist/command-bus/serialization/serializer.interface.js.map +1 -0
- package/dist/command-bus/transport/consumer-loop.d.ts +45 -0
- package/dist/command-bus/transport/consumer-loop.js +90 -0
- package/dist/command-bus/transport/consumer-loop.js.map +1 -0
- package/dist/command-bus/transport/consumer-loop.spec.js +216 -0
- package/dist/command-bus/transport/consumer-loop.spec.js.map +1 -0
- package/dist/command-bus/transport/index.d.ts +21 -0
- package/dist/command-bus/transport/index.js +23 -0
- package/dist/command-bus/transport/index.js.map +1 -0
- package/dist/command-bus/transport/message-processor.d.ts +70 -0
- package/dist/command-bus/transport/message-processor.js +158 -0
- package/dist/command-bus/transport/message-processor.js.map +1 -0
- package/dist/command-bus/transport/message-processor.spec.js +185 -0
- package/dist/command-bus/transport/message-processor.spec.js.map +1 -0
- package/dist/command-bus/transport/pending-recovery.d.ts +54 -0
- package/dist/command-bus/transport/pending-recovery.js +139 -0
- package/dist/command-bus/transport/pending-recovery.js.map +1 -0
- package/dist/command-bus/transport/pending-recovery.spec.js +176 -0
- package/dist/command-bus/transport/pending-recovery.spec.js.map +1 -0
- package/dist/command-bus/transport/redis-codec.d.ts +24 -0
- package/dist/command-bus/transport/redis-codec.js +33 -0
- package/dist/command-bus/transport/redis-codec.js.map +1 -0
- package/dist/command-bus/transport/redis-codec.spec.js +53 -0
- package/dist/command-bus/transport/redis-codec.spec.js.map +1 -0
- package/dist/command-bus/transport/redis-streams-transport.d.ts +94 -0
- package/dist/command-bus/transport/redis-streams-transport.js +143 -0
- package/dist/command-bus/transport/redis-streams-transport.js.map +1 -0
- package/dist/command-bus/transport/redis-streams-transport.spec.js +420 -0
- package/dist/command-bus/transport/redis-streams-transport.spec.js.map +1 -0
- package/dist/command-bus/transport/rpc-handler.d.ts +39 -0
- package/dist/command-bus/transport/rpc-handler.js +87 -0
- package/dist/command-bus/transport/rpc-handler.js.map +1 -0
- package/dist/command-bus/transport/rpc-handler.spec.js +157 -0
- package/dist/command-bus/transport/rpc-handler.spec.js.map +1 -0
- package/dist/command-bus/transport/stream-consumer.d.ts +91 -0
- package/dist/command-bus/transport/stream-consumer.js +182 -0
- package/dist/command-bus/transport/stream-consumer.js.map +1 -0
- package/dist/command-bus/transport/stream-consumer.spec.js +284 -0
- package/dist/command-bus/transport/stream-consumer.spec.js.map +1 -0
- package/dist/command-bus/transport/stream-producer.d.ts +23 -0
- package/dist/command-bus/transport/stream-producer.js +70 -0
- package/dist/command-bus/transport/stream-producer.js.map +1 -0
- package/dist/command-bus/transport/stream-producer.spec.js +125 -0
- package/dist/command-bus/transport/stream-producer.spec.js.map +1 -0
- package/dist/command-bus/transport/transport.interface.d.ts +87 -0
- package/dist/command-bus/transport/transport.interface.js +3 -0
- package/dist/command-bus/transport/transport.interface.js.map +1 -0
- package/dist/command-bus/types/index.d.ts +0 -84
- package/dist/examples/rpc.demo.js +1 -1
- package/dist/examples/rpc.demo.js.map +1 -1
- package/dist/index.d.ts +8 -5
- package/dist/index.js +6 -4
- package/dist/index.js.map +1 -1
- package/dist/pp-command-bus-2.0.1.tgz +0 -0
- package/dist/shared/redis/connection-pool.d.ts +54 -0
- package/dist/shared/redis/connection-pool.js +123 -0
- package/dist/shared/redis/connection-pool.js.map +1 -0
- package/dist/shared/redis/connection-pool.spec.d.ts +1 -0
- package/dist/shared/redis/connection-pool.spec.js +114 -0
- package/dist/shared/redis/connection-pool.spec.js.map +1 -0
- package/dist/shared/redis/index.d.ts +5 -3
- package/dist/shared/redis/index.js +6 -4
- package/dist/shared/redis/index.js.map +1 -1
- package/dist/shared/redis/rpc-connection-pool.d.ts +61 -0
- package/dist/shared/redis/rpc-connection-pool.js +154 -0
- package/dist/shared/redis/rpc-connection-pool.js.map +1 -0
- package/dist/shared/redis/rpc-connection-pool.spec.d.ts +1 -0
- package/dist/shared/redis/rpc-connection-pool.spec.js +173 -0
- package/dist/shared/redis/rpc-connection-pool.spec.js.map +1 -0
- package/dist/shared/types.d.ts +0 -4
- package/dist/shared/utils/error-utils.d.ts +8 -0
- package/dist/shared/utils/error-utils.js +14 -0
- package/dist/shared/utils/error-utils.js.map +1 -0
- package/package.json +12 -12
- package/dist/command-bus/config/auto-config-optimizer.d.ts +0 -35
- package/dist/command-bus/config/auto-config-optimizer.js +0 -52
- package/dist/command-bus/config/auto-config-optimizer.js.map +0 -1
- package/dist/command-bus/config/auto-config-optimizer.spec.js +0 -42
- package/dist/command-bus/config/auto-config-optimizer.spec.js.map +0 -1
- package/dist/command-bus/job/index.d.ts +0 -6
- package/dist/command-bus/job/index.js +0 -15
- package/dist/command-bus/job/index.js.map +0 -1
- package/dist/command-bus/job/job-options-builder.d.ts +0 -21
- package/dist/command-bus/job/job-options-builder.js +0 -58
- package/dist/command-bus/job/job-options-builder.js.map +0 -1
- package/dist/command-bus/job/job-options-builder.spec.js +0 -156
- package/dist/command-bus/job/job-options-builder.spec.js.map +0 -1
- package/dist/command-bus/job/job-processor.d.ts +0 -39
- package/dist/command-bus/job/job-processor.js +0 -203
- package/dist/command-bus/job/job-processor.js.map +0 -1
- package/dist/command-bus/job/job-processor.spec.js +0 -436
- package/dist/command-bus/job/job-processor.spec.js.map +0 -1
- package/dist/command-bus/queue/index.d.ts +0 -5
- package/dist/command-bus/queue/index.js +0 -13
- package/dist/command-bus/queue/index.js.map +0 -1
- package/dist/command-bus/queue/queue-manager.d.ts +0 -56
- package/dist/command-bus/queue/queue-manager.js +0 -163
- package/dist/command-bus/queue/queue-manager.js.map +0 -1
- package/dist/command-bus/queue/queue-manager.spec.js +0 -371
- package/dist/command-bus/queue/queue-manager.spec.js.map +0 -1
- package/dist/command-bus/rpc/index.d.ts +0 -11
- package/dist/command-bus/rpc/index.js +0 -19
- package/dist/command-bus/rpc/index.js.map +0 -1
- package/dist/command-bus/rpc/payload-compression.service.d.ts +0 -50
- package/dist/command-bus/rpc/payload-compression.service.js +0 -215
- package/dist/command-bus/rpc/payload-compression.service.js.map +0 -1
- package/dist/command-bus/rpc/payload-compression.service.spec.js +0 -376
- package/dist/command-bus/rpc/payload-compression.service.spec.js.map +0 -1
- package/dist/command-bus/rpc/rpc-coordinator.d.ts +0 -96
- package/dist/command-bus/rpc/rpc-coordinator.js +0 -500
- package/dist/command-bus/rpc/rpc-coordinator.js.map +0 -1
- package/dist/command-bus/rpc/rpc-coordinator.spec.js +0 -621
- package/dist/command-bus/rpc/rpc-coordinator.spec.js.map +0 -1
- package/dist/command-bus/rpc/rpc-job-cancellation.service.d.ts +0 -82
- package/dist/command-bus/rpc/rpc-job-cancellation.service.js +0 -180
- package/dist/command-bus/rpc/rpc-job-cancellation.service.js.map +0 -1
- package/dist/command-bus/rpc/rpc-job-cancellation.service.spec.js +0 -286
- package/dist/command-bus/rpc/rpc-job-cancellation.service.spec.js.map +0 -1
- package/dist/command-bus/worker/index.d.ts +0 -10
- package/dist/command-bus/worker/index.js +0 -19
- package/dist/command-bus/worker/index.js.map +0 -1
- package/dist/command-bus/worker/worker-benchmark.d.ts +0 -71
- package/dist/command-bus/worker/worker-benchmark.js +0 -202
- package/dist/command-bus/worker/worker-benchmark.js.map +0 -1
- package/dist/command-bus/worker/worker-benchmark.spec.js +0 -310
- package/dist/command-bus/worker/worker-benchmark.spec.js.map +0 -1
- package/dist/command-bus/worker/worker-metrics-collector.d.ts +0 -98
- package/dist/command-bus/worker/worker-metrics-collector.js +0 -242
- package/dist/command-bus/worker/worker-metrics-collector.js.map +0 -1
- package/dist/command-bus/worker/worker-orchestrator.d.ts +0 -70
- package/dist/command-bus/worker/worker-orchestrator.js +0 -339
- package/dist/command-bus/worker/worker-orchestrator.js.map +0 -1
- package/dist/command-bus/worker/worker-orchestrator.spec.js +0 -712
- package/dist/command-bus/worker/worker-orchestrator.spec.js.map +0 -1
- package/dist/examples/auto-config.demo.d.ts +0 -9
- package/dist/examples/auto-config.demo.js +0 -106
- package/dist/examples/auto-config.demo.js.map +0 -1
- package/dist/examples/rpc-compression.demo.d.ts +0 -5
- package/dist/examples/rpc-compression.demo.js +0 -358
- package/dist/examples/rpc-compression.demo.js.map +0 -1
- package/dist/examples/rpc-resilience.demo.d.ts +0 -15
- package/dist/examples/rpc-resilience.demo.js +0 -233
- package/dist/examples/rpc-resilience.demo.js.map +0 -1
- package/dist/pp-command-bus-1.5.0.tgz +0 -0
- package/dist/shared/config/base-config.d.ts +0 -54
- package/dist/shared/config/base-config.js +0 -114
- package/dist/shared/config/base-config.js.map +0 -1
- package/dist/shared/config/base-config.spec.js +0 -204
- package/dist/shared/config/base-config.spec.js.map +0 -1
- package/dist/shared/config/index.d.ts +0 -1
- package/dist/shared/config/index.js +0 -9
- package/dist/shared/config/index.js.map +0 -1
- package/dist/shared/redis/redis-connection-factory.d.ts +0 -66
- package/dist/shared/redis/redis-connection-factory.js +0 -113
- package/dist/shared/redis/redis-connection-factory.js.map +0 -1
- /package/dist/command-bus/{config/auto-config-optimizer.spec.d.ts → interceptors/performance-interceptor.spec.d.ts} +0 -0
- /package/dist/command-bus/{job/job-options-builder.spec.d.ts → serialization/msgpack-serializer.spec.d.ts} +0 -0
- /package/dist/command-bus/{job/job-processor.spec.d.ts → transport/consumer-loop.spec.d.ts} +0 -0
- /package/dist/command-bus/{queue/queue-manager.spec.d.ts → transport/message-processor.spec.d.ts} +0 -0
- /package/dist/command-bus/{rpc/payload-compression.service.spec.d.ts → transport/pending-recovery.spec.d.ts} +0 -0
- /package/dist/command-bus/{rpc/rpc-coordinator.spec.d.ts → transport/redis-codec.spec.d.ts} +0 -0
- /package/dist/command-bus/{rpc/rpc-job-cancellation.service.spec.d.ts → transport/redis-streams-transport.spec.d.ts} +0 -0
- /package/dist/command-bus/{worker/worker-benchmark.spec.d.ts → transport/rpc-handler.spec.d.ts} +0 -0
- /package/dist/command-bus/{worker/worker-orchestrator.spec.d.ts → transport/stream-consumer.spec.d.ts} +0 -0
- /package/dist/{shared/config/base-config.spec.d.ts → command-bus/transport/stream-producer.spec.d.ts} +0 -0
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
const redis_streams_transport_1 = __importDefault(require("./redis-streams-transport"));
|
|
16
|
+
/**
|
|
17
|
+
* Tworzy mock Redis connection z disconnect() który przerywa blocking operations
|
|
18
|
+
* Symuluje prawdziwe zachowanie: disconnect() zamyka socket → XREADGROUP BLOCK rejectuje
|
|
19
|
+
*/
|
|
20
|
+
const createMockConnection = () => {
|
|
21
|
+
/** Rejecty pending blocking operations — disconnect() je wywołuje */
|
|
22
|
+
const pendingRejects = [];
|
|
23
|
+
return {
|
|
24
|
+
xadd: jest.fn().mockResolvedValue('1700000000000-0'),
|
|
25
|
+
xreadgroup: jest.fn().mockImplementation(() => new Promise((_resolve, reject) => {
|
|
26
|
+
pendingRejects.push(reject);
|
|
27
|
+
})),
|
|
28
|
+
xack: jest.fn().mockResolvedValue(1),
|
|
29
|
+
xgroup: jest.fn().mockResolvedValue('OK'),
|
|
30
|
+
xpending: jest.fn().mockResolvedValue([]),
|
|
31
|
+
xclaim: jest.fn().mockResolvedValue([]),
|
|
32
|
+
xtrim: jest.fn().mockResolvedValue(0),
|
|
33
|
+
brpop: jest.fn().mockResolvedValue(null),
|
|
34
|
+
lpush: jest.fn().mockResolvedValue(1),
|
|
35
|
+
expire: jest.fn().mockResolvedValue(1),
|
|
36
|
+
del: jest.fn().mockResolvedValue(1),
|
|
37
|
+
quit: jest.fn().mockResolvedValue('OK'),
|
|
38
|
+
on: jest.fn(),
|
|
39
|
+
disconnect: jest.fn().mockImplementation(() => {
|
|
40
|
+
// Przerwij pending blocking operations — symuluje zamknięcie socketa
|
|
41
|
+
const disconnectError = new Error('Connection is closed.');
|
|
42
|
+
while (pendingRejects.length > 0) {
|
|
43
|
+
const reject = pendingRejects.shift();
|
|
44
|
+
if (reject)
|
|
45
|
+
reject(disconnectError);
|
|
46
|
+
}
|
|
47
|
+
}),
|
|
48
|
+
pipeline: jest.fn().mockReturnValue({
|
|
49
|
+
xadd: jest.fn().mockReturnThis(),
|
|
50
|
+
xack: jest.fn().mockReturnThis(),
|
|
51
|
+
xtrim: jest.fn().mockReturnThis(),
|
|
52
|
+
lpush: jest.fn().mockReturnThis(),
|
|
53
|
+
expire: jest.fn().mockReturnThis(),
|
|
54
|
+
exec: jest.fn().mockResolvedValue([
|
|
55
|
+
[null, '1700000000000-0'],
|
|
56
|
+
[null, '1700000000001-0'],
|
|
57
|
+
]),
|
|
58
|
+
}),
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
const createMockPool = (conn) => ({
|
|
62
|
+
next: jest.fn().mockReturnValue(conn),
|
|
63
|
+
createDedicated: jest.fn().mockReturnValue(conn),
|
|
64
|
+
close: jest.fn().mockResolvedValue(undefined),
|
|
65
|
+
size: 4,
|
|
66
|
+
});
|
|
67
|
+
const createMockRpcPool = () => {
|
|
68
|
+
const rpcConn = createMockConnection();
|
|
69
|
+
return {
|
|
70
|
+
acquire: jest.fn().mockResolvedValue(rpcConn),
|
|
71
|
+
release: jest.fn(),
|
|
72
|
+
close: jest.fn().mockResolvedValue(undefined),
|
|
73
|
+
activeCount: 0,
|
|
74
|
+
waitingCount: 0,
|
|
75
|
+
};
|
|
76
|
+
};
|
|
77
|
+
const createMockSerializer = () => ({
|
|
78
|
+
serialize: jest.fn((data) => Buffer.from(JSON.stringify(data))),
|
|
79
|
+
deserialize: jest.fn((buf) => JSON.parse(buf.toString())),
|
|
80
|
+
});
|
|
81
|
+
const createMockLogger = () => ({
|
|
82
|
+
log: jest.fn(),
|
|
83
|
+
error: jest.fn(),
|
|
84
|
+
warn: jest.fn(),
|
|
85
|
+
debug: jest.fn(),
|
|
86
|
+
});
|
|
87
|
+
describe('RedisStreamsTransport', () => {
|
|
88
|
+
let transport;
|
|
89
|
+
let mockConn;
|
|
90
|
+
let mockPool;
|
|
91
|
+
let mockRpcPool;
|
|
92
|
+
let mockSerializer;
|
|
93
|
+
let logger;
|
|
94
|
+
beforeEach(() => {
|
|
95
|
+
jest.useFakeTimers({ legacyFakeTimers: false });
|
|
96
|
+
jest.clearAllMocks();
|
|
97
|
+
mockConn = createMockConnection();
|
|
98
|
+
mockPool = createMockPool(mockConn);
|
|
99
|
+
mockRpcPool = createMockRpcPool();
|
|
100
|
+
mockSerializer = createMockSerializer();
|
|
101
|
+
logger = createMockLogger();
|
|
102
|
+
transport = new redis_streams_transport_1.default({
|
|
103
|
+
pool: mockPool,
|
|
104
|
+
rpcPool: mockRpcPool,
|
|
105
|
+
serializer: mockSerializer,
|
|
106
|
+
logger,
|
|
107
|
+
maxRetained: 10000,
|
|
108
|
+
maxAttempts: 3,
|
|
109
|
+
claimTimeout: 30000,
|
|
110
|
+
batchSize: 10,
|
|
111
|
+
concurrency: 100,
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
afterEach(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
115
|
+
yield transport.close();
|
|
116
|
+
jest.useRealTimers();
|
|
117
|
+
}));
|
|
118
|
+
describe('enqueue()', () => {
|
|
119
|
+
it('powinien wysłać dane do strumienia przez XADD', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
120
|
+
const data = Buffer.from('test-data');
|
|
121
|
+
const messageId = yield transport.enqueue('cmd:TestCommand', data);
|
|
122
|
+
// Base64: ioredis dekoduje string responses jako UTF-8, co korumpuje surowe bajty
|
|
123
|
+
expect(mockConn.xadd).toHaveBeenCalledWith('cmd:TestCommand', '*', 'data', data.toString('base64'));
|
|
124
|
+
expect(messageId).toBe('1700000000000-0');
|
|
125
|
+
}));
|
|
126
|
+
it('powinien używać połączenia z puli (round-robin)', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
127
|
+
yield transport.enqueue('cmd:Test', Buffer.from('data'));
|
|
128
|
+
expect(mockPool.next).toHaveBeenCalledTimes(1);
|
|
129
|
+
}));
|
|
130
|
+
});
|
|
131
|
+
describe('enqueueBatch()', () => {
|
|
132
|
+
it('powinien wysłać wiele komend w jednym pipeline', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
133
|
+
const entries = [
|
|
134
|
+
{ streamName: 'cmd:A', data: Buffer.from('data-a') },
|
|
135
|
+
{ streamName: 'cmd:B', data: Buffer.from('data-b') },
|
|
136
|
+
];
|
|
137
|
+
const ids = yield transport.enqueueBatch(entries);
|
|
138
|
+
expect(mockPool.next).toHaveBeenCalled();
|
|
139
|
+
expect(ids).toHaveLength(2);
|
|
140
|
+
}));
|
|
141
|
+
it('powinien zwrócić pustą tablicę dla pustego wejścia', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
142
|
+
const ids = yield transport.enqueueBatch([]);
|
|
143
|
+
expect(ids).toEqual([]);
|
|
144
|
+
}));
|
|
145
|
+
});
|
|
146
|
+
describe('consume()', () => {
|
|
147
|
+
it('powinien utworzyć consumer group i rozpocząć nasłuchiwanie', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
148
|
+
jest.useRealTimers(); // Potrzebujemy real timers dla setTimeout
|
|
149
|
+
const handler = jest.fn().mockResolvedValue(undefined);
|
|
150
|
+
// Mockujemy XREADGROUP:
|
|
151
|
+
// 1. Pierwsza wiadomość — dane do przetworzenia
|
|
152
|
+
// 2. Potem blocking promise (symuluje BLOCK 5000)
|
|
153
|
+
const consumerConn = createMockConnection();
|
|
154
|
+
// Dane w strumieniu są base64-encoded (jak w prawdziwym Redis)
|
|
155
|
+
const testPayload = Buffer.from('test-payload').toString('base64');
|
|
156
|
+
consumerConn.xreadgroup.mockResolvedValueOnce([
|
|
157
|
+
['cmd:TestCommand', [['1700000000000-0', ['data', testPayload]]]],
|
|
158
|
+
]);
|
|
159
|
+
mockPool.createDedicated.mockReturnValue(consumerConn);
|
|
160
|
+
yield transport.consume('cmd:TestCommand', 'workers', handler);
|
|
161
|
+
// Poczekaj na przetworzenie wiadomości
|
|
162
|
+
yield new Promise((r) => setTimeout(r, 50));
|
|
163
|
+
// Powinien utworzyć consumer group
|
|
164
|
+
expect(consumerConn.xgroup).toHaveBeenCalledWith('CREATE', 'cmd:TestCommand', 'workers', '0', 'MKSTREAM');
|
|
165
|
+
// Powinien wywołać handler z danymi
|
|
166
|
+
expect(handler).toHaveBeenCalledTimes(1);
|
|
167
|
+
expect(handler).toHaveBeenCalledWith(expect.any(Buffer));
|
|
168
|
+
// XACK idzie przez pool connection (nie dedykowane consumer conn — XREADGROUP BLOCK)
|
|
169
|
+
expect(mockConn.xack).toHaveBeenCalledWith('cmd:TestCommand', 'workers', '1700000000000-0');
|
|
170
|
+
}));
|
|
171
|
+
it('powinien obsłużyć błąd tworzenia grupy jeśli już istnieje', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
172
|
+
const handler = jest.fn().mockResolvedValue(undefined);
|
|
173
|
+
const consumerConn = createMockConnection();
|
|
174
|
+
consumerConn.xgroup.mockRejectedValueOnce(new Error('BUSYGROUP Consumer Group name already exists'));
|
|
175
|
+
mockPool.createDedicated.mockReturnValue(consumerConn);
|
|
176
|
+
// Nie powinien rzucić błędu
|
|
177
|
+
yield transport.consume('cmd:Test', 'workers', handler);
|
|
178
|
+
}));
|
|
179
|
+
});
|
|
180
|
+
describe('rpcCall()', () => {
|
|
181
|
+
it('powinien wysłać komendę i czekać na odpowiedź przez BRPOP', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
182
|
+
const responseData = Buffer.from(JSON.stringify({ result: 42 }));
|
|
183
|
+
const rpcConn = createMockConnection();
|
|
184
|
+
// BRPOP zwraca base64-encoded string (rpcRespond koduje dane jako base64)
|
|
185
|
+
rpcConn.brpop.mockResolvedValue(['rpc:res:test-id', responseData.toString('base64')]);
|
|
186
|
+
mockRpcPool.acquire.mockResolvedValue(rpcConn);
|
|
187
|
+
const result = yield transport.rpcCall('TestCommand', Buffer.from('request-data'), 5000);
|
|
188
|
+
// Powinien wysłać komendę do strumienia
|
|
189
|
+
expect(mockConn.xadd).toHaveBeenCalled();
|
|
190
|
+
// Powinien pobrać połączenie z RPC pool
|
|
191
|
+
expect(mockRpcPool.acquire).toHaveBeenCalled();
|
|
192
|
+
// Powinien czekać na odpowiedź przez BRPOP
|
|
193
|
+
expect(rpcConn.brpop).toHaveBeenCalled();
|
|
194
|
+
// Powinien zwolnić połączenie po zakończeniu
|
|
195
|
+
expect(mockRpcPool.release).toHaveBeenCalledWith(rpcConn);
|
|
196
|
+
expect(result).toBeInstanceOf(Buffer);
|
|
197
|
+
}));
|
|
198
|
+
it('powinien rzucić błąd przy timeout (BRPOP zwraca null)', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
199
|
+
const rpcConn = createMockConnection();
|
|
200
|
+
rpcConn.brpop.mockResolvedValue(null);
|
|
201
|
+
mockRpcPool.acquire.mockResolvedValue(rpcConn);
|
|
202
|
+
yield expect(transport.rpcCall('TestCommand', Buffer.from('data'), 1000)).rejects.toThrow('RPC timeout');
|
|
203
|
+
// Powinien zwolnić połączenie nawet przy błędzie
|
|
204
|
+
expect(mockRpcPool.release).toHaveBeenCalledWith(rpcConn);
|
|
205
|
+
}));
|
|
206
|
+
});
|
|
207
|
+
describe('rpcRespond()', () => {
|
|
208
|
+
it('powinien wysłać odpowiedź przez pipeline LPUSH + EXPIRE', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
209
|
+
const responseData = Buffer.from('response');
|
|
210
|
+
yield transport.rpcRespond('rpc:res:test-id', responseData, 60);
|
|
211
|
+
// Powinien użyć pipeline
|
|
212
|
+
expect(mockConn.pipeline).toHaveBeenCalled();
|
|
213
|
+
}));
|
|
214
|
+
});
|
|
215
|
+
describe('XTRIM throttling (H1+H2)', () => {
|
|
216
|
+
it('powinien wykonać XTRIM co batchSize wiadomości', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
217
|
+
jest.useRealTimers();
|
|
218
|
+
const handler = jest.fn().mockResolvedValue(undefined);
|
|
219
|
+
const consumerConn = createMockConnection();
|
|
220
|
+
// Symuluj 10 wiadomości (batchSize = 10)
|
|
221
|
+
const testPayload = Buffer.from('payload').toString('base64');
|
|
222
|
+
const messages = Array.from({ length: 10 }, (_, i) => [
|
|
223
|
+
`170000000000${i}-0`,
|
|
224
|
+
['data', testPayload],
|
|
225
|
+
]);
|
|
226
|
+
consumerConn.xreadgroup.mockResolvedValueOnce([['cmd:Test', messages]]);
|
|
227
|
+
mockPool.createDedicated.mockReturnValue(consumerConn);
|
|
228
|
+
yield transport.consume('cmd:Test', 'workers', handler);
|
|
229
|
+
// Poczekaj na przetworzenie
|
|
230
|
+
yield new Promise((r) => setTimeout(r, 100));
|
|
231
|
+
// Pipeline powinien być użyty dokładnie raz (10. wiadomość = batchSize)
|
|
232
|
+
// Wiadomości 1-9: sam XACK, wiadomość 10: pipeline XACK+XTRIM
|
|
233
|
+
const pipelineCalls = mockConn.pipeline.mock.calls.length;
|
|
234
|
+
expect(pipelineCalls).toBeGreaterThanOrEqual(1);
|
|
235
|
+
// XTRIM NIE powinien być wywołany bezpośrednio (fire-and-forget usunięty)
|
|
236
|
+
expect(mockConn.xtrim).not.toHaveBeenCalled();
|
|
237
|
+
}));
|
|
238
|
+
});
|
|
239
|
+
describe('RPC marker field (H3)', () => {
|
|
240
|
+
it('powinien dodać marker rpc do XADD dla RPC', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
241
|
+
const rpcConn = createMockConnection();
|
|
242
|
+
const responseData = Buffer.from(JSON.stringify({ result: 42 }));
|
|
243
|
+
rpcConn.brpop.mockResolvedValue(['rpc:res:test-id', responseData.toString('base64')]);
|
|
244
|
+
mockRpcPool.acquire.mockResolvedValue(rpcConn);
|
|
245
|
+
yield transport.rpcCall('TestCommand', Buffer.from('request-data'), 5000);
|
|
246
|
+
// XADD powinien mieć 6 argumentów: streamName, *, 'data', value, 'rpc', '1'
|
|
247
|
+
const xaddCall = mockConn.xadd.mock.calls[0];
|
|
248
|
+
expect(xaddCall).toContain('rpc');
|
|
249
|
+
expect(xaddCall).toContain('1');
|
|
250
|
+
}));
|
|
251
|
+
it('powinien użyć markera rpc do identyfikacji RPC envelope', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
252
|
+
jest.useRealTimers();
|
|
253
|
+
const handler = jest.fn().mockResolvedValue('rpc-result');
|
|
254
|
+
const consumerConn = createMockConnection();
|
|
255
|
+
// Symuluj RPC wiadomość z markerem
|
|
256
|
+
const envelope = {
|
|
257
|
+
commandData: Buffer.from('cmd-data').toString('base64'),
|
|
258
|
+
rpc: { correlationId: 'test', responseKey: 'rpc:res:test' },
|
|
259
|
+
};
|
|
260
|
+
const envelopePayload = Buffer.from(JSON.stringify(envelope)).toString('base64');
|
|
261
|
+
consumerConn.xreadgroup.mockResolvedValueOnce([
|
|
262
|
+
['cmd:Test', [['msg-rpc', ['data', envelopePayload, 'rpc', '1']]]],
|
|
263
|
+
]);
|
|
264
|
+
mockPool.createDedicated.mockReturnValue(consumerConn);
|
|
265
|
+
yield transport.consume('cmd:Test', 'workers', handler);
|
|
266
|
+
yield new Promise((r) => setTimeout(r, 50));
|
|
267
|
+
// Handler powinien być wywołany
|
|
268
|
+
expect(handler).toHaveBeenCalledTimes(1);
|
|
269
|
+
}));
|
|
270
|
+
it('powinien obsługiwać zwykłe komendy bez markera', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
271
|
+
jest.useRealTimers();
|
|
272
|
+
const handler = jest.fn().mockResolvedValue(undefined);
|
|
273
|
+
const consumerConn = createMockConnection();
|
|
274
|
+
// Zwykła komenda — bez markera 'rpc'
|
|
275
|
+
const testPayload = Buffer.from('normal-command').toString('base64');
|
|
276
|
+
consumerConn.xreadgroup.mockResolvedValueOnce([
|
|
277
|
+
['cmd:Test', [['msg-normal', ['data', testPayload]]]],
|
|
278
|
+
]);
|
|
279
|
+
mockPool.createDedicated.mockReturnValue(consumerConn);
|
|
280
|
+
yield transport.consume('cmd:Test', 'workers', handler);
|
|
281
|
+
yield new Promise((r) => setTimeout(r, 50));
|
|
282
|
+
// Handler powinien być wywołany z surowymi danymi (nie RPC)
|
|
283
|
+
expect(handler).toHaveBeenCalledTimes(1);
|
|
284
|
+
expect(handler).toHaveBeenCalledWith(expect.any(Buffer));
|
|
285
|
+
}));
|
|
286
|
+
});
|
|
287
|
+
describe('close()', () => {
|
|
288
|
+
it('powinien zamknąć transport gracefully', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
289
|
+
yield transport.close();
|
|
290
|
+
expect(mockPool.close).toHaveBeenCalled();
|
|
291
|
+
expect(mockRpcPool.close).toHaveBeenCalled();
|
|
292
|
+
}));
|
|
293
|
+
it('powinien być idempotentny (drugie close nie rzuca)', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
294
|
+
yield transport.close();
|
|
295
|
+
yield transport.close();
|
|
296
|
+
}));
|
|
297
|
+
it('powinien czekać na zakończenie aktywnych processMessage przed zamknięciem puli', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
298
|
+
jest.useRealTimers();
|
|
299
|
+
// Przygotuj consumer z wolnym handlerem
|
|
300
|
+
let handlerResolve;
|
|
301
|
+
const handlerPromise = new Promise((resolve) => {
|
|
302
|
+
handlerResolve = resolve;
|
|
303
|
+
});
|
|
304
|
+
const handler = jest.fn().mockReturnValue(handlerPromise);
|
|
305
|
+
const consumerConn = createMockConnection();
|
|
306
|
+
const testPayload = Buffer.from('test-payload').toString('base64');
|
|
307
|
+
// Pierwsza wiadomość → przetworzenie, kolejne → domyślne interruptible blocking
|
|
308
|
+
consumerConn.xreadgroup.mockResolvedValueOnce([
|
|
309
|
+
['cmd:Test', [['1700000000000-0', ['data', testPayload]]]],
|
|
310
|
+
]);
|
|
311
|
+
mockPool.createDedicated.mockReturnValue(consumerConn);
|
|
312
|
+
yield transport.consume('cmd:Test', 'workers', handler);
|
|
313
|
+
// Poczekaj na start przetwarzania
|
|
314
|
+
yield new Promise((r) => setTimeout(r, 50));
|
|
315
|
+
expect(handler).toHaveBeenCalledTimes(1);
|
|
316
|
+
// Zamknij transport — close powinno czekać na handler
|
|
317
|
+
const closePromise = transport.close();
|
|
318
|
+
// Handler jeszcze pracuje — pool.close() jeszcze nie powinien być wywołany
|
|
319
|
+
yield new Promise((r) => setTimeout(r, 20));
|
|
320
|
+
// Zakończ handler
|
|
321
|
+
handlerResolve();
|
|
322
|
+
yield closePromise;
|
|
323
|
+
// Pool zamknięty dopiero po zakończeniu handlera
|
|
324
|
+
expect(mockPool.close).toHaveBeenCalled();
|
|
325
|
+
}));
|
|
326
|
+
});
|
|
327
|
+
describe('recoverPending()', () => {
|
|
328
|
+
/**
|
|
329
|
+
* Pomocnik — wyciąga wewnętrzne komponenty fasady do testowania recovery
|
|
330
|
+
* Po refaktorze SRP: recoverPending jest w PendingRecovery, processMessage w StreamConsumer
|
|
331
|
+
*/
|
|
332
|
+
const getInternals = (t) => {
|
|
333
|
+
const any = t;
|
|
334
|
+
return {
|
|
335
|
+
recovery: any['recovery'],
|
|
336
|
+
consumer: any['consumer'],
|
|
337
|
+
};
|
|
338
|
+
};
|
|
339
|
+
it('powinien przetworzyć wiadomość po XCLAIM', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
340
|
+
jest.useRealTimers();
|
|
341
|
+
const handler = jest.fn().mockResolvedValue('ok');
|
|
342
|
+
const consumerConn = createMockConnection();
|
|
343
|
+
mockPool.createDedicated.mockReturnValue(consumerConn);
|
|
344
|
+
// Symuluj XPENDING z 1 stalled wiadomością
|
|
345
|
+
const testPayload = Buffer.from('recovered-data').toString('base64');
|
|
346
|
+
mockConn.xpending.mockResolvedValue([['msg-1', 'consumer-old', 60000, 1]]);
|
|
347
|
+
// XCLAIM zwraca dane wiadomości
|
|
348
|
+
mockConn.xclaim.mockResolvedValue([['msg-1', ['data', testPayload]]]);
|
|
349
|
+
yield transport.consume('cmd:Test', 'workers', handler);
|
|
350
|
+
// Wywołujemy recoverPending bezpośrednio przez wewnętrzny komponent recovery
|
|
351
|
+
const { recovery, consumer } = getInternals(transport);
|
|
352
|
+
yield recovery['recoverPending']('cmd:Test', 'workers', handler, consumer);
|
|
353
|
+
// Poczekaj na przetworzenie
|
|
354
|
+
yield new Promise((r) => setTimeout(r, 50));
|
|
355
|
+
// Handler powinien być wywołany z recovered danymi
|
|
356
|
+
expect(handler).toHaveBeenCalledWith(expect.any(Buffer));
|
|
357
|
+
// XACK powinno być wywołane
|
|
358
|
+
expect(mockConn.xack).toHaveBeenCalledWith('cmd:Test', 'workers', 'msg-1');
|
|
359
|
+
}));
|
|
360
|
+
it('powinien przenieść do DLQ po maxAttempts (D3)', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
361
|
+
jest.useRealTimers();
|
|
362
|
+
const handler = jest.fn().mockResolvedValue('ok');
|
|
363
|
+
const consumerConn = createMockConnection();
|
|
364
|
+
mockPool.createDedicated.mockReturnValue(consumerConn);
|
|
365
|
+
const testPayload = Buffer.from('dead-data').toString('base64');
|
|
366
|
+
// Wiadomość z deliveryCount >= maxAttempts (3)
|
|
367
|
+
mockConn.xpending.mockResolvedValue([['msg-dead', 'consumer-old', 60000, 3]]);
|
|
368
|
+
// D3: XCLAIM teraz jest wywoływany żeby pobrać dane do DLQ
|
|
369
|
+
mockConn.xclaim.mockResolvedValue([['msg-dead', ['data', testPayload]]]);
|
|
370
|
+
yield transport.consume('cmd:Test', 'workers', handler);
|
|
371
|
+
const { recovery, consumer } = getInternals(transport);
|
|
372
|
+
yield recovery['recoverPending']('cmd:Test', 'workers', handler, consumer);
|
|
373
|
+
// XCLAIM powinno być wywołane (pobiera dane do DLQ)
|
|
374
|
+
expect(mockConn.xclaim).toHaveBeenCalled();
|
|
375
|
+
// XADD do DLQ strumienia
|
|
376
|
+
expect(mockConn.xadd).toHaveBeenCalledWith('dlq:cmd:Test', '*', 'data', testPayload, 'original_stream', 'cmd:Test', 'delivery_count', '3');
|
|
377
|
+
// XACK po przeniesieniu do DLQ
|
|
378
|
+
expect(mockConn.xack).toHaveBeenCalledWith('cmd:Test', 'workers', 'msg-dead');
|
|
379
|
+
// Handler NIE powinien być wywołany
|
|
380
|
+
expect(handler).not.toHaveBeenCalled();
|
|
381
|
+
// Powinien zalogować
|
|
382
|
+
expect(logger.error).toHaveBeenCalledWith('Wiadomość przeniesiona do Dead Letter Queue', expect.objectContaining({
|
|
383
|
+
messageId: 'msg-dead',
|
|
384
|
+
dlqStream: 'dlq:cmd:Test',
|
|
385
|
+
deliveryCount: 3,
|
|
386
|
+
}));
|
|
387
|
+
}));
|
|
388
|
+
it('nie powinien duplikować gdy wiadomość jest już przetwarzana', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
389
|
+
jest.useRealTimers();
|
|
390
|
+
// Handler który trwa długo
|
|
391
|
+
let handlerCallCount = 0;
|
|
392
|
+
const handler = jest.fn().mockImplementation(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
393
|
+
handlerCallCount++;
|
|
394
|
+
yield new Promise((r) => setTimeout(r, 100));
|
|
395
|
+
return 'ok';
|
|
396
|
+
}));
|
|
397
|
+
const consumerConn = createMockConnection();
|
|
398
|
+
const testPayload = Buffer.from('dedup-data').toString('base64');
|
|
399
|
+
// Consumer loop dostaje wiadomość — po niej domyślne interruptible blocking
|
|
400
|
+
consumerConn.xreadgroup.mockResolvedValueOnce([
|
|
401
|
+
['cmd:Test', [['msg-dedup', ['data', testPayload]]]],
|
|
402
|
+
]);
|
|
403
|
+
mockPool.createDedicated.mockReturnValue(consumerConn);
|
|
404
|
+
// XCLAIM też zwraca tę samą wiadomość
|
|
405
|
+
mockConn.xpending.mockResolvedValue([['msg-dedup', 'consumer-old', 60000, 1]]);
|
|
406
|
+
mockConn.xclaim.mockResolvedValue([['msg-dedup', ['data', testPayload]]]);
|
|
407
|
+
yield transport.consume('cmd:Test', 'workers', handler);
|
|
408
|
+
// Poczekaj aż consumer loop zacznie przetwarzać
|
|
409
|
+
yield new Promise((r) => setTimeout(r, 30));
|
|
410
|
+
// Recovery próbuje XCLAIM tej samej wiadomości — przez wewnętrzny komponent
|
|
411
|
+
const { recovery, consumer } = getInternals(transport);
|
|
412
|
+
yield recovery['recoverPending']('cmd:Test', 'workers', handler, consumer);
|
|
413
|
+
// Poczekaj na zakończenie
|
|
414
|
+
yield new Promise((r) => setTimeout(r, 200));
|
|
415
|
+
// Handler powinien być wywołany tylko RAZ (deduplicacja)
|
|
416
|
+
expect(handlerCallCount).toBe(1);
|
|
417
|
+
}));
|
|
418
|
+
});
|
|
419
|
+
});
|
|
420
|
+
//# sourceMappingURL=redis-streams-transport.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis-streams-transport.spec.js","sourceRoot":"","sources":["../../../src/command-bus/transport/redis-streams-transport.spec.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { ISerializer } from '../serialization/serializer.interface';
|
|
2
|
+
import type { IRpcTransport, RpcMessageMetadata } from './transport.interface';
|
|
3
|
+
import type RedisConnectionPool from '../../shared/redis/connection-pool';
|
|
4
|
+
import type RpcConnectionPool from '../../shared/redis/rpc-connection-pool';
|
|
5
|
+
/**
|
|
6
|
+
* Dane envelope dla RPC — wiadomość w strumieniu zawiera zarówno
|
|
7
|
+
* dane komendy jak i metadane RPC do routingu odpowiedzi
|
|
8
|
+
*/
|
|
9
|
+
export interface RpcEnvelope {
|
|
10
|
+
/** Surowe dane komendy zakodowane jako base64 string */
|
|
11
|
+
commandData: string;
|
|
12
|
+
/** Metadane RPC */
|
|
13
|
+
rpc: RpcMessageMetadata;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Handler RPC — odpowiada za request/response przez Redis Streams + LPUSH/BRPOP
|
|
17
|
+
* Jedna odpowiedzialność: RPC call i respond
|
|
18
|
+
*/
|
|
19
|
+
export declare class RpcHandler implements IRpcTransport {
|
|
20
|
+
private readonly pool;
|
|
21
|
+
private readonly rpcPool;
|
|
22
|
+
private readonly serializer;
|
|
23
|
+
constructor(pool: RedisConnectionPool, rpcPool: RpcConnectionPool, serializer: ISerializer);
|
|
24
|
+
/**
|
|
25
|
+
* RPC: wysyła request do strumienia i czeka na odpowiedź via BRPOP
|
|
26
|
+
*
|
|
27
|
+
* Flow (4 komendy Redis):
|
|
28
|
+
* 1. XADD — komenda z metadanymi RPC do strumienia
|
|
29
|
+
* 2. BRPOP — czekaj na odpowiedź na dedykowanym połączeniu
|
|
30
|
+
* 3. Worker: LPUSH — odpowiedź na klucz
|
|
31
|
+
* 4. DEL — cleanup klucza (TTL safety net)
|
|
32
|
+
*/
|
|
33
|
+
rpcCall(commandName: string, data: Buffer, timeout: number): Promise<Buffer>;
|
|
34
|
+
/**
|
|
35
|
+
* RPC: odpowiada na request przez LPUSH + EXPIRE w jednym pipeline
|
|
36
|
+
* Wywoływane przez worker po przetworzeniu komendy RPC
|
|
37
|
+
*/
|
|
38
|
+
rpcRespond(responseKey: string, data: Buffer, ttl: number): Promise<void>;
|
|
39
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.RpcHandler = void 0;
|
|
13
|
+
const crypto_1 = require("crypto");
|
|
14
|
+
const redis_codec_1 = require("./redis-codec");
|
|
15
|
+
/**
|
|
16
|
+
* Handler RPC — odpowiada za request/response przez Redis Streams + LPUSH/BRPOP
|
|
17
|
+
* Jedna odpowiedzialność: RPC call i respond
|
|
18
|
+
*/
|
|
19
|
+
class RpcHandler {
|
|
20
|
+
constructor(pool, rpcPool, serializer) {
|
|
21
|
+
this.pool = pool;
|
|
22
|
+
this.rpcPool = rpcPool;
|
|
23
|
+
this.serializer = serializer;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* RPC: wysyła request do strumienia i czeka na odpowiedź via BRPOP
|
|
27
|
+
*
|
|
28
|
+
* Flow (4 komendy Redis):
|
|
29
|
+
* 1. XADD — komenda z metadanymi RPC do strumienia
|
|
30
|
+
* 2. BRPOP — czekaj na odpowiedź na dedykowanym połączeniu
|
|
31
|
+
* 3. Worker: LPUSH — odpowiedź na klucz
|
|
32
|
+
* 4. DEL — cleanup klucza (TTL safety net)
|
|
33
|
+
*/
|
|
34
|
+
rpcCall(commandName, data, timeout) {
|
|
35
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
36
|
+
// M1: Walidacja timeout — zapobiega infinite BRPOP przy 0/negatywnych/Infinity
|
|
37
|
+
if (!Number.isFinite(timeout) || timeout <= 0) {
|
|
38
|
+
throw new Error(`Nieprawidłowy timeout RPC: ${timeout}ms`);
|
|
39
|
+
}
|
|
40
|
+
const correlationId = (0, crypto_1.randomUUID)();
|
|
41
|
+
const responseKey = `rpc:res:${correlationId}`;
|
|
42
|
+
const envelope = {
|
|
43
|
+
commandData: redis_codec_1.RedisCodec.encode(data),
|
|
44
|
+
rpc: { correlationId, responseKey },
|
|
45
|
+
};
|
|
46
|
+
const envelopeBuffer = this.serializer.serialize(envelope);
|
|
47
|
+
// Wyślij do strumienia z markerem 'rpc' do identyfikacji
|
|
48
|
+
const poolConn = this.pool.next();
|
|
49
|
+
yield poolConn.xadd(`cmd:${commandName}`, '*', 'data', redis_codec_1.RedisCodec.encode(envelopeBuffer), 'rpc', '1');
|
|
50
|
+
// Pobierz dedykowane połączenie dla BRPOP (blocking)
|
|
51
|
+
const rpcConn = yield this.rpcPool.acquire();
|
|
52
|
+
try {
|
|
53
|
+
const timeoutSec = Math.max(1, Math.ceil(timeout / 1000));
|
|
54
|
+
const result = yield rpcConn.brpop(responseKey, timeoutSec);
|
|
55
|
+
if (!result) {
|
|
56
|
+
// C2: await DEL z fallback EXPIRE — zapobiega orphaned Redis keys
|
|
57
|
+
yield poolConn.del(responseKey).catch(() => {
|
|
58
|
+
// Fallback: EXPIRE zapewnia automatyczne usunięcie klucza po 120s
|
|
59
|
+
void poolConn.expire(responseKey, 120).catch(() => undefined);
|
|
60
|
+
});
|
|
61
|
+
// M2: bez correlationId w error message — zapobiega information leakage
|
|
62
|
+
throw new Error(`RPC timeout dla komendy ${commandName} (${timeout}ms)`);
|
|
63
|
+
}
|
|
64
|
+
const value = result[1];
|
|
65
|
+
return redis_codec_1.RedisCodec.decode(value);
|
|
66
|
+
}
|
|
67
|
+
finally {
|
|
68
|
+
this.rpcPool.release(rpcConn);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* RPC: odpowiada na request przez LPUSH + EXPIRE w jednym pipeline
|
|
74
|
+
* Wywoływane przez worker po przetworzeniu komendy RPC
|
|
75
|
+
*/
|
|
76
|
+
rpcRespond(responseKey, data, ttl) {
|
|
77
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
78
|
+
const conn = this.pool.next();
|
|
79
|
+
const pipeline = conn.pipeline();
|
|
80
|
+
pipeline.lpush(responseKey, redis_codec_1.RedisCodec.encode(data));
|
|
81
|
+
pipeline.expire(responseKey, ttl);
|
|
82
|
+
yield pipeline.exec();
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
exports.RpcHandler = RpcHandler;
|
|
87
|
+
//# sourceMappingURL=rpc-handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rpc-handler.js","sourceRoot":"","sources":["../../../src/command-bus/transport/rpc-handler.ts"],"names":[],"mappings":"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"}
|