Introducing simple-store and simple-cell
simple-store1 provides persistence, atomicity and consistency for a shared data type. simple-cell2 uses simple-store and directed-keys3 to create multiple atomic values with the unique keys. We will review a few concepts before discussing these packages further.
Concurrency control4 ensures that concurrent operations are safe and fast and solves the following problems:
- lost update
- dirty read
- incorrect summary
We will only discuss one type here, optimistic concurrency control.
Optimistic concurrency control (OCC)
OCC5 assumes that multiple transactions can be made frequently without interfering with each other. OCC does not use locks6. Rather, before committing each transaction, it verifies that no other transaction has modified the data it has read. If the check reveals that modifications have occurred, the committing transaction rolls back and can be restarted. OCC is good for environments with low data contention.
- begin: record a timestamp which marks the beginning of a transaction.
- modify: read db values, tentatively write changes.
- validate: check if other transactions have modified the data that the transaction has read/wrote.
- commit/rollback: if there is no conflict, make all changes take effect, otherwise abort the changes.
Software transactional memory (STM)
STM7 is a type of OCC. A thread modifies shared memory without concern for what other threads may be doing to that memory. STM makes the reader responsible for making sure nothing is operating on the shared memory.
Concurrent Haskell and STM
MVar t8 is a mutable location that is empty or contains a value
putMVarsets the value in an
readMVargets the value in an
MVarand sets it to the value it just took.
takeMVargets the value in an
MVarand sets the value to empty.
TMVar t9 is the STM version of
MVar and is thread safe.
putTMVarsets the value in an
readTMVargets the value in an
TMVarand sets it to the value it just took.
takeTMVargets the value in an
TMVarand sets the value to empty.
cereal10 is a package the performs binary serialization. By declaring an instance of the
Serialize type class, we can perform serialization and deserialization on a type. It can also use
GHC.Generics to automatically declare a
directed-keys provides a data type and functions to serialize data to and from Base6411.
DirectedKeyRawis data type that has a unique key, a source url/file path, a destination url/file path and a time. They all need an instance of
ByteStringof the data in
DKeyRawconstructor, but it enforces the
Serializerestriction on the keys.
decodeKeyto an from a base64
decodeFilenameescape and unescape the necesssary Unix characters in a file path.
The main data type is
SimpleStore. You do not need to manipulate
SimpleStore records directly. simple-store provides a set of functions to save and retrieve data via the
SimpleStore data type and filesystem.
The most important functions are:
makeSimpleStoresave a serializable type to a file
openSimpleStoreread a deserializable type from a file
getSimpleStoreget the data type value from the
SimpleCell takes a function to retrieve keys and a function to make those keys into filenames. It maintains a key-value pair of filename to
SimpleStore. By convention, simple-cell uses a type suffixed
Store as a newtype wrapped entity of an entity with DB specific properties (like external keys).
The general work flow is:
- Declare CellKey value for the data type we want to store.
- Generate functions with Template Haskell.
- Initialize a
- Pass the cell around manually or with
- Insert data with
- Perform batch operations with
- Get stores out the cell with
We need to define three functions for
CellKey for looking up, decoding and encoding a
Then we use Template Haskell to produce a set of type specific functions.
makeStoreCell generates the following functions, but you have to provide the type signature for each of them to help the Template Haskell.
We generally do not need to manipulate the
SimpleCell data type directly, but it is helpful to know what it contains.
cellCoreis an in-memory representation of the map of keys and
cellKeyis the key provided by the user.
cellParentFPis the file path that the root of the project is in.
cellRootFPis the file path that the cell occupies.
Types to remember for simple-cell:
For a complete example, take a look at the simple-cell tests.