@volontariapp/post-processors 1.0.0-snap-856af13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/dist/core/base.post-processor.d.ts +27 -0
- package/dist/core/base.post-processor.d.ts.map +1 -0
- package/dist/core/base.post-processor.js +206 -0
- package/dist/core/base.post-processor.js.map +1 -0
- package/dist/core/batch.post-processor.d.ts +13 -0
- package/dist/core/batch.post-processor.d.ts.map +1 -0
- package/dist/core/batch.post-processor.js +92 -0
- package/dist/core/batch.post-processor.js.map +1 -0
- package/dist/core/index.d.ts +5 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +5 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/redis-stream.helper.d.ts +12 -0
- package/dist/core/redis-stream.helper.d.ts.map +1 -0
- package/dist/core/redis-stream.helper.js +44 -0
- package/dist/core/redis-stream.helper.js.map +1 -0
- package/dist/core/single.post-processor.d.ts +12 -0
- package/dist/core/single.post-processor.d.ts.map +1 -0
- package/dist/core/single.post-processor.js +58 -0
- package/dist/core/single.post-processor.js.map +1 -0
- package/dist/data-source.d.ts +5 -0
- package/dist/data-source.d.ts.map +1 -0
- package/dist/data-source.js +33 -0
- package/dist/data-source.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/interfaces/batch-event-item.interface.d.ts +7 -0
- package/dist/interfaces/batch-event-item.interface.d.ts.map +1 -0
- package/dist/interfaces/batch-event-item.interface.js +2 -0
- package/dist/interfaces/batch-event-item.interface.js.map +1 -0
- package/dist/interfaces/index.d.ts +4 -0
- package/dist/interfaces/index.d.ts.map +1 -0
- package/dist/interfaces/index.js +2 -0
- package/dist/interfaces/index.js.map +1 -0
- package/dist/interfaces/pending-message-info.interface.d.ts +7 -0
- package/dist/interfaces/pending-message-info.interface.d.ts.map +1 -0
- package/dist/interfaces/pending-message-info.interface.js +2 -0
- package/dist/interfaces/pending-message-info.interface.js.map +1 -0
- package/dist/interfaces/post-processor-options.interface.d.ts +11 -0
- package/dist/interfaces/post-processor-options.interface.d.ts.map +1 -0
- package/dist/interfaces/post-processor-options.interface.js +2 -0
- package/dist/interfaces/post-processor-options.interface.js.map +1 -0
- package/dist/migrations/1776783577425-CreateJobsOutbox.d.ts +7 -0
- package/dist/migrations/1776783577425-CreateJobsOutbox.d.ts.map +1 -0
- package/dist/migrations/1776783577425-CreateJobsOutbox.js +25 -0
- package/dist/migrations/1776783577425-CreateJobsOutbox.js.map +1 -0
- package/dist/migrations/1778328780881-InitialSchemaJobAudit.d.ts +6 -0
- package/dist/migrations/1778328780881-InitialSchemaJobAudit.d.ts.map +1 -0
- package/dist/migrations/1778328780881-InitialSchemaJobAudit.js +93 -0
- package/dist/migrations/1778328780881-InitialSchemaJobAudit.js.map +1 -0
- package/dist/test/global-setup.d.ts +3 -0
- package/dist/test/global-setup.d.ts.map +1 -0
- package/dist/test/global-setup.js +34 -0
- package/dist/test/global-setup.js.map +1 -0
- package/dist/test/redis-config.d.ts +3 -0
- package/dist/test/redis-config.d.ts.map +1 -0
- package/dist/test/redis-config.js +8 -0
- package/dist/test/redis-config.js.map +1 -0
- package/dist/test/setup.d.ts +2 -0
- package/dist/test/setup.d.ts.map +1 -0
- package/dist/test/setup.js +2 -0
- package/dist/test/setup.js.map +1 -0
- package/dist/test/specs/batch-post-processor/batch-post-processor.int.spec.d.ts +2 -0
- package/dist/test/specs/batch-post-processor/batch-post-processor.int.spec.d.ts.map +1 -0
- package/dist/test/specs/batch-post-processor/batch-post-processor.int.spec.js +87 -0
- package/dist/test/specs/batch-post-processor/batch-post-processor.int.spec.js.map +1 -0
- package/dist/test/specs/batch-post-processor/batch-post-processor.unit.spec.d.ts +2 -0
- package/dist/test/specs/batch-post-processor/batch-post-processor.unit.spec.d.ts.map +1 -0
- package/dist/test/specs/batch-post-processor/batch-post-processor.unit.spec.js +62 -0
- package/dist/test/specs/batch-post-processor/batch-post-processor.unit.spec.js.map +1 -0
- package/dist/test/specs/single-post-processor/single-post-processor.int.spec.d.ts +2 -0
- package/dist/test/specs/single-post-processor/single-post-processor.int.spec.d.ts.map +1 -0
- package/dist/test/specs/single-post-processor/single-post-processor.int.spec.js +89 -0
- package/dist/test/specs/single-post-processor/single-post-processor.int.spec.js.map +1 -0
- package/dist/test/specs/single-post-processor/single-post-processor.unit.spec.d.ts +2 -0
- package/dist/test/specs/single-post-processor/single-post-processor.unit.spec.d.ts.map +1 -0
- package/dist/test/specs/single-post-processor/single-post-processor.unit.spec.js +131 -0
- package/dist/test/specs/single-post-processor/single-post-processor.unit.spec.js.map +1 -0
- package/dist/test/utils/classes/test-batch-post-processor.class.d.ts +11 -0
- package/dist/test/utils/classes/test-batch-post-processor.class.d.ts.map +1 -0
- package/dist/test/utils/classes/test-batch-post-processor.class.js +20 -0
- package/dist/test/utils/classes/test-batch-post-processor.class.js.map +1 -0
- package/dist/test/utils/classes/test-post-processor.class.d.ts +12 -0
- package/dist/test/utils/classes/test-post-processor.class.d.ts.map +1 -0
- package/dist/test/utils/classes/test-post-processor.class.js +20 -0
- package/dist/test/utils/classes/test-post-processor.class.js.map +1 -0
- package/dist/test/utils/enums/test-messaging.enum.d.ts +16 -0
- package/dist/test/utils/enums/test-messaging.enum.d.ts.map +1 -0
- package/dist/test/utils/enums/test-messaging.enum.js +20 -0
- package/dist/test/utils/enums/test-messaging.enum.js.map +1 -0
- package/dist/test/utils/factories/test-event.factory.d.ts +5 -0
- package/dist/test/utils/factories/test-event.factory.d.ts.map +1 -0
- package/dist/test/utils/factories/test-event.factory.js +49 -0
- package/dist/test/utils/factories/test-event.factory.js.map +1 -0
- package/dist/test/utils/index.d.ts +7 -0
- package/dist/test/utils/index.d.ts.map +1 -0
- package/dist/test/utils/index.js +7 -0
- package/dist/test/utils/index.js.map +1 -0
- package/dist/test/utils/mocks/redis-call.mock.d.ts +21 -0
- package/dist/test/utils/mocks/redis-call.mock.d.ts.map +1 -0
- package/dist/test/utils/mocks/redis-call.mock.js +73 -0
- package/dist/test/utils/mocks/redis-call.mock.js.map +1 -0
- package/dist/test/utils/types/test-messaging.types.d.ts +9 -0
- package/dist/test/utils/types/test-messaging.types.d.ts.map +1 -0
- package/dist/test/utils/types/test-messaging.types.js +2 -0
- package/dist/test/utils/types/test-messaging.types.js.map +1 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/post-processor.types.d.ts +14 -0
- package/dist/types/post-processor.types.d.ts.map +1 -0
- package/dist/types/post-processor.types.js +2 -0
- package/dist/types/post-processor.types.js.map +1 -0
- package/package.json +62 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Logger } from '@volontariapp/logger';
|
|
2
|
+
import type { Redis } from 'ioredis';
|
|
3
|
+
import type { PostProcessorOptions } from '../interfaces/index.js';
|
|
4
|
+
import type { RedisStreamEntry } from '../types/index.js';
|
|
5
|
+
export declare abstract class BasePostProcessor {
|
|
6
|
+
protected readonly redis: Redis;
|
|
7
|
+
protected readonly logger: Logger;
|
|
8
|
+
protected readonly options: Required<PostProcessorOptions>;
|
|
9
|
+
private isRunning;
|
|
10
|
+
private readPending;
|
|
11
|
+
private claimTimeout;
|
|
12
|
+
private loopPromise;
|
|
13
|
+
constructor(redis: Redis, options: PostProcessorOptions);
|
|
14
|
+
start(): Promise<void>;
|
|
15
|
+
stop(): Promise<void>;
|
|
16
|
+
protected abstract processEntries(entries: RedisStreamEntry[]): Promise<void>;
|
|
17
|
+
protected shouldProcess(_eventType: string): boolean;
|
|
18
|
+
protected acknowledge(messageId: string): Promise<void>;
|
|
19
|
+
private ensureConsumerGroup;
|
|
20
|
+
private runLoop;
|
|
21
|
+
private processNextCycle;
|
|
22
|
+
private buildXreadgroupArgs;
|
|
23
|
+
private startClaimLoop;
|
|
24
|
+
private claimPendingMessages;
|
|
25
|
+
private claimMessagesList;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=base.post-processor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base.post-processor.d.ts","sourceRoot":"","sources":["../../src/core/base.post-processor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAE9C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AACnE,OAAO,KAAK,EAAE,gBAAgB,EAA0B,MAAM,mBAAmB,CAAC;AAGlF,8BAAsB,iBAAiB;IASnC,SAAS,CAAC,QAAQ,CAAC,KAAK,EAAE,KAAK;IARjC,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAClC,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,oBAAoB,CAAC,CAAC;IAC3D,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,WAAW,CAAQ;IAC3B,OAAO,CAAC,YAAY,CAA+B;IACnD,OAAO,CAAC,WAAW,CAA8B;gBAG5B,KAAK,EAAE,KAAK,EAC/B,OAAO,EAAE,oBAAoB;IAmBzB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAmCtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IA4B3B,SAAS,CAAC,QAAQ,CAAC,cAAc,CAAC,OAAO,EAAE,gBAAgB,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAM7E,SAAS,CAAC,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO;cAOpC,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YAa/C,mBAAmB;YA2BnB,OAAO;YAeP,gBAAgB;IAsC9B,OAAO,CAAC,mBAAmB;IAoB3B,OAAO,CAAC,cAAc;YAkBR,oBAAoB;YAgCpB,iBAAiB;CAoBhC"}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { Logger } from '@volontariapp/logger';
|
|
2
|
+
import { hostname } from 'node:os';
|
|
3
|
+
import { RedisStreamHelper } from './redis-stream.helper.js';
|
|
4
|
+
export class BasePostProcessor {
|
|
5
|
+
redis;
|
|
6
|
+
logger;
|
|
7
|
+
options;
|
|
8
|
+
isRunning = false;
|
|
9
|
+
readPending = true;
|
|
10
|
+
claimTimeout = null;
|
|
11
|
+
loopPromise = null;
|
|
12
|
+
constructor(redis, options) {
|
|
13
|
+
this.redis = redis;
|
|
14
|
+
this.logger = new Logger({ context: this.constructor.name });
|
|
15
|
+
const host = hostname();
|
|
16
|
+
this.options = {
|
|
17
|
+
streamName: options.streamName,
|
|
18
|
+
groupName: options.groupName,
|
|
19
|
+
consumerName: options.consumerName ?? `${host}-${this.constructor.name}`,
|
|
20
|
+
batchSize: options.batchSize ?? 10,
|
|
21
|
+
blockMs: options.blockMs ?? 2000,
|
|
22
|
+
claimIntervalMs: options.claimIntervalMs ?? 30000,
|
|
23
|
+
claimMinIdleTimeMs: options.claimMinIdleTimeMs ?? 60000,
|
|
24
|
+
idempotencyTtlSeconds: options.idempotencyTtlSeconds ?? 86400,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
async start() {
|
|
28
|
+
if (this.isRunning) {
|
|
29
|
+
this.logger.warn('Post-processor is already running');
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
this.isRunning = true;
|
|
33
|
+
this.readPending = true;
|
|
34
|
+
this.logger.info('Starting post-processor', {
|
|
35
|
+
streamName: this.options.streamName,
|
|
36
|
+
groupName: this.options.groupName,
|
|
37
|
+
consumerName: this.options.consumerName,
|
|
38
|
+
});
|
|
39
|
+
try {
|
|
40
|
+
await this.ensureConsumerGroup();
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
44
|
+
this.logger.error('Failed to initialize consumer group', { error });
|
|
45
|
+
this.isRunning = false;
|
|
46
|
+
throw error;
|
|
47
|
+
}
|
|
48
|
+
this.loopPromise = this.runLoop().catch((err) => {
|
|
49
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
50
|
+
this.logger.error('Post-processor loop crashed', { error });
|
|
51
|
+
this.isRunning = false;
|
|
52
|
+
});
|
|
53
|
+
this.startClaimLoop();
|
|
54
|
+
}
|
|
55
|
+
async stop() {
|
|
56
|
+
if (!this.isRunning) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
this.logger.info('Stopping post-processor...');
|
|
60
|
+
this.isRunning = false;
|
|
61
|
+
if (this.claimTimeout) {
|
|
62
|
+
clearTimeout(this.claimTimeout);
|
|
63
|
+
this.claimTimeout = null;
|
|
64
|
+
}
|
|
65
|
+
if (this.loopPromise) {
|
|
66
|
+
try {
|
|
67
|
+
await this.loopPromise;
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
this.logger.error('Failed to stop post-processor', { error: err });
|
|
71
|
+
}
|
|
72
|
+
this.loopPromise = null;
|
|
73
|
+
}
|
|
74
|
+
this.logger.info('Post-processor stopped');
|
|
75
|
+
}
|
|
76
|
+
shouldProcess(_eventType) {
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
async acknowledge(messageId) {
|
|
80
|
+
try {
|
|
81
|
+
await this.redis.call('XACK', this.options.streamName, this.options.groupName, messageId);
|
|
82
|
+
this.logger.info('Acknowledged message', { messageId });
|
|
83
|
+
}
|
|
84
|
+
catch (err) {
|
|
85
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
86
|
+
this.logger.error('Failed to acknowledge message', { messageId, error });
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
async ensureConsumerGroup() {
|
|
90
|
+
try {
|
|
91
|
+
await this.redis.call('XGROUP', 'CREATE', this.options.streamName, this.options.groupName, '0', 'MKSTREAM');
|
|
92
|
+
this.logger.info('Consumer group created', {
|
|
93
|
+
streamName: this.options.streamName,
|
|
94
|
+
groupName: this.options.groupName,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
catch (err) {
|
|
98
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
99
|
+
if (error.message.includes('BUSYGROUP')) {
|
|
100
|
+
this.logger.debug('Consumer group already exists');
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
throw error;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
async runLoop() {
|
|
108
|
+
while (this.isRunning) {
|
|
109
|
+
try {
|
|
110
|
+
await this.processNextCycle();
|
|
111
|
+
}
|
|
112
|
+
catch (err) {
|
|
113
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
114
|
+
this.logger.error('Error in post-processor consumption cycle', { error });
|
|
115
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
async processNextCycle() {
|
|
120
|
+
const idToRead = this.readPending ? '0' : '>';
|
|
121
|
+
const args = this.buildXreadgroupArgs(idToRead);
|
|
122
|
+
const rawResult = (await this.redis.call('XREADGROUP', ...args));
|
|
123
|
+
if (!rawResult || rawResult.length === 0) {
|
|
124
|
+
this.logger.warn('No raw entries received', {
|
|
125
|
+
idToRead,
|
|
126
|
+
readPending: this.readPending,
|
|
127
|
+
args,
|
|
128
|
+
});
|
|
129
|
+
if (idToRead === '0')
|
|
130
|
+
this.readPending = false;
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
const rawEntries = rawResult[0][1];
|
|
134
|
+
if (rawEntries.length === 0) {
|
|
135
|
+
this.logger.warn('No raw entries received');
|
|
136
|
+
if (idToRead === '0')
|
|
137
|
+
this.readPending = false;
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
const entries = RedisStreamHelper.parseRawEntries(rawEntries);
|
|
141
|
+
this.logger.info('Fetched entries from stream', {
|
|
142
|
+
count: entries.length,
|
|
143
|
+
readPending: this.readPending,
|
|
144
|
+
});
|
|
145
|
+
await this.processEntries(entries);
|
|
146
|
+
}
|
|
147
|
+
buildXreadgroupArgs(idToRead) {
|
|
148
|
+
const args = [
|
|
149
|
+
'GROUP',
|
|
150
|
+
this.options.groupName,
|
|
151
|
+
this.options.consumerName,
|
|
152
|
+
'COUNT',
|
|
153
|
+
this.options.batchSize,
|
|
154
|
+
];
|
|
155
|
+
if (idToRead === '>') {
|
|
156
|
+
args.push('BLOCK', this.options.blockMs);
|
|
157
|
+
}
|
|
158
|
+
args.push('STREAMS', this.options.streamName, idToRead);
|
|
159
|
+
return args;
|
|
160
|
+
}
|
|
161
|
+
startClaimLoop() {
|
|
162
|
+
if (!this.isRunning)
|
|
163
|
+
return;
|
|
164
|
+
this.claimTimeout = setTimeout(() => {
|
|
165
|
+
this.claimPendingMessages()
|
|
166
|
+
.catch((err) => {
|
|
167
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
168
|
+
this.logger.error('Failed claiming pending messages', { error });
|
|
169
|
+
})
|
|
170
|
+
.finally(() => {
|
|
171
|
+
this.startClaimLoop();
|
|
172
|
+
});
|
|
173
|
+
}, this.options.claimIntervalMs);
|
|
174
|
+
}
|
|
175
|
+
async claimPendingMessages() {
|
|
176
|
+
this.logger.debug('Scanning for pending messages to claim');
|
|
177
|
+
const pendingMessages = await RedisStreamHelper.getPendingMessages(this.redis, this.options.streamName, this.options.groupName, this.options.batchSize);
|
|
178
|
+
const claimable = pendingMessages.filter((msg) => msg.consumerName !== this.options.consumerName &&
|
|
179
|
+
msg.idleTimeMs >= this.options.claimMinIdleTimeMs);
|
|
180
|
+
if (claimable.length === 0)
|
|
181
|
+
return;
|
|
182
|
+
this.logger.info('Claiming idle pending messages', {
|
|
183
|
+
count: claimable.length,
|
|
184
|
+
messageIds: claimable.map((c) => c.messageId),
|
|
185
|
+
});
|
|
186
|
+
const claimedCount = await this.claimMessagesList(claimable.map((c) => c.messageId));
|
|
187
|
+
if (claimedCount > 0) {
|
|
188
|
+
this.readPending = true;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
async claimMessagesList(messageIds) {
|
|
192
|
+
let claimedCount = 0;
|
|
193
|
+
for (const id of messageIds) {
|
|
194
|
+
try {
|
|
195
|
+
await RedisStreamHelper.claimMessage(this.redis, this.options.streamName, this.options.groupName, this.options.consumerName, this.options.claimMinIdleTimeMs, id);
|
|
196
|
+
claimedCount++;
|
|
197
|
+
}
|
|
198
|
+
catch (err) {
|
|
199
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
200
|
+
this.logger.error('Failed to claim message', { messageId: id, error });
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return claimedCount;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
//# sourceMappingURL=base.post-processor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base.post-processor.js","sourceRoot":"","sources":["../../src/core/base.post-processor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { BasePostProcessor } from './base.post-processor.js';
|
|
2
|
+
import type { EventMessagingType } from '@volontariapp/messaging';
|
|
3
|
+
import type { RedisStreamEntry } from '../types/index.js';
|
|
4
|
+
import type { BatchEventItem } from '../interfaces/index.js';
|
|
5
|
+
export declare abstract class BatchPostProcessor<TKey extends EventMessagingType = EventMessagingType> extends BasePostProcessor {
|
|
6
|
+
protected abstract processEvents(events: BatchEventItem<TKey>[]): Promise<void>;
|
|
7
|
+
protected processEntries(entries: RedisStreamEntry[]): Promise<void>;
|
|
8
|
+
private filterAndLockEntry;
|
|
9
|
+
private parseAndAccumulateEntry;
|
|
10
|
+
private executeBatchProcessing;
|
|
11
|
+
private releaseIdempotencyLocks;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=batch.post-processor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"batch.post-processor.d.ts","sourceRoot":"","sources":["../../src/core/batch.post-processor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,KAAK,EAAE,kBAAkB,EAA8B,MAAM,yBAAyB,CAAC;AAC9F,OAAO,KAAK,EAAE,gBAAgB,EAAkB,MAAM,mBAAmB,CAAC;AAC1E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAG7D,8BAAsB,kBAAkB,CACtC,IAAI,SAAS,kBAAkB,GAAG,kBAAkB,CACpD,SAAQ,iBAAiB;IACzB,SAAS,CAAC,QAAQ,CAAC,aAAa,CAAC,MAAM,EAAE,cAAc,CAAC,IAAI,CAAC,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;cAKtD,cAAc,CAAC,OAAO,EAAE,gBAAgB,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;YAiBrE,kBAAkB;YA0ClB,uBAAuB;YA6BvB,sBAAsB;YAyBtB,uBAAuB;CAUtC"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { BasePostProcessor } from './base.post-processor.js';
|
|
2
|
+
import { RedisStreamHelper } from './redis-stream.helper.js';
|
|
3
|
+
export class BatchPostProcessor extends BasePostProcessor {
|
|
4
|
+
async processEntries(entries) {
|
|
5
|
+
const items = [];
|
|
6
|
+
const acquiredMessageIds = [];
|
|
7
|
+
const ttl = this.options.idempotencyTtlSeconds;
|
|
8
|
+
for (const entry of entries) {
|
|
9
|
+
await this.filterAndLockEntry(entry, ttl, items, acquiredMessageIds);
|
|
10
|
+
}
|
|
11
|
+
if (items.length === 0)
|
|
12
|
+
return;
|
|
13
|
+
await this.executeBatchProcessing(items, acquiredMessageIds, ttl > 0);
|
|
14
|
+
}
|
|
15
|
+
async filterAndLockEntry(entry, ttl, items, acquiredMessageIds) {
|
|
16
|
+
const { id, fields } = entry;
|
|
17
|
+
if (!fields.event) {
|
|
18
|
+
this.logger.warn('Stream message missing event payload, acknowledging and skipping', {
|
|
19
|
+
messageId: id,
|
|
20
|
+
});
|
|
21
|
+
await this.acknowledge(id);
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
if (!this.shouldProcess(fields.type ?? '')) {
|
|
25
|
+
this.logger.debug('Skipping message: type not registered/handled', {
|
|
26
|
+
messageId: id,
|
|
27
|
+
type: fields.type,
|
|
28
|
+
});
|
|
29
|
+
await this.acknowledge(id);
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
if (ttl > 0 &&
|
|
33
|
+
!(await RedisStreamHelper.acquireIdempotencyLock(this.redis, this.options.groupName, id, ttl))) {
|
|
34
|
+
this.logger.warn('Message already processed or currently processing (idempotency block)', {
|
|
35
|
+
messageId: id,
|
|
36
|
+
});
|
|
37
|
+
await this.acknowledge(id);
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
return this.parseAndAccumulateEntry(id, fields.event, ttl > 0, items, acquiredMessageIds);
|
|
41
|
+
}
|
|
42
|
+
async parseAndAccumulateEntry(id, rawEvent, useIdempotency, items, acquiredMessageIds) {
|
|
43
|
+
try {
|
|
44
|
+
const event = JSON.parse(rawEvent);
|
|
45
|
+
items.push({ event, messageId: id });
|
|
46
|
+
if (useIdempotency)
|
|
47
|
+
acquiredMessageIds.push(id);
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
catch (err) {
|
|
51
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
52
|
+
this.logger.error('Failed to parse event payload, acknowledging and skipping', {
|
|
53
|
+
messageId: id,
|
|
54
|
+
error,
|
|
55
|
+
});
|
|
56
|
+
await this.acknowledge(id);
|
|
57
|
+
if (useIdempotency) {
|
|
58
|
+
await RedisStreamHelper.removeIdempotencyLock(this.redis, this.options.groupName, id);
|
|
59
|
+
}
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
async executeBatchProcessing(items, acquiredMessageIds, useIdempotency) {
|
|
64
|
+
try {
|
|
65
|
+
this.logger.info('Processing batch of events', { count: items.length });
|
|
66
|
+
await this.processEvents(items);
|
|
67
|
+
for (const item of items) {
|
|
68
|
+
await this.acknowledge(item.messageId);
|
|
69
|
+
}
|
|
70
|
+
this.logger.info('Successfully processed batch of events', { count: items.length });
|
|
71
|
+
}
|
|
72
|
+
catch (err) {
|
|
73
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
74
|
+
this.logger.error('Failed to process batch of events from stream', { error });
|
|
75
|
+
if (useIdempotency) {
|
|
76
|
+
await this.releaseIdempotencyLocks(acquiredMessageIds);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
async releaseIdempotencyLocks(messageIds) {
|
|
81
|
+
for (const id of messageIds) {
|
|
82
|
+
try {
|
|
83
|
+
await RedisStreamHelper.removeIdempotencyLock(this.redis, this.options.groupName, id);
|
|
84
|
+
}
|
|
85
|
+
catch (err) {
|
|
86
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
87
|
+
this.logger.error('Failed to release idempotency lock', { messageId: id, error });
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=batch.post-processor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"batch.post-processor.js","sourceRoot":"","sources":["../../src/core/batch.post-processor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { BasePostProcessor } from './base.post-processor.js';
|
|
2
|
+
export { SinglePostProcessor } from './single.post-processor.js';
|
|
3
|
+
export { BatchPostProcessor } from './batch.post-processor.js';
|
|
4
|
+
export { RedisStreamHelper } from './redis-stream.helper.js';
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { BasePostProcessor } from './base.post-processor.js';
|
|
2
|
+
export { SinglePostProcessor } from './single.post-processor.js';
|
|
3
|
+
export { BatchPostProcessor } from './batch.post-processor.js';
|
|
4
|
+
export { RedisStreamHelper } from './redis-stream.helper.js';
|
|
5
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Redis } from 'ioredis';
|
|
2
|
+
import type { RedisStreamEntry, RedisStreamRawEntry } from '../types/index.js';
|
|
3
|
+
import type { PendingMessageInfo } from '../interfaces/index.js';
|
|
4
|
+
export declare class RedisStreamHelper {
|
|
5
|
+
static parseRawEntries(rawEntries: RedisStreamRawEntry[]): RedisStreamEntry[];
|
|
6
|
+
static getPendingMessages(redis: Redis, streamName: string, groupName: string, batchSize: number): Promise<PendingMessageInfo[]>;
|
|
7
|
+
static claimMessage(redis: Redis, streamName: string, groupName: string, consumerName: string, claimMinIdleTimeMs: number, messageId: string): Promise<void>;
|
|
8
|
+
static getIdempotencyKey(groupName: string, messageId: string): string;
|
|
9
|
+
static acquireIdempotencyLock(redis: Redis, groupName: string, messageId: string, ttlSeconds: number): Promise<boolean>;
|
|
10
|
+
static removeIdempotencyLock(redis: Redis, groupName: string, messageId: string): Promise<void>;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=redis-stream.helper.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis-stream.helper.d.ts","sourceRoot":"","sources":["../../src/core/redis-stream.helper.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,KAAK,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAC/E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAEjE,qBAAa,iBAAiB;IAI5B,MAAM,CAAC,eAAe,CAAC,UAAU,EAAE,mBAAmB,EAAE,GAAG,gBAAgB,EAAE;WAmBhE,kBAAkB,CAC7B,KAAK,EAAE,KAAK,EACZ,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,kBAAkB,EAAE,CAAC;WAuBnB,YAAY,CACvB,KAAK,EAAE,KAAK,EACZ,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,EACpB,kBAAkB,EAAE,MAAM,EAC1B,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC;IAehB,MAAM,CAAC,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM;WAQzD,sBAAsB,CACjC,KAAK,EAAE,KAAK,EACZ,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,OAAO,CAAC;WASN,qBAAqB,CAChC,KAAK,EAAE,KAAK,EACZ,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC;CAIjB"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export class RedisStreamHelper {
|
|
2
|
+
static parseRawEntries(rawEntries) {
|
|
3
|
+
const entries = [];
|
|
4
|
+
for (const [id, fieldsArray] of rawEntries) {
|
|
5
|
+
const fields = {};
|
|
6
|
+
for (let i = 0; i < fieldsArray.length; i += 2) {
|
|
7
|
+
const key = fieldsArray[i];
|
|
8
|
+
const val = fieldsArray[i + 1];
|
|
9
|
+
if (key && val) {
|
|
10
|
+
fields[key] = val;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
entries.push({ id, fields });
|
|
14
|
+
}
|
|
15
|
+
return entries;
|
|
16
|
+
}
|
|
17
|
+
static async getPendingMessages(redis, streamName, groupName, batchSize) {
|
|
18
|
+
const rawPending = (await redis.call('XPENDING', streamName, groupName, '-', '+', batchSize));
|
|
19
|
+
if (!rawPending)
|
|
20
|
+
return [];
|
|
21
|
+
return rawPending.map(([messageId, consumerName, idleTimeMs, deliveryCount]) => ({
|
|
22
|
+
messageId,
|
|
23
|
+
consumerName,
|
|
24
|
+
idleTimeMs: Number(idleTimeMs),
|
|
25
|
+
deliveryCount: Number(deliveryCount),
|
|
26
|
+
}));
|
|
27
|
+
}
|
|
28
|
+
static async claimMessage(redis, streamName, groupName, consumerName, claimMinIdleTimeMs, messageId) {
|
|
29
|
+
await redis.call('XCLAIM', streamName, groupName, consumerName, claimMinIdleTimeMs.toString(), messageId, 'JUSTID');
|
|
30
|
+
}
|
|
31
|
+
static getIdempotencyKey(groupName, messageId) {
|
|
32
|
+
return `idempotency:post-processor:${groupName}:${messageId}`;
|
|
33
|
+
}
|
|
34
|
+
static async acquireIdempotencyLock(redis, groupName, messageId, ttlSeconds) {
|
|
35
|
+
const key = this.getIdempotencyKey(groupName, messageId);
|
|
36
|
+
const result = await redis.call('SET', key, 'processing', 'NX', 'EX', ttlSeconds);
|
|
37
|
+
return result === 'OK';
|
|
38
|
+
}
|
|
39
|
+
static async removeIdempotencyLock(redis, groupName, messageId) {
|
|
40
|
+
const key = this.getIdempotencyKey(groupName, messageId);
|
|
41
|
+
await redis.del(key);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=redis-stream.helper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis-stream.helper.js","sourceRoot":"","sources":["../../src/core/redis-stream.helper.ts"],"names":[],"mappings":"AAIA,MAAM,OAAO,iBAAiB;IAI5B,MAAM,CAAC,eAAe,CAAC,UAAiC;QACtD,MAAM,OAAO,GAAuB,EAAE,CAAC;QACvC,KAAK,MAAM,CAAC,EAAE,EAAE,WAAW,CAAC,IAAI,UAAU,EAAE,CAAC;YAC3C,MAAM,MAAM,GAA2B,EAAE,CAAC;YAC1C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,WAAW,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC/C,MAAM,GAAG,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;gBAC3B,MAAM,GAAG,GAAG,WAAW,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;gBAC/B,IAAI,GAAG,IAAI,GAAG,EAAE,CAAC;oBACf,MAAM,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;gBACpB,CAAC;YACH,CAAC;YACD,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;QAC/B,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;IAKD,MAAM,CAAC,KAAK,CAAC,kBAAkB,CAC7B,KAAY,EACZ,UAAkB,EAClB,SAAiB,EACjB,SAAiB;QAEjB,MAAM,UAAU,GAAG,CAAC,MAAM,KAAK,CAAC,IAAI,CAClC,UAAU,EACV,UAAU,EACV,SAAS,EACT,GAAG,EACH,GAAG,EACH,SAAS,CACV,CAA8C,CAAC;QAEhD,IAAI,CAAC,UAAU;YAAE,OAAO,EAAE,CAAC;QAE3B,OAAO,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,aAAa,CAAC,EAAE,EAAE,CAAC,CAAC;YAC/E,SAAS;YACT,YAAY;YACZ,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC;YAC9B,aAAa,EAAE,MAAM,CAAC,aAAa,CAAC;SACrC,CAAC,CAAC,CAAC;IACN,CAAC;IAKD,MAAM,CAAC,KAAK,CAAC,YAAY,CACvB,KAAY,EACZ,UAAkB,EAClB,SAAiB,EACjB,YAAoB,EACpB,kBAA0B,EAC1B,SAAiB;QAEjB,MAAM,KAAK,CAAC,IAAI,CACd,QAAQ,EACR,UAAU,EACV,SAAS,EACT,YAAY,EACZ,kBAAkB,CAAC,QAAQ,EAAE,EAC7B,SAAS,EACT,QAAQ,CACT,CAAC;IACJ,CAAC;IAKD,MAAM,CAAC,iBAAiB,CAAC,SAAiB,EAAE,SAAiB;QAC3D,OAAO,8BAA8B,SAAS,IAAI,SAAS,EAAE,CAAC;IAChE,CAAC;IAMD,MAAM,CAAC,KAAK,CAAC,sBAAsB,CACjC,KAAY,EACZ,SAAiB,EACjB,SAAiB,EACjB,UAAkB;QAElB,MAAM,GAAG,GAAG,IAAI,CAAC,iBAAiB,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;QACzD,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC;QAClF,OAAO,MAAM,KAAK,IAAI,CAAC;IACzB,CAAC;IAKD,MAAM,CAAC,KAAK,CAAC,qBAAqB,CAChC,KAAY,EACZ,SAAiB,EACjB,SAAiB;QAEjB,MAAM,GAAG,GAAG,IAAI,CAAC,iBAAiB,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;QACzD,MAAM,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACvB,CAAC;CACF"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { BasePostProcessor } from './base.post-processor.js';
|
|
2
|
+
import type { EventMessagingType, EventRegistry, StreamEvent, EventChangedPayload } from '@volontariapp/messaging';
|
|
3
|
+
import type { RedisStreamEntry } from '../types/index.js';
|
|
4
|
+
type ExtractPayload<T> = T extends EventChangedPayload<infer P> ? P : T;
|
|
5
|
+
export declare abstract class SinglePostProcessor<TKey extends EventMessagingType = EventMessagingType> extends BasePostProcessor {
|
|
6
|
+
protected abstract processEvent(event: StreamEvent<ExtractPayload<EventRegistry[TKey]>>, messageId: string): Promise<void>;
|
|
7
|
+
protected processEntries(entries: RedisStreamEntry[]): Promise<void>;
|
|
8
|
+
private processSingleEntry;
|
|
9
|
+
private executeEventProcessing;
|
|
10
|
+
}
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=single.post-processor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"single.post-processor.d.ts","sourceRoot":"","sources":["../../src/core/single.post-processor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,KAAK,EACV,kBAAkB,EAClB,aAAa,EACb,WAAW,EACX,mBAAmB,EACpB,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAG1D,KAAK,cAAc,CAAC,CAAC,IAAI,CAAC,SAAS,mBAAmB,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AAExE,8BAAsB,mBAAmB,CACvC,IAAI,SAAS,kBAAkB,GAAG,kBAAkB,CACpD,SAAQ,iBAAiB;IAIzB,SAAS,CAAC,QAAQ,CAAC,YAAY,CAC7B,KAAK,EAAE,WAAW,CAAC,cAAc,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,EACvD,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC;cAKS,cAAc,CAAC,OAAO,EAAE,gBAAgB,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;YASrE,kBAAkB;YAsClB,sBAAsB;CAwBrC"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { BasePostProcessor } from './base.post-processor.js';
|
|
2
|
+
import { RedisStreamHelper } from './redis-stream.helper.js';
|
|
3
|
+
export class SinglePostProcessor extends BasePostProcessor {
|
|
4
|
+
async processEntries(entries) {
|
|
5
|
+
for (const entry of entries) {
|
|
6
|
+
await this.processSingleEntry(entry);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
async processSingleEntry(entry) {
|
|
10
|
+
const { id, fields } = entry;
|
|
11
|
+
if (!fields.event) {
|
|
12
|
+
this.logger.warn('Stream message missing event payload, acknowledging and skipping', {
|
|
13
|
+
messageId: id,
|
|
14
|
+
});
|
|
15
|
+
await this.acknowledge(id);
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
if (!this.shouldProcess(fields.type ?? '')) {
|
|
19
|
+
this.logger.debug('Skipping message: type not registered/handled', {
|
|
20
|
+
messageId: id,
|
|
21
|
+
type: fields.type,
|
|
22
|
+
});
|
|
23
|
+
await this.acknowledge(id);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const ttl = this.options.idempotencyTtlSeconds;
|
|
27
|
+
if (ttl > 0 &&
|
|
28
|
+
!(await RedisStreamHelper.acquireIdempotencyLock(this.redis, this.options.groupName, id, ttl))) {
|
|
29
|
+
this.logger.warn('Message already processed or currently processing (idempotency block)', {
|
|
30
|
+
messageId: id,
|
|
31
|
+
});
|
|
32
|
+
await this.acknowledge(id);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
await this.executeEventProcessing(id, fields.event, ttl > 0);
|
|
36
|
+
}
|
|
37
|
+
async executeEventProcessing(id, rawEvent, useIdempotency) {
|
|
38
|
+
try {
|
|
39
|
+
const event = JSON.parse(rawEvent);
|
|
40
|
+
this.logger.info('Processing event', { messageId: id, eventId: event.id, type: event.type });
|
|
41
|
+
await this.processEvent(event, id);
|
|
42
|
+
await this.acknowledge(id);
|
|
43
|
+
this.logger.info('Successfully processed event', {
|
|
44
|
+
messageId: id,
|
|
45
|
+
eventId: event.id,
|
|
46
|
+
type: event.type,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
51
|
+
this.logger.error('Failed to process event from stream', { messageId: id, error });
|
|
52
|
+
if (useIdempotency) {
|
|
53
|
+
await RedisStreamHelper.removeIdempotencyLock(this.redis, this.options.groupName, id);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=single.post-processor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"single.post-processor.js","sourceRoot":"","sources":["../../src/core/single.post-processor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data-source.d.ts","sourceRoot":"","sources":["../src/data-source.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAKrC,eAAO,MAAM,cAAc,YAWzB,CAAC;AAEH,eAAO,MAAM,gBAAgB,qBAU5B,CAAC;AAEF,eAAO,MAAM,WAAW,qBAIvB,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { DataSource } from 'typeorm';
|
|
2
|
+
import { JobAuditModel, JobsOutboxModel } from '@volontariapp/database';
|
|
3
|
+
import { InitialSchemaJobAudit1778328780881 } from './migrations/1778328780881-InitialSchemaJobAudit.js';
|
|
4
|
+
import { CreateJobsOutbox1776783577425 } from './migrations/1776783577425-CreateJobsOutbox.js';
|
|
5
|
+
export const testDataSource = new DataSource({
|
|
6
|
+
type: 'postgres',
|
|
7
|
+
host: '127.0.0.1',
|
|
8
|
+
port: 5433,
|
|
9
|
+
username: 'testuser',
|
|
10
|
+
password: 'testpassword',
|
|
11
|
+
database: 'volontariapp_test',
|
|
12
|
+
entities: [JobAuditModel, JobsOutboxModel],
|
|
13
|
+
migrations: [CreateJobsOutbox1776783577425, InitialSchemaJobAudit1778328780881],
|
|
14
|
+
synchronize: false,
|
|
15
|
+
logging: false,
|
|
16
|
+
});
|
|
17
|
+
export const initializeTestDb = async () => {
|
|
18
|
+
if (!testDataSource.isInitialized) {
|
|
19
|
+
await testDataSource.initialize();
|
|
20
|
+
const queryRunner = testDataSource.createQueryRunner();
|
|
21
|
+
await queryRunner.dropTable('job_audit', true);
|
|
22
|
+
await queryRunner.dropTable('jobs_outbox', true);
|
|
23
|
+
await queryRunner.dropTable('migrations', true);
|
|
24
|
+
await queryRunner.release();
|
|
25
|
+
await testDataSource.runMigrations();
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
export const closeTestDb = async () => {
|
|
29
|
+
if (testDataSource.isInitialized) {
|
|
30
|
+
await testDataSource.destroy();
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
//# sourceMappingURL=data-source.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data-source.js","sourceRoot":"","sources":["../src/data-source.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACxE,OAAO,EAAE,kCAAkC,EAAE,MAAM,qDAAqD,CAAC;AACzG,OAAO,EAAE,6BAA6B,EAAE,MAAM,gDAAgD,CAAC;AAE/F,MAAM,CAAC,MAAM,cAAc,GAAG,IAAI,UAAU,CAAC;IAC3C,IAAI,EAAE,UAAU;IAChB,IAAI,EAAE,WAAW;IACjB,IAAI,EAAE,IAAI;IACV,QAAQ,EAAE,UAAU;IACpB,QAAQ,EAAE,cAAc;IACxB,QAAQ,EAAE,mBAAmB;IAC7B,QAAQ,EAAE,CAAC,aAAa,EAAE,eAAe,CAAC;IAC1C,UAAU,EAAE,CAAC,6BAA6B,EAAE,kCAAkC,CAAC;IAC/E,WAAW,EAAE,KAAK;IAClB,OAAO,EAAE,KAAK;CACf,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,gBAAgB,GAAG,KAAK,IAAI,EAAE;IACzC,IAAI,CAAC,cAAc,CAAC,aAAa,EAAE,CAAC;QAClC,MAAM,cAAc,CAAC,UAAU,EAAE,CAAC;QAClC,MAAM,WAAW,GAAG,cAAc,CAAC,iBAAiB,EAAE,CAAC;QACvD,MAAM,WAAW,CAAC,SAAS,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;QAC/C,MAAM,WAAW,CAAC,SAAS,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC;QACjD,MAAM,WAAW,CAAC,SAAS,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC;QAChD,MAAM,WAAW,CAAC,OAAO,EAAE,CAAC;QAC5B,MAAM,cAAc,CAAC,aAAa,EAAE,CAAC;IACvC,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,WAAW,GAAG,KAAK,IAAI,EAAE;IACpC,IAAI,cAAc,CAAC,aAAa,EAAE,CAAC;QACjC,MAAM,cAAc,CAAC,OAAO,EAAE,CAAC;IACjC,CAAC;AACH,CAAC,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,uBAAuB,CAAC;AACtC,cAAc,kBAAkB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,uBAAuB,CAAC;AACtC,cAAc,kBAAkB,CAAC"}
|