module Eventful.Store.Memory
( tvarEventStore
, tvarGloballyOrderedEventStore
, stateEventStore
, stateGloballyOrderedEventStore
, embeddedStateEventStore
, embeddedStateGloballyOrderedEventStore
, EventMap
, emptyEventMap
, eventMapTVar
, module Eventful.Store.Class
) where
import Control.Concurrent.STM
import Control.Monad.State.Class
import Data.Foldable (toList)
import Data.List (sortOn)
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as Map
import Data.Maybe (fromMaybe)
import Data.Sequence (Seq, (><))
import qualified Data.Sequence as Seq
import Eventful.Store.Class
import Eventful.UUID
data EventMap serialized
= EventMap
{ _eventMapUuidMap :: Map UUID (Seq (GloballyOrderedEvent serialized))
, _eventMapSeqNum :: SequenceNumber
}
deriving (Show)
emptyEventMap :: EventMap serialized
emptyEventMap = EventMap Map.empty 0
eventMapTVar :: IO (TVar (EventMap serialized))
eventMapTVar = newTVarIO emptyEventMap
tvarEventStore :: TVar (EventMap serialized) -> EventStore serialized STM
tvarEventStore tvar =
let
getLatestVersion uuid = flip latestEventVersion uuid <$> readTVar tvar
getEvents uuid range = (\s -> lookupEventsInRange s uuid range) <$> readTVar tvar
storeEvents' uuid events = modifyTVar' tvar (\store -> storeEventMap store uuid events)
storeEvents = transactionalExpectedWriteHelper getLatestVersion storeEvents'
in EventStore{..}
tvarGloballyOrderedEventStore :: TVar (EventMap serialized) -> GloballyOrderedEventStore serialized STM
tvarGloballyOrderedEventStore tvar =
let
getSequencedEvents range = flip lookupEventMapRange range <$> readTVar tvar
in GloballyOrderedEventStore{..}
stateEventStore
:: (MonadState (EventMap serialized) m)
=> EventStore serialized m
stateEventStore = embeddedStateEventStore id (flip const)
embeddedStateEventStore
:: (MonadState s m)
=> (s -> EventMap serialized)
-> (s -> EventMap serialized -> s)
-> EventStore serialized m
embeddedStateEventStore getMap setMap =
let
getLatestVersion uuid = flip latestEventVersion uuid <$> gets getMap
getEvents uuid range = (\s -> lookupEventsInRange s uuid range) <$> gets getMap
storeEvents' uuid events = modify' (modifyStore uuid events)
storeEvents = transactionalExpectedWriteHelper getLatestVersion storeEvents'
in EventStore{..}
where
modifyStore uuid events state' =
let
store = getMap state'
store' = storeEventMap store uuid events
in setMap state' store'
stateGloballyOrderedEventStore
:: (MonadState (EventMap serialized) m)
=> GloballyOrderedEventStore serialized m
stateGloballyOrderedEventStore = embeddedStateGloballyOrderedEventStore id
embeddedStateGloballyOrderedEventStore
:: (MonadState s m)
=> (s -> EventMap serialized)
-> GloballyOrderedEventStore serialized m
embeddedStateGloballyOrderedEventStore getMap =
let
getSequencedEvents range = flip lookupEventMapRange range <$> gets getMap
in GloballyOrderedEventStore{..}
lookupEventMapRaw :: EventMap serialized -> UUID -> Seq (StoredEvent serialized)
lookupEventMapRaw (EventMap uuidMap _) uuid =
fmap globallyOrderedEventToStoredEvent $ fromMaybe Seq.empty $ Map.lookup uuid uuidMap
lookupEventsInRange :: EventMap serialized -> UUID -> EventStoreQueryRange EventVersion -> [StoredEvent serialized]
lookupEventsInRange store uuid range = filterEventsByRange range' 0 rawEvents
where
range' = unEventVersion <$> range
rawEvents = toList $ lookupEventMapRaw store uuid
filterEventsByRange :: EventStoreQueryRange Int -> Int -> [event] -> [event]
filterEventsByRange EventStoreQueryRange{..} defaultStart events =
let
(start', events') =
case eventStoreQueryRangeStart of
StartFromBeginning -> (defaultStart, events)
StartQueryAt start -> (start, drop (start defaultStart) events)
events'' =
case eventStoreQueryRangeLimit of
NoQueryLimit -> events'
MaxNumberOfEvents num -> take num events'
StopQueryAt stop -> take (stop start' + 1) events'
in events''
latestEventVersion :: EventMap serialized -> UUID -> EventVersion
latestEventVersion store uuid = EventVersion $ Seq.length (lookupEventMapRaw store uuid) 1
lookupEventMapRange :: EventMap serialized -> EventStoreQueryRange SequenceNumber -> [GloballyOrderedEvent serialized]
lookupEventMapRange (EventMap uuidMap _) range = filterEventsByRange range' 1 rawEvents
where
range' = unSequenceNumber <$> range
rawEvents =
sortOn globallyOrderedEventSequenceNumber $
concat $
toList <$> toList uuidMap
storeEventMap
:: EventMap serialized -> UUID -> [serialized] -> EventMap serialized
storeEventMap store@(EventMap uuidMap seqNum) uuid events =
let
versStart = latestEventVersion store uuid + 1
storedEvents = zipWith storedEventToGloballyOrderedEvent [seqNum + 1..] $ zipWith (StoredEvent uuid) [versStart..] events
newMap = Map.insertWith (flip (><)) uuid (Seq.fromList storedEvents) uuidMap
newSeq = seqNum + (SequenceNumber $ length events)
in EventMap newMap newSeq