tx- Persistent transactions on top of STM.

Safe HaskellNone





class SafeCopy (Update d) => Persistable d whereSource

The type family at the heart of TX.

You make any data type you want to use with the TX monad an instance of Persistable and define Update constructors for each of the methods acting on this type that you want to be able to record during a transaction. Then you implement replay in such a way that for each of the Update constructors, the appropiate method is called.


 data MyDB = MyDB { posts :: TVar [String] }

 instance Persistable MyDB where
     data Update MyDB = CreatePost String
                      | ModifyPost Int String

     replay (CreatePost p)   = void $ createPost p
     replay (ModifyPost n p) = modifyPost n p

where createPost and modifyPost are functions in the TX monad:

 createPost :: String -> TX MyDB Int
 createPost p = do
     record (CreatePost p)
     (MyDB posts) <- getData
     liftSTM $ do
         ps <- readTVar posts
         writeTVar posts (ps ++ [p])
         return $ length ps

 modifyPost :: Int -> String -> TX MyDB ()
 modifyPost n p = do
     record (ModifyPost n p)
     (MyDB posts) <- getData
     liftSTM $ do
         ps <- readTVar posts
         let (xs,ys) = splitAt n ps
             ps'     = xs ++ p : (tail ys)
         writeTVar posts ps'

Note that Update also needs to be an instance of SafeCopy. Currently, it's not possible to derive SafeCopy instances for associated types automatically, so you have to do it by hand:

 instance SafeCopy (Update MyDB) where
     putCopy (CreatePost p)   = contain $ putWord8 0 >> safePut p
     putCopy (ModifyPost n p) = contain $ putWord8 1 >> safePut n >> safePut p
     getCopy = contain $ do
         tag <- getWord8
         case tag of
             0 -> CreatePost <$> safeGet
             1 -> ModifyPost <$> safeGet <*> safeGet
             _ -> fail $ "unknown tag: " ++ show tag

Associated Types

data Update d Source


replay :: Update d -> TX d ()Source

Managing the database

data Database d Source

An opaque type wrapping any kind of user data for use in the TX monad.



:: Persistable d 
=> FilePath

Location of the log file.

-> d

Base data. Any existing log is replayed on top of this.

-> IO (Database d) 

Opens the database at the given path or creates a new one.

closeDatabase :: Database d -> IO ()Source

Close a database. Blocks until all pending recordings been serialized. Using a database after it has been closed is an error.

withUserData :: Database d -> (d -> a) -> aSource

Operate non-persistently on the user data contained in the database.

The TX monad

data TX d a Source

A thin wrapper around STM. The main feature is the ability to record updates of the underlying data.


Monad (TX d) 
Functor (TX d) 
Applicative (TX d) 

persistently :: Database d -> TX d a -> IO aSource

Perform a series of TX actions persistently.

Note that there is no guarantee that all recorded updates have been serialized when the functions returns. As such, durability is only partially guaranteed.

Since this calls atomically on the underlying STM actions, the same caveats apply (e.g. you can't use it inside unsafePerformIO).

record :: Update d -> TX d ()Source

Record an Update to be serialized to disk when the transaction commits. If the transaction retries, the update is still only recorded once. If the transaction aborts, the update is not recorded at all.

getData :: TX d dSource

Get the user data from the database.

liftSTM :: STM a -> TX d aSource

Run STM actions inside TX.

throwTX :: Exception e => e -> TX d aSource

Throw an exception in TX, which will abort the transaction.

throwTX = liftSTM . throwSTM

unsafeIOToTX :: IO a -> TX d aSource

Unsafely performs IO in the TX monad. Highly dangerous! The same caveats as with unsafeIOToSTM apply.

unsafeIOToTX = liftSTM . unsafeIOToSTM

Utility functions

(<?>) :: Exception e => TX d (Maybe a) -> e -> TX d aSource

act <?> err = maybe (throwTX err) return =<< act