pp-command-bus 1.1.0 → 1.2.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/dist/command-bus/command-bus.spec.js +17 -3
- package/dist/command-bus/command-bus.spec.js.map +1 -1
- package/dist/command-bus/config/command-bus-config.d.ts +6 -0
- package/dist/command-bus/config/command-bus-config.js +4 -0
- package/dist/command-bus/config/command-bus-config.js.map +1 -1
- package/dist/command-bus/index.d.ts +1 -2
- package/dist/command-bus/index.js +15 -12
- package/dist/command-bus/index.js.map +1 -1
- package/dist/command-bus/job/job-processor.d.ts +10 -10
- package/dist/command-bus/job/job-processor.js +28 -113
- package/dist/command-bus/job/job-processor.js.map +1 -1
- package/dist/command-bus/job/job-processor.spec.js +28 -107
- package/dist/command-bus/job/job-processor.spec.js.map +1 -1
- package/dist/command-bus/rpc/index.d.ts +3 -0
- package/dist/command-bus/rpc/index.js +4 -1
- package/dist/command-bus/rpc/index.js.map +1 -1
- package/dist/command-bus/rpc/payload-compression.service.d.ts +32 -0
- package/dist/command-bus/rpc/payload-compression.service.js +109 -0
- package/dist/command-bus/rpc/payload-compression.service.js.map +1 -0
- package/dist/command-bus/rpc/payload-compression.service.spec.js +233 -0
- package/dist/command-bus/rpc/payload-compression.service.spec.js.map +1 -0
- package/dist/command-bus/rpc/rpc-coordinator.d.ts +35 -16
- package/dist/command-bus/rpc/rpc-coordinator.js +138 -89
- package/dist/command-bus/rpc/rpc-coordinator.js.map +1 -1
- package/dist/command-bus/rpc/rpc-coordinator.spec.js +195 -431
- package/dist/command-bus/rpc/rpc-coordinator.spec.js.map +1 -1
- package/dist/command-bus/types/index.d.ts +6 -4
- package/dist/command-bus/worker/worker-benchmark.d.ts +2 -1
- package/dist/command-bus/worker/worker-benchmark.js +34 -41
- package/dist/command-bus/worker/worker-benchmark.js.map +1 -1
- package/dist/command-bus/worker/worker-benchmark.spec.js +25 -15
- package/dist/command-bus/worker/worker-benchmark.spec.js.map +1 -1
- package/dist/command-bus/worker/worker-metrics-collector.d.ts +60 -49
- package/dist/command-bus/worker/worker-metrics-collector.js +176 -193
- package/dist/command-bus/worker/worker-metrics-collector.js.map +1 -1
- package/dist/command-bus/worker/worker-orchestrator.d.ts +1 -1
- package/dist/command-bus/worker/worker-orchestrator.js +20 -25
- package/dist/command-bus/worker/worker-orchestrator.js.map +1 -1
- package/dist/command-bus/worker/worker-orchestrator.spec.js +32 -28
- package/dist/command-bus/worker/worker-orchestrator.spec.js.map +1 -1
- package/dist/pp-command-bus-1.2.1.tgz +0 -0
- package/package.json +1 -1
- package/dist/command-bus/worker/worker-metrics-collector.spec.js +0 -315
- package/dist/command-bus/worker/worker-metrics-collector.spec.js.map +0 -1
- package/dist/pp-command-bus-1.1.0.tgz +0 -0
- /package/dist/command-bus/{worker/worker-metrics-collector.spec.d.ts → rpc/payload-compression.service.spec.d.ts} +0 -0
|
@@ -0,0 +1,233 @@
|
|
|
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 payload_compression_service_1 = __importDefault(require("./payload-compression.service"));
|
|
16
|
+
/**
|
|
17
|
+
* Testy dla PayloadCompressionService
|
|
18
|
+
* Testuje kompresję gzip dla payloadów RPC >threshold
|
|
19
|
+
*/
|
|
20
|
+
describe('PayloadCompressionService', () => {
|
|
21
|
+
let service;
|
|
22
|
+
let mockLogger;
|
|
23
|
+
beforeEach(() => {
|
|
24
|
+
mockLogger = {
|
|
25
|
+
debug: jest.fn(),
|
|
26
|
+
log: jest.fn(),
|
|
27
|
+
warn: jest.fn(),
|
|
28
|
+
error: jest.fn(),
|
|
29
|
+
};
|
|
30
|
+
});
|
|
31
|
+
describe('compress()', () => {
|
|
32
|
+
it('nie kompresuje payloadu mniejszego niż threshold (1KB)', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
33
|
+
// Arrange
|
|
34
|
+
service = new payload_compression_service_1.default(1024, mockLogger);
|
|
35
|
+
const smallPayload = { correlationId: 'test-123', result: 'ok', error: null };
|
|
36
|
+
// Act
|
|
37
|
+
const { data, compressed } = yield service.compress(smallPayload);
|
|
38
|
+
// Assert
|
|
39
|
+
expect(compressed).toBe(false);
|
|
40
|
+
expect(data).toBe(JSON.stringify(smallPayload));
|
|
41
|
+
expect(mockLogger.debug).toHaveBeenCalledWith(expect.stringContaining('Payload NIE skompresowany'), expect.objectContaining({
|
|
42
|
+
originalSize: expect.any(Number),
|
|
43
|
+
threshold: 1024,
|
|
44
|
+
}));
|
|
45
|
+
}));
|
|
46
|
+
it('kompresuje payload większy niż threshold (1KB)', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
47
|
+
// Arrange
|
|
48
|
+
service = new payload_compression_service_1.default(1024, mockLogger);
|
|
49
|
+
// Utwórz duży payload >1KB
|
|
50
|
+
const largePayload = {
|
|
51
|
+
correlationId: 'test-456',
|
|
52
|
+
result: Array.from({ length: 100 }, (_, i) => ({
|
|
53
|
+
id: i,
|
|
54
|
+
name: `Item ${i}`,
|
|
55
|
+
description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit',
|
|
56
|
+
})),
|
|
57
|
+
error: null,
|
|
58
|
+
};
|
|
59
|
+
// Act
|
|
60
|
+
const { data, compressed } = yield service.compress(largePayload);
|
|
61
|
+
// Assert
|
|
62
|
+
expect(compressed).toBe(true);
|
|
63
|
+
expect(data).not.toBe(JSON.stringify(largePayload)); // Skompresowane
|
|
64
|
+
expect(typeof data).toBe('string'); // Base64 string
|
|
65
|
+
expect(mockLogger.debug).toHaveBeenCalledWith(expect.stringContaining('Payload skompresowany'), expect.objectContaining({
|
|
66
|
+
originalSize: expect.any(Number),
|
|
67
|
+
compressedSize: expect.any(Number),
|
|
68
|
+
compressionRatio: expect.any(String),
|
|
69
|
+
}));
|
|
70
|
+
}));
|
|
71
|
+
it('używa niestandardowego threshold (512 bytes)', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
72
|
+
// Arrange
|
|
73
|
+
service = new payload_compression_service_1.default(512, mockLogger);
|
|
74
|
+
const mediumPayload = {
|
|
75
|
+
correlationId: 'test-789',
|
|
76
|
+
result: Array.from({ length: 30 }, (_, i) => ({
|
|
77
|
+
id: i,
|
|
78
|
+
data: 'test data item with more content to exceed 512 bytes threshold',
|
|
79
|
+
})),
|
|
80
|
+
error: null,
|
|
81
|
+
};
|
|
82
|
+
// Act
|
|
83
|
+
const { compressed } = yield service.compress(mediumPayload);
|
|
84
|
+
// Assert - ten payload jest >512B więc powinien być skompresowany
|
|
85
|
+
expect(compressed).toBe(true);
|
|
86
|
+
}));
|
|
87
|
+
it('zawsze kompresuje gdy threshold=0', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
88
|
+
// Arrange
|
|
89
|
+
service = new payload_compression_service_1.default(0, mockLogger);
|
|
90
|
+
const tinyPayload = { result: 'ok' };
|
|
91
|
+
// Act
|
|
92
|
+
const { compressed } = yield service.compress(tinyPayload);
|
|
93
|
+
// Assert
|
|
94
|
+
expect(compressed).toBe(true);
|
|
95
|
+
}));
|
|
96
|
+
it('oblicza compression ratio poprawnie', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
97
|
+
// Arrange
|
|
98
|
+
service = new payload_compression_service_1.default(100, mockLogger);
|
|
99
|
+
const payload = {
|
|
100
|
+
data: Array.from({ length: 50 }, () => 'repeated text for compression test '),
|
|
101
|
+
};
|
|
102
|
+
// Act
|
|
103
|
+
yield service.compress(payload);
|
|
104
|
+
// Assert
|
|
105
|
+
expect(mockLogger.debug).toHaveBeenCalledWith(expect.stringContaining('Payload skompresowany'), expect.objectContaining({
|
|
106
|
+
compressionRatio: expect.stringMatching(/\d+%/), // Format: "XX%"
|
|
107
|
+
}));
|
|
108
|
+
}));
|
|
109
|
+
});
|
|
110
|
+
describe('decompress()', () => {
|
|
111
|
+
it('dekompresuje payload gdy compressed=true', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
112
|
+
// Arrange
|
|
113
|
+
service = new payload_compression_service_1.default(100, mockLogger);
|
|
114
|
+
const originalPayload = {
|
|
115
|
+
correlationId: 'test-999',
|
|
116
|
+
result: {
|
|
117
|
+
status: 'success',
|
|
118
|
+
data: Array.from({ length: 20 }, (_, i) => ({
|
|
119
|
+
id: i,
|
|
120
|
+
value: `item-${i}`,
|
|
121
|
+
description: 'some longer text to make payload bigger',
|
|
122
|
+
})),
|
|
123
|
+
},
|
|
124
|
+
error: null,
|
|
125
|
+
};
|
|
126
|
+
// Najpierw skompresuj
|
|
127
|
+
const { data: compressedData, compressed } = yield service.compress(originalPayload);
|
|
128
|
+
expect(compressed).toBe(true);
|
|
129
|
+
// Act - dekompresuj
|
|
130
|
+
const decompressed = yield service.decompress(compressedData, true);
|
|
131
|
+
// Assert
|
|
132
|
+
expect(decompressed).toEqual(originalPayload);
|
|
133
|
+
}));
|
|
134
|
+
it('parsuje JSON gdy compressed=false', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
135
|
+
// Arrange
|
|
136
|
+
service = new payload_compression_service_1.default(10000, mockLogger); // Bardzo wysoki threshold
|
|
137
|
+
const originalPayload = {
|
|
138
|
+
correlationId: 'test-111',
|
|
139
|
+
result: 'simple result',
|
|
140
|
+
error: null,
|
|
141
|
+
};
|
|
142
|
+
// Kompresuj (ale nie zostanie skompresowany bo <10KB)
|
|
143
|
+
const { data, compressed } = yield service.compress(originalPayload);
|
|
144
|
+
expect(compressed).toBe(false);
|
|
145
|
+
// Act - dekompresuj
|
|
146
|
+
const decompressed = yield service.decompress(data, false);
|
|
147
|
+
// Assert
|
|
148
|
+
expect(decompressed).toEqual(originalPayload);
|
|
149
|
+
}));
|
|
150
|
+
it('rzuca błąd gdy corrupted gzip data', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
151
|
+
// Arrange
|
|
152
|
+
service = new payload_compression_service_1.default(1024, mockLogger);
|
|
153
|
+
const corruptedData = 'invalid-base64-gzip-data!!!';
|
|
154
|
+
// Act & Assert
|
|
155
|
+
yield expect(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
156
|
+
yield service.decompress(corruptedData, true);
|
|
157
|
+
})).rejects.toThrow('Failed to decompress payload');
|
|
158
|
+
expect(mockLogger.error).toHaveBeenCalledWith(expect.stringContaining('Błąd dekompresji'), expect.any(Object));
|
|
159
|
+
}));
|
|
160
|
+
it('rzuca błąd gdy invalid JSON (compressed=false)', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
161
|
+
// Arrange
|
|
162
|
+
service = new payload_compression_service_1.default(1024, mockLogger);
|
|
163
|
+
const invalidJson = '{invalid json}';
|
|
164
|
+
// Act & Assert
|
|
165
|
+
yield expect(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
166
|
+
yield service.decompress(invalidJson, false);
|
|
167
|
+
})).rejects.toThrow();
|
|
168
|
+
}));
|
|
169
|
+
});
|
|
170
|
+
describe('edge cases', () => {
|
|
171
|
+
it('obsługuje pusty payload', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
172
|
+
// Arrange
|
|
173
|
+
service = new payload_compression_service_1.default(1024, mockLogger);
|
|
174
|
+
const emptyPayload = {};
|
|
175
|
+
// Act
|
|
176
|
+
const { data, compressed } = yield service.compress(emptyPayload);
|
|
177
|
+
const decompressed = yield service.decompress(data, compressed);
|
|
178
|
+
// Assert
|
|
179
|
+
expect(decompressed).toEqual(emptyPayload);
|
|
180
|
+
}));
|
|
181
|
+
it('obsługuje payload z null i undefined', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
182
|
+
// Arrange
|
|
183
|
+
service = new payload_compression_service_1.default(1024, mockLogger);
|
|
184
|
+
const payload = { result: null, error: undefined };
|
|
185
|
+
// Act
|
|
186
|
+
const { data, compressed } = yield service.compress(payload);
|
|
187
|
+
const decompressed = yield service.decompress(data, compressed);
|
|
188
|
+
// Assert
|
|
189
|
+
expect(decompressed).toEqual({ result: null, error: undefined });
|
|
190
|
+
}));
|
|
191
|
+
it('obsługuje payload z obiektami zagnieżdżonymi', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
192
|
+
// Arrange
|
|
193
|
+
service = new payload_compression_service_1.default(100, mockLogger);
|
|
194
|
+
const nestedPayload = {
|
|
195
|
+
level1: {
|
|
196
|
+
level2: {
|
|
197
|
+
level3: {
|
|
198
|
+
data: [1, 2, 3],
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
};
|
|
203
|
+
// Act
|
|
204
|
+
const { data, compressed } = yield service.compress(nestedPayload);
|
|
205
|
+
const decompressed = yield service.decompress(data, compressed);
|
|
206
|
+
// Assert
|
|
207
|
+
expect(decompressed).toEqual(nestedPayload);
|
|
208
|
+
}));
|
|
209
|
+
it('osiąga compression ratio >50% dla typowego JSON', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
210
|
+
// Arrange
|
|
211
|
+
service = new payload_compression_service_1.default(100, mockLogger);
|
|
212
|
+
const typicalPayload = {
|
|
213
|
+
correlationId: 'abc-123',
|
|
214
|
+
result: Array.from({ length: 50 }, (_, i) => ({
|
|
215
|
+
id: i,
|
|
216
|
+
name: `User ${i}`,
|
|
217
|
+
email: `user${i}@example.com`,
|
|
218
|
+
active: true,
|
|
219
|
+
})),
|
|
220
|
+
error: null,
|
|
221
|
+
};
|
|
222
|
+
// Act
|
|
223
|
+
const { data, compressed } = yield service.compress(typicalPayload);
|
|
224
|
+
// Assert
|
|
225
|
+
expect(compressed).toBe(true);
|
|
226
|
+
const originalSize = Buffer.byteLength(JSON.stringify(typicalPayload), 'utf8');
|
|
227
|
+
const compressedSize = Buffer.byteLength(data, 'utf8');
|
|
228
|
+
const ratio = ((originalSize - compressedSize) / originalSize) * 100;
|
|
229
|
+
expect(ratio).toBeGreaterThan(50); // >50% redukcja
|
|
230
|
+
}));
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
//# sourceMappingURL=payload-compression.service.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"payload-compression.service.spec.js","sourceRoot":"","sources":["../../../src/command-bus/rpc/payload-compression.service.spec.ts"],"names":[],"mappings":";;;;;;;;;;;;;;AAAA,gGAAsE;AAGtE;;;GAGG;AACH,QAAQ,CAAC,2BAA2B,EAAE,GAAG,EAAE;IACzC,IAAI,OAAkC,CAAC;IACvC,IAAI,UAAmB,CAAC;IAExB,UAAU,CAAC,GAAG,EAAE;QACd,UAAU,GAAG;YACX,KAAK,EAAE,IAAI,CAAC,EAAE,EAAE;YAChB,GAAG,EAAE,IAAI,CAAC,EAAE,EAAE;YACd,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE;YACf,KAAK,EAAE,IAAI,CAAC,EAAE,EAAE;SACjB,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,YAAY,EAAE,GAAG,EAAE;QAC1B,EAAE,CAAC,wDAAwD,EAAE,GAAS,EAAE;YACtE,UAAU;YACV,OAAO,GAAG,IAAI,qCAAyB,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;YAC1D,MAAM,YAAY,GAAG,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;YAE9E,MAAM;YACN,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,MAAM,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;YAElE,SAAS;YACT,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC/B,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC,CAAC;YAChD,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,oBAAoB,CAC3C,MAAM,CAAC,gBAAgB,CAAC,2BAA2B,CAAC,EACpD,MAAM,CAAC,gBAAgB,CAAC;gBACtB,YAAY,EAAE,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC;gBAChC,SAAS,EAAE,IAAI;aAChB,CAAC,CACH,CAAC;QACJ,CAAC,CAAA,CAAC,CAAC;QAEH,EAAE,CAAC,gDAAgD,EAAE,GAAS,EAAE;YAC9D,UAAU;YACV,OAAO,GAAG,IAAI,qCAAyB,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;YAC1D,2BAA2B;YAC3B,MAAM,YAAY,GAAG;gBACnB,aAAa,EAAE,UAAU;gBACzB,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;oBAC7C,EAAE,EAAE,CAAC;oBACL,IAAI,EAAE,QAAQ,CAAC,EAAE;oBACjB,WAAW,EAAE,yDAAyD;iBACvE,CAAC,CAAC;gBACH,KAAK,EAAE,IAAI;aACZ,CAAC;YAEF,MAAM;YACN,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,MAAM,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;YAElE,SAAS;YACT,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC9B,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,gBAAgB;YACrE,MAAM,CAAC,OAAO,IAAI,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,gBAAgB;YACpD,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,oBAAoB,CAC3C,MAAM,CAAC,gBAAgB,CAAC,uBAAuB,CAAC,EAChD,MAAM,CAAC,gBAAgB,CAAC;gBACtB,YAAY,EAAE,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC;gBAChC,cAAc,EAAE,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC;gBAClC,gBAAgB,EAAE,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC;aACrC,CAAC,CACH,CAAC;QACJ,CAAC,CAAA,CAAC,CAAC;QAEH,EAAE,CAAC,8CAA8C,EAAE,GAAS,EAAE;YAC5D,UAAU;YACV,OAAO,GAAG,IAAI,qCAAyB,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;YACzD,MAAM,aAAa,GAAG;gBACpB,aAAa,EAAE,UAAU;gBACzB,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;oBAC5C,EAAE,EAAE,CAAC;oBACL,IAAI,EAAE,gEAAgE;iBACvE,CAAC,CAAC;gBACH,KAAK,EAAE,IAAI;aACZ,CAAC;YAEF,MAAM;YACN,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;YAE7D,kEAAkE;YAClE,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAChC,CAAC,CAAA,CAAC,CAAC;QAEH,EAAE,CAAC,mCAAmC,EAAE,GAAS,EAAE;YACjD,UAAU;YACV,OAAO,GAAG,IAAI,qCAAyB,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;YACvD,MAAM,WAAW,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;YAErC,MAAM;YACN,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;YAE3D,SAAS;YACT,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAChC,CAAC,CAAA,CAAC,CAAC;QAEH,EAAE,CAAC,qCAAqC,EAAE,GAAS,EAAE;YACnD,UAAU;YACV,OAAO,GAAG,IAAI,qCAAyB,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;YACzD,MAAM,OAAO,GAAG;gBACd,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,qCAAqC,CAAC;aAC9E,CAAC;YAEF,MAAM;YACN,MAAM,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;YAEhC,SAAS;YACT,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,oBAAoB,CAC3C,MAAM,CAAC,gBAAgB,CAAC,uBAAuB,CAAC,EAChD,MAAM,CAAC,gBAAgB,CAAC;gBACtB,gBAAgB,EAAE,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE,gBAAgB;aAClE,CAAC,CACH,CAAC;QACJ,CAAC,CAAA,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;QAC5B,EAAE,CAAC,0CAA0C,EAAE,GAAS,EAAE;YACxD,UAAU;YACV,OAAO,GAAG,IAAI,qCAAyB,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;YACzD,MAAM,eAAe,GAAG;gBACtB,aAAa,EAAE,UAAU;gBACzB,MAAM,EAAE;oBACN,MAAM,EAAE,SAAS;oBACjB,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;wBAC1C,EAAE,EAAE,CAAC;wBACL,KAAK,EAAE,QAAQ,CAAC,EAAE;wBAClB,WAAW,EAAE,yCAAyC;qBACvD,CAAC,CAAC;iBACJ;gBACD,KAAK,EAAE,IAAI;aACZ,CAAC;YAEF,sBAAsB;YACtB,MAAM,EAAE,IAAI,EAAE,cAAc,EAAE,UAAU,EAAE,GAAG,MAAM,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC;YACrF,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAE9B,oBAAoB;YACpB,MAAM,YAAY,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC,cAAc,EAAE,IAAI,CAAC,CAAC;YAEpE,SAAS;YACT,MAAM,CAAC,YAAY,CAAC,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;QAChD,CAAC,CAAA,CAAC,CAAC;QAEH,EAAE,CAAC,mCAAmC,EAAE,GAAS,EAAE;YACjD,UAAU;YACV,OAAO,GAAG,IAAI,qCAAyB,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,0BAA0B;YACtF,MAAM,eAAe,GAAG;gBACtB,aAAa,EAAE,UAAU;gBACzB,MAAM,EAAE,eAAe;gBACvB,KAAK,EAAE,IAAI;aACZ,CAAC;YAEF,sDAAsD;YACtD,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,MAAM,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC;YACrE,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAE/B,oBAAoB;YACpB,MAAM,YAAY,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;YAE3D,SAAS;YACT,MAAM,CAAC,YAAY,CAAC,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;QAChD,CAAC,CAAA,CAAC,CAAC;QAEH,EAAE,CAAC,oCAAoC,EAAE,GAAS,EAAE;YAClD,UAAU;YACV,OAAO,GAAG,IAAI,qCAAyB,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;YAC1D,MAAM,aAAa,GAAG,6BAA6B,CAAC;YAEpD,eAAe;YACf,MAAM,MAAM,CAAC,GAAS,EAAE;gBACtB,MAAM,OAAO,CAAC,UAAU,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC;YAChD,CAAC,CAAA,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,8BAA8B,CAAC,CAAC;YAEnD,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,oBAAoB,CAC3C,MAAM,CAAC,gBAAgB,CAAC,kBAAkB,CAAC,EAC3C,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CACnB,CAAC;QACJ,CAAC,CAAA,CAAC,CAAC;QAEH,EAAE,CAAC,gDAAgD,EAAE,GAAS,EAAE;YAC9D,UAAU;YACV,OAAO,GAAG,IAAI,qCAAyB,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;YAC1D,MAAM,WAAW,GAAG,gBAAgB,CAAC;YAErC,eAAe;YACf,MAAM,MAAM,CAAC,GAAS,EAAE;gBACtB,MAAM,OAAO,CAAC,UAAU,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;YAC/C,CAAC,CAAA,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;QACvB,CAAC,CAAA,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,YAAY,EAAE,GAAG,EAAE;QAC1B,EAAE,CAAC,yBAAyB,EAAE,GAAS,EAAE;YACvC,UAAU;YACV,OAAO,GAAG,IAAI,qCAAyB,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;YAC1D,MAAM,YAAY,GAAG,EAAE,CAAC;YAExB,MAAM;YACN,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,MAAM,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;YAClE,MAAM,YAAY,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;YAEhE,SAAS;YACT,MAAM,CAAC,YAAY,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;QAC7C,CAAC,CAAA,CAAC,CAAC;QAEH,EAAE,CAAC,sCAAsC,EAAE,GAAS,EAAE;YACpD,UAAU;YACV,OAAO,GAAG,IAAI,qCAAyB,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;YAC1D,MAAM,OAAO,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;YAEnD,MAAM;YACN,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,MAAM,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;YAC7D,MAAM,YAAY,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;YAEhE,SAAS;YACT,MAAM,CAAC,YAAY,CAAC,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;QACnE,CAAC,CAAA,CAAC,CAAC;QAEH,EAAE,CAAC,8CAA8C,EAAE,GAAS,EAAE;YAC5D,UAAU;YACV,OAAO,GAAG,IAAI,qCAAyB,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;YACzD,MAAM,aAAa,GAAG;gBACpB,MAAM,EAAE;oBACN,MAAM,EAAE;wBACN,MAAM,EAAE;4BACN,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;yBAChB;qBACF;iBACF;aACF,CAAC;YAEF,MAAM;YACN,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,MAAM,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;YACnE,MAAM,YAAY,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;YAEhE,SAAS;YACT,MAAM,CAAC,YAAY,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;QAC9C,CAAC,CAAA,CAAC,CAAC;QAEH,EAAE,CAAC,iDAAiD,EAAE,GAAS,EAAE;YAC/D,UAAU;YACV,OAAO,GAAG,IAAI,qCAAyB,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;YACzD,MAAM,cAAc,GAAG;gBACrB,aAAa,EAAE,SAAS;gBACxB,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;oBAC5C,EAAE,EAAE,CAAC;oBACL,IAAI,EAAE,QAAQ,CAAC,EAAE;oBACjB,KAAK,EAAE,OAAO,CAAC,cAAc;oBAC7B,MAAM,EAAE,IAAI;iBACb,CAAC,CAAC;gBACH,KAAK,EAAE,IAAI;aACZ,CAAC;YAEF,MAAM;YACN,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,MAAM,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC;YAEpE,SAAS;YACT,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC9B,MAAM,YAAY,GAAG,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC,CAAC;YAC/E,MAAM,cAAc,GAAG,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;YACvD,MAAM,KAAK,GAAG,CAAC,CAAC,YAAY,GAAG,cAAc,CAAC,GAAG,YAAY,CAAC,GAAG,GAAG,CAAC;YAErE,MAAM,CAAC,KAAK,CAAC,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC,CAAC,gBAAgB;QACrD,CAAC,CAAA,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -2,30 +2,40 @@ import type { Redis } from 'ioredis';
|
|
|
2
2
|
import type { ILogger } from '../../shared/types';
|
|
3
3
|
import type { RpcMetadata } from '../types';
|
|
4
4
|
import type Command from '../command';
|
|
5
|
-
import type
|
|
5
|
+
import type PayloadCompressionService from './payload-compression.service';
|
|
6
6
|
/**
|
|
7
|
-
* Zarządza lifecycle wywołań RPC
|
|
8
|
-
* Każde wywołanie call() tworzy
|
|
9
|
-
*
|
|
7
|
+
* Zarządza lifecycle wywołań RPC przez Redis Pub/Sub
|
|
8
|
+
* Każde wywołanie call() tworzy dedykowany subscriber dla odpowiedzi
|
|
9
|
+
* Odpowiedzi są wysyłane przez redis.publish() zamiast kolejek BullMQ
|
|
10
|
+
*
|
|
11
|
+
* Architektura: Każdy RPC call ma własny subscriber (bez poolingu)
|
|
12
|
+
* - Prostsza implementacja bez race conditions
|
|
13
|
+
* - Lepsza skalowalność przy wysokim obciążeniu (brak pool exhaustion)
|
|
14
|
+
* - Redis duplicate() jest lekkie (reużywa connection pool ioredis)
|
|
10
15
|
*/
|
|
11
16
|
export default class RpcCoordinator {
|
|
12
17
|
private readonly logger;
|
|
13
18
|
private readonly redisConnection;
|
|
14
|
-
private readonly
|
|
15
|
-
constructor(logger: ILogger, redisConnection: Redis,
|
|
19
|
+
private readonly compressionService;
|
|
20
|
+
constructor(logger: ILogger, redisConnection: Redis, compressionService: PayloadCompressionService);
|
|
16
21
|
/**
|
|
17
22
|
* Przygotowuje komendę RPC z odpowiednimi metadanymi
|
|
18
23
|
* @param command - Komenda do wysłania
|
|
19
|
-
* @param
|
|
24
|
+
* @param responseChannel - Nazwa kanału Redis Pub/Sub dla odpowiedzi
|
|
20
25
|
* @returns Komenda z metadanymi RPC
|
|
21
26
|
*/
|
|
22
|
-
prepareRpcCommand<T extends Command>(command: T,
|
|
27
|
+
prepareRpcCommand<T extends Command>(command: T, responseChannel: string): T & {
|
|
23
28
|
__rpcMetadata: RpcMetadata;
|
|
24
29
|
};
|
|
25
30
|
/**
|
|
26
|
-
* Rejestruje nowe wywołanie RPC z timeout
|
|
27
|
-
* Tworzy
|
|
28
|
-
* Automatycznie
|
|
31
|
+
* Rejestruje nowe wywołanie RPC z timeout przez Redis Pub/Sub
|
|
32
|
+
* Tworzy dedykowany subscriber dla każdego wywołania
|
|
33
|
+
* Automatycznie zamyka subscriber po zakończeniu (sukces/błąd/timeout)
|
|
34
|
+
*
|
|
35
|
+
* Architektura: Każdy call ma własny subscriber
|
|
36
|
+
* - Brak race conditions przy wysokim obciążeniu
|
|
37
|
+
* - Prostsza implementacja bez poolingu
|
|
38
|
+
* - Automatyczne cleanup przez finally block
|
|
29
39
|
*
|
|
30
40
|
* @param correlationId - Unikalny ID wywołania
|
|
31
41
|
* @param commandName - Nazwa komendy
|
|
@@ -34,12 +44,21 @@ export default class RpcCoordinator {
|
|
|
34
44
|
*/
|
|
35
45
|
registerCall<T>(correlationId: string, commandName: string, timeout: number): Promise<T>;
|
|
36
46
|
/**
|
|
37
|
-
* Czyści
|
|
47
|
+
* Czyści subscriber po zakończeniu RPC call
|
|
48
|
+
* Unsubscribe i zamknięcie dedykowanego subscriber
|
|
38
49
|
* Fail-safe - nie rzuca błędów, tylko loguje
|
|
39
50
|
*
|
|
40
|
-
* @param
|
|
41
|
-
* @param
|
|
42
|
-
* @param
|
|
51
|
+
* @param subscriber - Subscriber do wyczyszczenia
|
|
52
|
+
* @param responseChannel - Kanał do unsubscribe
|
|
53
|
+
* @param correlationId - ID wywołania (dla logowania)
|
|
54
|
+
*/
|
|
55
|
+
private cleanupSubscriber;
|
|
56
|
+
/**
|
|
57
|
+
* Zamyka RpcCoordinator i czyści zasoby
|
|
58
|
+
* Wywołane przy zamykaniu CommandBus
|
|
59
|
+
*
|
|
60
|
+
* Uwaga: Dedykowane subscribers są automatycznie zamykane w finally block registerCall()
|
|
61
|
+
* Ta metoda nie musi już zamykać puli subscribers (pool został usunięty)
|
|
43
62
|
*/
|
|
44
|
-
|
|
63
|
+
close(): void;
|
|
45
64
|
}
|
|
@@ -9,35 +9,45 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
const bullmq_1 = require("bullmq");
|
|
13
12
|
/**
|
|
14
|
-
* Zarządza lifecycle wywołań RPC
|
|
15
|
-
* Każde wywołanie call() tworzy
|
|
16
|
-
*
|
|
13
|
+
* Zarządza lifecycle wywołań RPC przez Redis Pub/Sub
|
|
14
|
+
* Każde wywołanie call() tworzy dedykowany subscriber dla odpowiedzi
|
|
15
|
+
* Odpowiedzi są wysyłane przez redis.publish() zamiast kolejek BullMQ
|
|
16
|
+
*
|
|
17
|
+
* Architektura: Każdy RPC call ma własny subscriber (bez poolingu)
|
|
18
|
+
* - Prostsza implementacja bez race conditions
|
|
19
|
+
* - Lepsza skalowalność przy wysokim obciążeniu (brak pool exhaustion)
|
|
20
|
+
* - Redis duplicate() jest lekkie (reużywa connection pool ioredis)
|
|
17
21
|
*/
|
|
18
22
|
class RpcCoordinator {
|
|
19
|
-
constructor(logger, redisConnection,
|
|
23
|
+
constructor(logger, redisConnection, compressionService) {
|
|
20
24
|
this.logger = logger;
|
|
21
25
|
this.redisConnection = redisConnection;
|
|
22
|
-
this.
|
|
26
|
+
this.compressionService = compressionService;
|
|
23
27
|
}
|
|
24
28
|
/**
|
|
25
29
|
* Przygotowuje komendę RPC z odpowiednimi metadanymi
|
|
26
30
|
* @param command - Komenda do wysłania
|
|
27
|
-
* @param
|
|
31
|
+
* @param responseChannel - Nazwa kanału Redis Pub/Sub dla odpowiedzi
|
|
28
32
|
* @returns Komenda z metadanymi RPC
|
|
29
33
|
*/
|
|
30
|
-
prepareRpcCommand(command,
|
|
34
|
+
prepareRpcCommand(command, responseChannel) {
|
|
31
35
|
return Object.assign(Object.assign({}, command), { __rpcMetadata: {
|
|
32
36
|
correlationId: command.__id,
|
|
33
|
-
|
|
37
|
+
responseChannel,
|
|
34
38
|
timestamp: Date.now(),
|
|
39
|
+
compressed: true, // Zawsze próbujemy kompresji (zależy od threshold)
|
|
35
40
|
} });
|
|
36
41
|
}
|
|
37
42
|
/**
|
|
38
|
-
* Rejestruje nowe wywołanie RPC z timeout
|
|
39
|
-
* Tworzy
|
|
40
|
-
* Automatycznie
|
|
43
|
+
* Rejestruje nowe wywołanie RPC z timeout przez Redis Pub/Sub
|
|
44
|
+
* Tworzy dedykowany subscriber dla każdego wywołania
|
|
45
|
+
* Automatycznie zamyka subscriber po zakończeniu (sukces/błąd/timeout)
|
|
46
|
+
*
|
|
47
|
+
* Architektura: Każdy call ma własny subscriber
|
|
48
|
+
* - Brak race conditions przy wysokim obciążeniu
|
|
49
|
+
* - Prostsza implementacja bez poolingu
|
|
50
|
+
* - Automatyczne cleanup przez finally block
|
|
41
51
|
*
|
|
42
52
|
* @param correlationId - Unikalny ID wywołania
|
|
43
53
|
* @param commandName - Nazwa komendy
|
|
@@ -46,55 +56,83 @@ class RpcCoordinator {
|
|
|
46
56
|
*/
|
|
47
57
|
registerCall(correlationId, commandName, timeout) {
|
|
48
58
|
return __awaiter(this, void 0, void 0, function* () {
|
|
49
|
-
const
|
|
50
|
-
|
|
59
|
+
const responseChannel = `rpc:response:${correlationId}`;
|
|
60
|
+
// Każdy RPC call tworzy własny dedykowany subscriber
|
|
61
|
+
const subscriber = this.redisConnection.duplicate();
|
|
62
|
+
let timeoutId;
|
|
63
|
+
this.logger.debug('Rejestrowanie wywołania RPC przez Pub/Sub', {
|
|
51
64
|
correlationId,
|
|
52
|
-
|
|
65
|
+
responseChannel,
|
|
53
66
|
commandName,
|
|
54
67
|
timeout: `${timeout}ms`,
|
|
55
68
|
timestamp: new Date().toISOString(),
|
|
56
69
|
});
|
|
57
|
-
// Utwórz dedykowaną kolejkę dla tego wywołania
|
|
58
|
-
const replyQueue = this.queueManager.createQueue(replyQueueName, this.redisConnection);
|
|
59
|
-
// Utwórz dedykowany worker dla tej kolejki (concurrency=1, jedna odpowiedź)
|
|
60
|
-
let replyWorker;
|
|
61
|
-
let timeoutId;
|
|
62
70
|
try {
|
|
63
|
-
// Promise dla odpowiedzi z
|
|
71
|
+
// Promise dla odpowiedzi z Redis Pub/Sub
|
|
64
72
|
const responsePromise = new Promise((resolve, reject) => {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
error,
|
|
73
|
+
const messageHandler = (channel, message) => {
|
|
74
|
+
if (channel !== responseChannel)
|
|
75
|
+
return;
|
|
76
|
+
// Dekompresuj i parsuj odpowiedź (async)
|
|
77
|
+
this.compressionService
|
|
78
|
+
.decompress(message, true)
|
|
79
|
+
.then((decompressed) => {
|
|
80
|
+
const { correlationId: responseCorrelationId, result, error, } = decompressed;
|
|
81
|
+
this.logger.debug('Otrzymano odpowiedź RPC przez Pub/Sub', {
|
|
82
|
+
correlationId: responseCorrelationId,
|
|
83
|
+
hasResult: result !== undefined,
|
|
84
|
+
hasError: error !== undefined,
|
|
77
85
|
timestamp: new Date().toISOString(),
|
|
78
86
|
});
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
87
|
+
// Usunięcie handlera po otrzymaniu odpowiedzi
|
|
88
|
+
subscriber.off('message', messageHandler);
|
|
89
|
+
if (error) {
|
|
90
|
+
this.logger.error('Wywołanie RPC odrzucone', {
|
|
91
|
+
correlationId: responseCorrelationId,
|
|
92
|
+
error,
|
|
93
|
+
timestamp: new Date().toISOString(),
|
|
94
|
+
});
|
|
95
|
+
reject(new Error(error));
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
this.logger.debug('Wywołanie RPC rozwiązane', {
|
|
99
|
+
correlationId: responseCorrelationId,
|
|
100
|
+
timestamp: new Date().toISOString(),
|
|
101
|
+
});
|
|
102
|
+
resolve(result);
|
|
103
|
+
}
|
|
104
|
+
})
|
|
105
|
+
.catch((decompressError) => {
|
|
106
|
+
this.logger.error('Błąd dekompresji/parsowania odpowiedzi RPC', {
|
|
107
|
+
correlationId,
|
|
108
|
+
error: decompressError instanceof Error
|
|
109
|
+
? decompressError.message
|
|
110
|
+
: String(decompressError),
|
|
84
111
|
timestamp: new Date().toISOString(),
|
|
85
112
|
});
|
|
86
|
-
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
113
|
+
reject(new Error('Failed to decompress/parse RPC response'));
|
|
114
|
+
});
|
|
115
|
+
};
|
|
116
|
+
// Obsługa błędów Redis
|
|
117
|
+
const errorHandler = (error) => {
|
|
118
|
+
this.logger.error('Błąd Redis subscriber podczas RPC', {
|
|
119
|
+
correlationId,
|
|
120
|
+
error: error.message,
|
|
121
|
+
timestamp: new Date().toISOString(),
|
|
122
|
+
});
|
|
123
|
+
subscriber.off('message', messageHandler);
|
|
124
|
+
subscriber.off('error', errorHandler);
|
|
125
|
+
reject(new Error(`Redis subscriber error: ${error.message}`));
|
|
126
|
+
};
|
|
127
|
+
subscriber.on('message', messageHandler);
|
|
128
|
+
subscriber.on('error', errorHandler);
|
|
129
|
+
});
|
|
130
|
+
// Subskrybuj kanał PRZED wysłaniem komendy (zapobiega race condition)
|
|
131
|
+
yield subscriber.subscribe(responseChannel);
|
|
132
|
+
this.logger.debug('Subskrybowano kanał RPC', {
|
|
133
|
+
correlationId,
|
|
134
|
+
responseChannel,
|
|
135
|
+
timestamp: new Date().toISOString(),
|
|
98
136
|
});
|
|
99
137
|
// Promise dla timeout
|
|
100
138
|
const timeoutPromise = new Promise((_, reject) => {
|
|
@@ -116,9 +154,10 @@ class RpcCoordinator {
|
|
|
116
154
|
if (timeoutId) {
|
|
117
155
|
clearTimeout(timeoutId);
|
|
118
156
|
}
|
|
119
|
-
// Cleanup
|
|
120
|
-
this.
|
|
121
|
-
this.logger.error('Błąd podczas cleanup
|
|
157
|
+
// Cleanup: zamknij dedykowany subscriber
|
|
158
|
+
this.cleanupSubscriber(subscriber, responseChannel, correlationId).catch((error) => {
|
|
159
|
+
this.logger.error('Błąd podczas cleanup subscriber RPC', {
|
|
160
|
+
correlationId,
|
|
122
161
|
error: error instanceof Error ? error.message : String(error),
|
|
123
162
|
timestamp: new Date().toISOString(),
|
|
124
163
|
});
|
|
@@ -127,68 +166,78 @@ class RpcCoordinator {
|
|
|
127
166
|
});
|
|
128
167
|
}
|
|
129
168
|
/**
|
|
130
|
-
* Czyści
|
|
169
|
+
* Czyści subscriber po zakończeniu RPC call
|
|
170
|
+
* Unsubscribe i zamknięcie dedykowanego subscriber
|
|
131
171
|
* Fail-safe - nie rzuca błędów, tylko loguje
|
|
132
172
|
*
|
|
133
|
-
* @param
|
|
134
|
-
* @param
|
|
135
|
-
* @param
|
|
173
|
+
* @param subscriber - Subscriber do wyczyszczenia
|
|
174
|
+
* @param responseChannel - Kanał do unsubscribe
|
|
175
|
+
* @param correlationId - ID wywołania (dla logowania)
|
|
136
176
|
*/
|
|
137
|
-
|
|
177
|
+
cleanupSubscriber(subscriber, responseChannel, correlationId) {
|
|
138
178
|
return __awaiter(this, void 0, void 0, function* () {
|
|
139
|
-
this.logger.debug('Rozpoczynam cleanup
|
|
140
|
-
|
|
179
|
+
this.logger.debug('Rozpoczynam cleanup subscriber RPC', {
|
|
180
|
+
correlationId,
|
|
181
|
+
responseChannel,
|
|
141
182
|
timestamp: new Date().toISOString(),
|
|
142
183
|
});
|
|
143
|
-
//
|
|
144
|
-
if (worker) {
|
|
145
|
-
try {
|
|
146
|
-
yield worker.close();
|
|
147
|
-
this.logger.debug('Worker RPC zamknięty', {
|
|
148
|
-
queueName,
|
|
149
|
-
timestamp: new Date().toISOString(),
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
catch (error) {
|
|
153
|
-
this.logger.warn('Błąd podczas zamykania Worker RPC', {
|
|
154
|
-
queueName,
|
|
155
|
-
error: error instanceof Error ? error.message : String(error),
|
|
156
|
-
timestamp: new Date().toISOString(),
|
|
157
|
-
});
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
// Usuń kolejkę całkowicie (obliterate + close)
|
|
184
|
+
// Unsubscribe z timeoutem (200ms max)
|
|
161
185
|
try {
|
|
162
|
-
yield
|
|
163
|
-
|
|
164
|
-
|
|
186
|
+
yield Promise.race([
|
|
187
|
+
subscriber.unsubscribe(responseChannel),
|
|
188
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error('Unsubscribe timeout')), 200)),
|
|
189
|
+
]);
|
|
190
|
+
this.logger.debug('Unsubscribe z kanału RPC wykonany', {
|
|
191
|
+
correlationId,
|
|
192
|
+
responseChannel,
|
|
165
193
|
timestamp: new Date().toISOString(),
|
|
166
194
|
});
|
|
167
195
|
}
|
|
168
196
|
catch (error) {
|
|
169
|
-
this.logger.warn('Błąd podczas
|
|
170
|
-
|
|
197
|
+
this.logger.warn('Błąd podczas unsubscribe z kanału RPC', {
|
|
198
|
+
correlationId,
|
|
199
|
+
responseChannel,
|
|
171
200
|
error: error instanceof Error ? error.message : String(error),
|
|
172
201
|
timestamp: new Date().toISOString(),
|
|
173
202
|
});
|
|
174
203
|
}
|
|
175
|
-
//
|
|
204
|
+
// Usuń wszystkie event listenery
|
|
205
|
+
subscriber.removeAllListeners('message');
|
|
206
|
+
subscriber.removeAllListeners('error');
|
|
207
|
+
// Zamknij dedykowany subscriber
|
|
176
208
|
try {
|
|
177
|
-
yield
|
|
178
|
-
this.logger.debug('
|
|
179
|
-
|
|
209
|
+
yield subscriber.quit();
|
|
210
|
+
this.logger.debug('Subscriber zamknięty', {
|
|
211
|
+
correlationId,
|
|
180
212
|
timestamp: new Date().toISOString(),
|
|
181
213
|
});
|
|
182
214
|
}
|
|
183
215
|
catch (error) {
|
|
184
|
-
this.logger.warn('Błąd podczas zamykania
|
|
185
|
-
|
|
216
|
+
this.logger.warn('Błąd podczas zamykania subscriber', {
|
|
217
|
+
correlationId,
|
|
186
218
|
error: error instanceof Error ? error.message : String(error),
|
|
187
219
|
timestamp: new Date().toISOString(),
|
|
188
220
|
});
|
|
189
221
|
}
|
|
190
222
|
});
|
|
191
223
|
}
|
|
224
|
+
/**
|
|
225
|
+
* Zamyka RpcCoordinator i czyści zasoby
|
|
226
|
+
* Wywołane przy zamykaniu CommandBus
|
|
227
|
+
*
|
|
228
|
+
* Uwaga: Dedykowane subscribers są automatycznie zamykane w finally block registerCall()
|
|
229
|
+
* Ta metoda nie musi już zamykać puli subscribers (pool został usunięty)
|
|
230
|
+
*/
|
|
231
|
+
close() {
|
|
232
|
+
this.logger.debug('Zamykanie RpcCoordinator', {
|
|
233
|
+
timestamp: new Date().toISOString(),
|
|
234
|
+
});
|
|
235
|
+
// Brak zasobów do wyczyszczenia - subscribers są zamykane per-call
|
|
236
|
+
// Główne połączenie Redis (redisConnection) jest zarządzane przez CommandBus
|
|
237
|
+
this.logger.debug('RpcCoordinator zamknięty', {
|
|
238
|
+
timestamp: new Date().toISOString(),
|
|
239
|
+
});
|
|
240
|
+
}
|
|
192
241
|
}
|
|
193
242
|
exports.default = RpcCoordinator;
|
|
194
243
|
//# sourceMappingURL=rpc-coordinator.js.map
|