Skip to content

Commit

Permalink
AP-4870 Transaction observability on bg listeners
Browse files Browse the repository at this point in the history
  • Loading branch information
CarlosGamero committed Aug 30, 2024
1 parent 892cf5b commit 74ff154
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 3 deletions.
86 changes: 86 additions & 0 deletions packages/core/lib/events/DomainEventEmitter.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,14 @@ describe('AutopilotEventEmitter', () => {
it('emits event to anyListener - foreground', async () => {
const fakeListener = new FakeListener()
eventEmitter.onAny(fakeListener)
const transactionManagerStartSpy = vi.spyOn(
diContainer.cradle.transactionObservabilityManager,
'startWithGroup',
)
const transactionManagerStopSpy = vi.spyOn(
diContainer.cradle.transactionObservabilityManager,
'stop',
)

const emittedEvent = await eventEmitter.emit(TestEvents.created, createdEventPayload)

Expand All @@ -74,6 +82,19 @@ describe('AutopilotEventEmitter', () => {
expect(processedEvent.message.type).toBe(TestEvents.created.consumerSchema.shape.type.value)
expect(fakeListener.receivedEvents).toHaveLength(1)
expect(fakeListener.receivedEvents[0]).toMatchObject(expectedCreatedPayload)

expect(transactionManagerStartSpy).toHaveBeenCalledOnce()
expect(transactionManagerStartSpy).toHaveBeenCalledWith(
'fg_event_listener:entity.created:FakeListener',
expect.any(String),
'entity.created',
)

expect(transactionManagerStopSpy).toHaveBeenCalledOnce()
expect(transactionManagerStopSpy).toHaveBeenCalledWith(
transactionManagerStartSpy.mock.calls[0][1],
true,
)
})

it('emits event to anyListener - background', async () => {
Expand Down Expand Up @@ -213,11 +234,32 @@ describe('AutopilotEventEmitter', () => {
it('emits event to singleListener - foreground', async () => {
const fakeListener = new FakeListener()
eventEmitter.on('entity.created', fakeListener)
const transactionManagerStartSpy = vi.spyOn(
diContainer.cradle.transactionObservabilityManager,
'startWithGroup',
)
const transactionManagerStopSpy = vi.spyOn(
diContainer.cradle.transactionObservabilityManager,
'stop',
)

await eventEmitter.emit(TestEvents.created, createdEventPayload)

expect(fakeListener.receivedEvents).toHaveLength(1)
expect(fakeListener.receivedEvents[0]).toMatchObject(expectedCreatedPayload)

expect(transactionManagerStartSpy).toHaveBeenCalledOnce()
expect(transactionManagerStartSpy).toHaveBeenCalledWith(
'fg_event_listener:entity.created:FakeListener',
expect.any(String),
'entity.created',
)

expect(transactionManagerStopSpy).toHaveBeenCalledOnce()
expect(transactionManagerStopSpy).toHaveBeenCalledWith(
transactionManagerStartSpy.mock.calls[0][1],
true,
)
})

it('emits event to singleListener - background', async () => {
Expand Down Expand Up @@ -259,13 +301,24 @@ describe('AutopilotEventEmitter', () => {
it('emits event to manyListener - foreground', async () => {
const fakeListener = new FakeListener()
eventEmitter.onMany(['entity.created', 'entity.updated'], fakeListener)
const transactionManagerStartSpy = vi.spyOn(
diContainer.cradle.transactionObservabilityManager,
'startWithGroup',
)
const transactionManagerStopSpy = vi.spyOn(
diContainer.cradle.transactionObservabilityManager,
'stop',
)

await eventEmitter.emit(TestEvents.created, createdEventPayload)
await eventEmitter.emit(TestEvents.updated, updatedEventPayload)

expect(fakeListener.receivedEvents).toHaveLength(2)
expect(fakeListener.receivedEvents[0]).toMatchObject(expectedCreatedPayload)
expect(fakeListener.receivedEvents[1]).toMatchObject(expectedUpdatedPayload)

expect(transactionManagerStartSpy).toHaveBeenCalledTimes(2)
expect(transactionManagerStopSpy).toHaveBeenCalledTimes(2)
})

it('emits event to manyListener - background', async () => {
Expand Down Expand Up @@ -294,6 +347,39 @@ describe('AutopilotEventEmitter', () => {
expect(transactionManagerStopSpy).toHaveBeenCalledTimes(2)
})

it('foreground listener error handling', async () => {
const fakeListener = new ErroredFakeListener()
eventEmitter.onAny(fakeListener)
const transactionManagerStartSpy = vi.spyOn(
diContainer.cradle.transactionObservabilityManager,
'startWithGroup',
)
const transactionManagerStopSpy = vi.spyOn(
diContainer.cradle.transactionObservabilityManager,
'stop',
)

await expect(eventEmitter.emit(TestEvents.created, createdEventPayload)).rejects.toThrow(
'ErroredFakeListener error',
)

expect(fakeListener.receivedEvents).toHaveLength(1)
expect(fakeListener.receivedEvents[0]).toMatchObject(expectedCreatedPayload)

expect(transactionManagerStartSpy).toHaveBeenCalledOnce()
expect(transactionManagerStartSpy).toHaveBeenCalledWith(
'fg_event_listener:entity.created:ErroredFakeListener',
expect.any(String),
'entity.created',
)

expect(transactionManagerStopSpy).toHaveBeenCalledOnce()
expect(transactionManagerStopSpy).toHaveBeenCalledWith(
transactionManagerStartSpy.mock.calls[0][1],
false,
)
})

it('background listener error handling', async () => {
const fakeListener = new ErroredFakeListener(100)
eventEmitter.onAny(fakeListener, true)
Expand Down
19 changes: 16 additions & 3 deletions packages/core/lib/events/DomainEventEmitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,19 @@ export class DomainEventEmitter<SupportedEvents extends CommonEventDefinition[]>

const fgHandlers = [...eventHandlers.foreground, ...this.anyHandlers.foreground]
for (const handler of fgHandlers) {
await handler.handleEvent(event)
const transactionId = randomUUID()
let isSuccessfull = false
try {
this.transactionObservabilityManager?.startWithGroup(
this.buildTransactionKey(event, handler, false),
transactionId,
event.type,
)
await handler.handleEvent(event)
isSuccessfull = true
} finally {
this.transactionObservabilityManager?.stop(transactionId, isSuccessfull)
}
}

const bgHandlers = [...eventHandlers.background, ...this.anyHandlers.background]
Expand All @@ -184,7 +196,7 @@ export class DomainEventEmitter<SupportedEvents extends CommonEventDefinition[]>
// not sure if we should use startWithGroup or start, using group to group all handlers for the same event type
// should it be eventId + eventType or just eventType?
this.transactionObservabilityManager?.startWithGroup(
this.buildTransactionKey(event, handler),
this.buildTransactionKey(event, handler, true),
transactionId,
event.type,
)
Expand Down Expand Up @@ -212,7 +224,8 @@ export class DomainEventEmitter<SupportedEvents extends CommonEventDefinition[]>
private buildTransactionKey<SupportedEvent extends SupportedEvents[number]>(
event: CommonEventDefinitionPublisherSchemaType<SupportedEvent>,
handler: EventHandler<CommonEventDefinitionPublisherSchemaType<SupportedEvent>>,
isBackgroundHandler: boolean,
): string {
return `bg_event_listener:${event.type}:${handler.eventHandlerId}`
return `${isBackgroundHandler ? 'bg' : 'fg'}_event_listener:${event.type}:${handler.eventHandlerId}`
}
}

0 comments on commit 74ff154

Please sign in to comment.