Skip to content

Commit 77794aa

Browse files
NeverBreakSpaghettilucagiove
authored andcommitted
feat(outbox): handle concurrency on publishEvents using publishEventWithConcurrencyControl method on every event
1 parent c6f97cc commit 77794aa

2 files changed

Lines changed: 10 additions & 42 deletions

File tree

packages/ddd-toolkit/src/outbox/mongo-outbox.spec.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { MongoMemoryReplSet } from 'mongodb-memory-server';
22
import { MongoClient, ObjectId } from 'mongodb';
33
import { MongoOutbox } from './mongo-outbox';
4-
import { Event } from '../event-bus/event';
4+
import { Event } from '../event-bus';
55
import { waitFor } from '../utils';
66

77
class FooEvent extends Event<{ foo: string }> {
@@ -89,14 +89,15 @@ describe('Mongo outbox', () => {
8989
});
9090

9191
describe('When publish', () => {
92-
it('should call publishEventsFn once', async () => {
92+
it('should call publishEventsFn for every event', async () => {
9393
await outbox.publishEvents(ids);
94-
expect(PublishEventsFnMock).toBeCalled();
94+
expect(PublishEventsFnMock).toHaveBeenCalledTimes(ids.length);
9595
});
9696

97-
it('should pass both events to publishEventsFn', async () => {
97+
it('should pass one event at time to publishEventsFn', async () => {
9898
await outbox.publishEvents(ids);
99-
expect(PublishEventsFnMock).toBeCalledWith(events);
99+
expect(PublishEventsFnMock).toHaveBeenCalledWith([events[0]]);
100+
expect(PublishEventsFnMock).toHaveBeenCalledWith([events[1]]);
100101
});
101102

102103
it('should update the status of the events to published', async () => {

packages/ddd-toolkit/src/outbox/mongo-outbox.ts

Lines changed: 4 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { ClientSession, Collection, MongoClient, ObjectId } from 'mongodb';
2-
import { IEvent } from '../event-bus/event-bus.interface';
2+
import { IEvent } from '../event-bus';
33
import { ILogger } from '../logger';
44
import { inspect } from 'util';
55
import { difference, intersection } from 'lodash';
@@ -57,42 +57,7 @@ export class MongoOutbox implements IOutbox, IInit, ITerminate {
5757
}
5858

5959
public async publishEvents(scheduledEventsIds: string[]): Promise<void> {
60-
const session = this.mongoClient.startSession();
61-
try {
62-
await session.withTransaction(async () => {
63-
const outboxModels = await this.outboxCollection
64-
.find(
65-
{
66-
_id: { $in: scheduledEventsIds.map((id) => new ObjectId(id)) },
67-
status: 'scheduled',
68-
},
69-
{ session },
70-
)
71-
.toArray();
72-
if (!outboxModels.length) return;
73-
const events = outboxModels.map((model) => model.event);
74-
await this.publishEventsFn(events);
75-
const publishedIds = outboxModels.map((model) => model._id);
76-
await this.outboxCollection.updateMany(
77-
{
78-
_id: { $in: publishedIds },
79-
status: 'scheduled',
80-
},
81-
{
82-
$set: {
83-
status: 'published',
84-
publishedAt: new Date(),
85-
},
86-
},
87-
{ session },
88-
);
89-
this.logger.debug(`Published events ${publishedIds.join(', ')}`);
90-
});
91-
} catch (e) {
92-
this.logger.warn(`Failed to publish events ${scheduledEventsIds.join(', ')}. ${inspect(e)}`);
93-
} finally {
94-
await session.endSession();
95-
}
60+
await Promise.all(scheduledEventsIds.map((eventId) => this.publishEventWithConcurrencyControl(eventId)));
9661
}
9762

9863
//FROM https://github.com/gpad/ms-practical-ws/blob/main/src/infra/outbox_pattern.ts
@@ -156,6 +121,8 @@ export class MongoOutbox implements IOutbox, IInit, ITerminate {
156121
{ session },
157122
);
158123
});
124+
} catch (e) {
125+
this.logger.warn(`Failed to publish event ${eventId}. ${inspect(e)}`);
159126
} finally {
160127
await session.endSession();
161128
}

0 commit comments

Comments
 (0)