feat(outbox): attempt cap → DLQ + admin endpoint + 6 unit-тестов
После N (default 1000) неудачных попыток публикации событие маркируется
dlq_at и исключается из polling. Без cap poller бесконечно ретраил мёртвые
сообщения (orphan topic, ACL deny), забивая логи и lag-метрику.
- 0011-outbox-dlq.xml: dlq_at column + перестроенный idx_outbox_unpublished
(исключает DLQ) + idx_outbox_dlq для admin-листинга.
- OutboxPoller: ordinis.outbox.attempt-max (default 1000), markDlq() при
достижении cap, новый counter ordinis_outbox_dlq_total.
- OutboxLagGauge: ordinis_outbox_dlq_size gauge — алерт на > 0.
- OutboxEventRepository: countPending() (без DLQ), findByDlqAtIsNotNull(Pageable).
- OutboxAdminController: GET /admin/outbox/{stats,dlq} для UI.
- 6 unit-тестов через mock-maker-subclass (JDK 25 ломает Mockito inline).
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<databaseChangeLog
|
||||
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
|
||||
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.27.xsd">
|
||||
|
||||
<!--
|
||||
Outbox DLQ: после N неудачных попыток публикации (ordinis.outbox.attempt-max,
|
||||
default 1000) события маркируются dlq_at и исключаются из поллинга. Без cap
|
||||
poller бесконечно ретраит мёртвые сообщения (orphan topic, тупик ACL),
|
||||
забивая лог и Prometheus alert ordinis_outbox_pending_events.
|
||||
-->
|
||||
|
||||
<changeSet id="0011-1-outbox-dlq-column" author="ordinis">
|
||||
<addColumn tableName="outbox_events">
|
||||
<column name="dlq_at" type="TIMESTAMPTZ"/>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
|
||||
<changeSet id="0011-2-outbox-rebuild-pending-index" author="ordinis">
|
||||
<comment>
|
||||
Pending index должен исключать dlq_at — иначе poller продолжит "видеть"
|
||||
DLQ-записи и ordinis_outbox_pending_events будет показывать их в lag.
|
||||
</comment>
|
||||
<sql>
|
||||
DROP INDEX IF EXISTS idx_outbox_unpublished;
|
||||
CREATE INDEX idx_outbox_unpublished ON outbox_events (created_at)
|
||||
WHERE published_at IS NULL AND dlq_at IS NULL;
|
||||
</sql>
|
||||
<rollback>
|
||||
<sql>
|
||||
DROP INDEX IF EXISTS idx_outbox_unpublished;
|
||||
CREATE INDEX idx_outbox_unpublished ON outbox_events (created_at)
|
||||
WHERE published_at IS NULL;
|
||||
</sql>
|
||||
</rollback>
|
||||
</changeSet>
|
||||
|
||||
<changeSet id="0011-3-outbox-dlq-index" author="ordinis">
|
||||
<comment>Партиальный индекс для быстрого листинга DLQ в admin UI.</comment>
|
||||
<sql>
|
||||
CREATE INDEX idx_outbox_dlq ON outbox_events (dlq_at DESC)
|
||||
WHERE dlq_at IS NOT NULL;
|
||||
</sql>
|
||||
<rollback>
|
||||
<sql>DROP INDEX IF EXISTS idx_outbox_dlq;</sql>
|
||||
</rollback>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
@@ -15,5 +15,6 @@
|
||||
<include file="changes/0008-fix-scope-check-uppercase.xml"/>
|
||||
<include file="changes/0009-fix-records-scope-check.xml"/>
|
||||
<include file="changes/0010-envers-tables.xml"/>
|
||||
<include file="changes/0011-outbox-dlq.xml"/>
|
||||
|
||||
</databaseChangeLog>
|
||||
|
||||
Reference in New Issue
Block a user