mirror of
https://github.com/aelve/guide.git
synced 2024-11-23 21:13:07 +03:00
Add a note about acid-state
This commit is contained in:
parent
e4555d0da3
commit
c408b8b08d
25
src/Main.hs
25
src/Main.hs
@ -59,12 +59,28 @@ import JS (JS(..), allJSFunctions)
|
||||
import Utils
|
||||
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
-- working with global state via acid-state
|
||||
------------------------------------------------------------------------------
|
||||
{- Note [acid-state]
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This application doesn't use a database – instead, it uses acid-state. Acid-state works as follows:
|
||||
|
||||
* Everything is stored as Haskell values (in particular, all data is stored in 'GlobalState').
|
||||
|
||||
* All changes to the state (and all queries) have to be done by using 'dbUpdate'/'dbQuery' and types (GetItem, SetItemName, etc) from the Types.hs module.
|
||||
|
||||
* The data is kept in-memory, but all changes are logged to the disk (which lets us recover the state in case of a crash by reapplying the changes) and you can't access the state directly. When the application exits, it creates a snapshot of the state (called “checkpoint”) and writes it to the disk. Additionally, a checkpoint is created every hour (grep for “createCheckpoint”).
|
||||
|
||||
* When any type is changed, we have to write a migration function that would read the old version of the type and turn it into the new version. It's enough to keep just one old version (and even that isn't needed after the migration happened and a new checkpoint has been created). For examples, look at “instance Migrate” in Types.hs. Also, all types involved in acid-state (whether migrate-able or not) have to have a SafeCopy instance, which is generated by 'deriveSafeCopy'.
|
||||
|
||||
* There are actually ways to access the state directly (GetGlobalState and SetGlobalState), but the latter should only be used when doing something one-off (like migrating all IDs to a different ID scheme, or whatever).
|
||||
|
||||
-}
|
||||
|
||||
-- | A pointer to an open acid-state database (allows making queries/updates,
|
||||
-- creating checkpoints, etc).
|
||||
type DB = AcidState GlobalState
|
||||
|
||||
-- | Update something in the database.
|
||||
dbUpdate :: (MonadIO m, HasSpock m, SpockState m ~ ServerState,
|
||||
EventState event ~ GlobalState, UpdateEvent event)
|
||||
=> event -> m (EventResult event)
|
||||
@ -72,6 +88,7 @@ dbUpdate x = do
|
||||
db <- _db <$> Spock.getState
|
||||
liftIO $ Acid.update db x
|
||||
|
||||
-- | Read something from the database.
|
||||
dbQuery :: (MonadIO m, HasSpock m, SpockState m ~ ServerState,
|
||||
EventState event ~ GlobalState, QueryEvent event)
|
||||
=> event -> m (EventResult event)
|
||||
@ -317,6 +334,8 @@ main = do
|
||||
-- running. This makes running this in GHCi annoying, because you have to
|
||||
-- restart GHCi before every run. So, we kill the thread in the finaliser.
|
||||
ekgId <- newIORef Nothing
|
||||
-- See Note [acid-state] for the explanation of 'openLocalStateFrom',
|
||||
-- 'createCheckpoint', etc
|
||||
let prepare = openLocalStateFrom "state/" emptyState
|
||||
finalise db = do
|
||||
createCheckpoint db
|
||||
|
@ -113,12 +113,15 @@ data Trait = Trait {
|
||||
_traitContent :: MarkdownInline }
|
||||
deriving (Eq)
|
||||
|
||||
-- See Note [acid-state]
|
||||
deriveSafeCopy 1 'extension ''Trait
|
||||
makeFields ''Trait
|
||||
|
||||
-- Old version, needed for safe migration. It can most likely be already
|
||||
-- deleted (if a checkpoint has been created), but it's been left here as a
|
||||
-- template for future migrations.
|
||||
--
|
||||
-- Again, see Note [acid-state].
|
||||
data Trait_v0 = Trait_v0 {
|
||||
_traitUid_v0 :: Uid,
|
||||
_traitContent_v0 :: Text }
|
||||
@ -306,6 +309,7 @@ instance Migrate Category where
|
||||
|
||||
--
|
||||
|
||||
-- See Note [acid-state]
|
||||
data GlobalState = GlobalState {
|
||||
_categories :: [Category],
|
||||
_categoriesDeleted :: [Category] }
|
||||
|
@ -112,6 +112,7 @@ makeSlug =
|
||||
newtype Uid = Uid {uidToText :: Text}
|
||||
deriving (Eq, Ord, Show, PathPiece, Format.Buildable)
|
||||
|
||||
-- See Note [acid-state]
|
||||
deriveSafeCopy 0 'base ''Uid
|
||||
|
||||
instance IsString Uid where
|
||||
|
Loading…
Reference in New Issue
Block a user