This template provides a grab-bag of example Deciders, including illustrations of the following generic techniques:
Consists of:
-
Period
: a Decider that manages ingestion into a chain of periodsEach period within the sequence has the following lifecycle:
- Carrying Forward a Balance from its immediate predecessor
Period
; then - being open for transactions for a period of time; ultimately
- carrying forward a closing balance to its successor Period
- Carrying Forward a Balance from its immediate predecessor
Notes:
- A given
Period
can thus be read without any need to load any preceding periods, as by definition, all relevant information has beenCarriedForward
- Any
Period
is guaranteed to have been initialized; thus the preceding epochs can safely be archived the moment the first event has been written to a givenPeriod
Consists of:
ExactlyOnceIngester
: Generic algorithm that manages efficient deterministic ingestion into a chain of epochs in a seriesListEpoch
: Represents a span of activity during the life of the list. May be closed at an arbitrary point in time by any writer.ListSeries
: Records the identifier of the current active Epoch of the series.ListIngester
: Uses anExactlyOnceIngester
to insert at the tail of a chain ofListEpoch
s indexed by aListSeries
ExactlyOnceIngester
accomplishes this by having each insertion logically:
- 'take a ticket':- grab the current epoch id from which we'll commence an attempt to insert into the list (the Ingester holds this memory for efficiency)
- Ensure this is stored at source in order to ensure that an idempotent reprocessing can guarantee to traverse the epochs from the same starting point
- Coalesce concurrent requests in order that concurrent insertions do not result in optimistic concurrency violations when those writers all converge on the same stream in the underlying store
NOTE: the feedSource
template illustrates a different ingestion scheme with different properties:-
- No requirement for the source to be able to log a starting epochId as necessary in the scheme implemented here
- Best-effort deduplication only (lookback is a defined number of batches, repeats outside that window are possible), on the assumption that the consumer will be able to deal with that cleanly
- Slightly more code, uses some memory to maintain the deduplication list
In general, the scheme here is a better default approach unless you have specific performance requirements which dictate that the scheme in feedSource
is the only one that's viable