packages feed

keiro-core-0.2.0.0: src/Keiro/Snapshot/Policy.hs

{- | Evaluation of a stream's 'SnapshotPolicy'.

This is the single decision procedure command handling consults after an
append to decide whether to persist a snapshot of the folded state. It
keeps the 'SnapshotPolicy' constructors purely declarative — the meaning
of each constructor lives here.
-}
module Keiro.Snapshot.Policy (
    shouldSnapshot,
    shouldSnapshotSpan,
)
where

import Keiro.EventStream (SnapshotPolicy (..), Terminality (..))
import Keiro.Prelude
import Kiroku.Store.Types (StreamVersion (..))
import Prelude qualified

{- | Decide whether to snapshot given a policy, a terminality flag, the
folded state, and the post-append stream version.

* 'Never' is always 'False'.
* 'Every' @n@ is 'True' when the version is a positive multiple of @n@
  (a non-positive interval never fires).
* 'OnTerminal' mirrors the @terminal@ flag — snapshot exactly when the
  machine has reached a final state.
* 'Custom' defers to the caller-supplied predicate over state and version.
-}
shouldSnapshot :: SnapshotPolicy state -> Terminality -> state -> StreamVersion -> Bool
shouldSnapshot Never _ _ _ = False
shouldSnapshot (Every interval) _ _ (StreamVersion version)
    | interval <= 0 = False
    | otherwise = version > 0 Prelude.&& version `Prelude.mod` Prelude.fromIntegral interval == 0
shouldSnapshot OnTerminal terminality _ _ = terminality == Terminal
shouldSnapshot (Custom decide) terminality state version = decide terminality state version

{- | Like 'shouldSnapshot', but evaluated over the half-open stream-version
span @(preVersion, postVersion]@ that one append covered.

For 'Every' @n@, this fires when any positive multiple of @n@ lies inside
the span, so a batch append that jumps over a boundary still snapshots at
the post-append version. The other policies ignore the span and behave like
'shouldSnapshot'.
-}
shouldSnapshotSpan ::
    SnapshotPolicy state ->
    Terminality ->
    state ->
    StreamVersion ->
    StreamVersion ->
    Bool
shouldSnapshotSpan Never _ _ _ _ = False
shouldSnapshotSpan (Every interval) _ _ (StreamVersion preVersion) (StreamVersion postVersion)
    | interval <= 0 = False
    | postVersion <= 0 = False
    | otherwise = postVersion `Prelude.div` n > preVersion `Prelude.div` n
  where
    n = Prelude.fromIntegral interval
shouldSnapshotSpan OnTerminal terminality _ _ _ = terminality == Terminal
shouldSnapshotSpan (Custom decide) terminality state _ postVersion = decide terminality state postVersion