Occurrent 0.20.4 is released with the following changes:
- Fixed a silent event loss in
CatchupSubscriptionModelat the handover from the catch-up phase to the live subscription.- An event written during the catch-up replay whose CloudEvent
timewas earlier than the replay cursor (a writer with a clock running behind) could be skipped by the delta reconciliation and also sit below the live subscription’s resume position, so no delivery path saw it. Since it was a miss and not a duplicate, idempotent consumers could not recover it. - The delta now reconciles events written during the replay by insertion order (
SortBy.naturaldescending with a limit) instead of the time-basedskip, so a clock-skewed event is still among the newest events and is delivered. It also reads only the recent tail instead of walking the whole backlog, so it is faster on large stores, and it removes a spurious duplicate the old reconciliation produced. InMemoryEventStorenow makesSortBy.naturalreflect global insertion order, matching MongoDB’s$natural, so the catch-up reconciliation behaves the same on both stores.
- An event written during the catch-up replay whose CloudEvent
- Fixed a
ConcurrentModificationExceptionthat could occur when consuming a stream returned byInMemoryEventStore.query(..)while another thread writes to the store.- The returned stream was lazy, so its sort, skip, and limit ran after the query lock was released. A concurrent
write(..)modifies the backing map at that point, which could throw or expose an in-flight stream. query(..)now snapshots the per-stream event lists while holding the lock and filters and sorts on that snapshot afterwards, so the lock is no longer held during filtering.- This matters more now because the
CatchupSubscriptionModeldelta reconciliation above queries withSortBy.naturaldescending, so the in-memory store can be read concurrently with writes during a catch-up.
- The returned stream was lazy, so its sort, skip, and limit ran after the query lock was released. A concurrent