20
20
- [ Loading any revision of the aggregate] ( #4-5 )
21
21
- [ Synchronously updating projections] ( #4-6 )
22
22
- [ Asynchronously sending integration events to a message broker] ( #4-7 )
23
- - [ Reliable transactional outbox with PostgreSQL] ( #4-7-1 )
24
- - [ Database polling] ( #4-7-2 )
25
- - [ Database polling alternative] ( #4-7-3 )
23
+ - [ Transactional outbox using transaction ID] ( #4-7-1 )
24
+ - [ Transactional outbox using table-level lock] ( #4-7-2 )
25
+ - [ Database polling] ( #4-7-3 )
26
+ - [ Listen/Notify as an alternative to database polling] ( #4-7-4 )
26
27
- [ Adding new asynchronous event handlers] ( #4-8 )
27
28
- [ Drawbacks] ( #4-9 )
28
29
- [ Project structure] ( #5 )
@@ -390,7 +391,7 @@ and processes them:
390
391
WHERE SUBSCRIPTION_NAME = :subscriptionName
391
392
```
392
393
393
- #### <a id="4-7-1"></a>Reliable transactional outbox with PostgreSQL
394
+ #### <a id="4-7-1"></a>Transactional outbox using transaction ID
394
395
395
396
Using only the event ID to track events processed by the subscription is unreliable
396
397
and can result in lost events.
@@ -423,9 +424,43 @@ All transaction IDs less than `xmin` are either committed and visible, or rolled
423
424
Even if transaction #2 started after transaction #1 and committed first,
424
425
the events it created won' t be read by the event subscription processor until transaction # 1 is committed.
425
426
426
- 
427
+ 
427
428
428
- # ### <a id="4-7-2"></a>Database polling
429
+ > ** NOTE**
430
+ > The transaction ID solution is used by default as it is non- blocking.
431
+
432
+ # ### <a id="4-7-2"></a>Transactional outbox using table-level lock
433
+
434
+ With the transaction ID solution, event subscription processor doesn' t wait for in-progress transactions to complete.
435
+ Events created by already committed transactions will not be available for processing
436
+ while transactions started earlier are still in-progress.
437
+ These events will be processed immediately after these earlier transactions have completed.
438
+
439
+ An alternative solution is to use PostgreSQL explicit locking to make event subscription processor wait for in-progress transactions.
440
+
441
+ Before reading new events, the event subscription processor
442
+ * gets the most recently issued ID sequence number,
443
+ * very briefly locks the table for writes to wait for all pending writes to complete.
444
+
445
+ The most recently issued `ES_EVENT_ID_SEQ` sequence number is obtained using the `pg_sequence_last_value` function:
446
+ `SELECT pg_sequence_last_value(' ES_EVENT_ID_SEQ' )`.
447
+
448
+ Events are created with the command `INSERT INTO ES_EVENT...` that acquires the `ROW EXCLUSIVE` lock mode on `ES_EVENT` table.
449
+ `ROW EXCLUSIVE (RowExclusiveLock)` lock mode is acquired by any command that modifies data in a table.
450
+
451
+ The command `LOCK ES_EVENT IN SHARE ROW EXCLUSIVE MODE` acquires the `SHARE ROW EXCLUSIVE` lock mode on `ES_EVENT` table.
452
+ `SHARE ROW EXCLUSIVE (ShareRowExclusiveLock)` mode protects a table against concurrent data changes,
453
+ and is self-exclusive so that only one session can hold it at a time.
454
+
455
+ `SHARE ROW EXCLUSIVE` lock must be acquired in a separate transaction (`Propagation.REQUIRES_NEW`).
456
+ The transaction must contain only this command and commit quickly to release the lock, so writes can resume.
457
+
458
+ When the lock is acquired and released, it means
459
+ that there are no more uncommitted writes with an ID less than or equal to the ID returned by `pg_sequence_last_value`.
460
+
461
+ 
462
+
463
+ #### <a id="4-7-3"></a>Database polling
429
464
430
465
To get new events from the `ES_EVENT` table, the application has to poll the database.
431
466
The shorter the polling period, the shorter the delay between persisting a new event and processing it by the subscription.
@@ -444,7 +479,7 @@ event-sourcing:
444
479
polling-interval: PT1S
445
480
```
446
481
447
- # ### <a id="4-7-3 "></a>Database polling alternative
482
+ #### <a id="4-7-4 "></a>Listen/Notify as an alternative to database polling
448
483
449
484
To reduce the lag associated with database polling, the polling period can be set to a very low value,
450
485
such as 1 second.
@@ -481,7 +516,8 @@ event-sourcing:
481
516
subscriptions: postgres-channel # Enable Listen/Notify event subscription processing
482
517
` ` `
483
518
484
- This mechanism is used by default as more efficient.
519
+ > ** NOTE**
520
+ > The Listen/ Notify mechanism is used by default as it is more efficient.
485
521
486
522
# ## <a id="4-8"></a>Adding new asynchronous event handlers
487
523
0 commit comments