Как правильно обрабатывать новые события в Event Sourcing Projector, если транзакции могут выполняться не по порядку?
В процессе изучения новых архитектурных подходов я решил поэкспериментировать с Event Sourcing + CQRS. Вместо того чтобы использовать готовые специализированные базы данных для Event Sourcing (вроде EventStoreDB), я на энтузиазме реализовал хранилище событий прямо в PostgreSQL:
CREATE TABLE event_store (
stream_id UUID NOT NULL,
stream_type VARCHAR(255) NOT NULL,
event_type VARCHAR(255) NOT NULL,
event JSONB NOT NULL,
version BIGINT NOT NULL,
created_at_utc TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (stream_id, stream_type)
);
Поскольку у меня был отдельный read-сервис (микросервис для чтения данных со своей БД), я решил реализовать Projector — фоновый процесс, который периодически опрашивает event_store, находит новые события и отправляет их в read-сервис.
Изначально я хотел добавить автоинкрементный ID для событий и запоминать последний обработанный ID в отдельной таблице, чтобы делать выборку примерно так:
SELECT * FROM event_store
WHERE Id > @last_processed_id
Но возникла проблема: если транзакция вставки события зависнет, новые события могут записаться раньше, и тогда Projector пропустит "застрявшее" событие.
Я не до конца понимаю, как правильно реализовать этот механизм, и в интернете мало толковой информации о работе Projector в Event Sourcing. Какие есть варианты решения?