These are chat archives for ramda/ramda

28th
Dec 2015
Aldwin Vlasblom
@Avaq
Dec 28 2015 00:27
As I understand it: STM is meant to solve the problem of multiple processes or threads or clients attempting to modify the same "state". The problem I tried to solve is that of a single process attempting to modify several state in different places, atomically. For example: Inserting a record into a MySQL database, waiting for the insert ID to come back, then indexing that in ElasticSearch. If the second step were to fail, I'd give my user an error, but the insertion into MySQL would not have been undone, leaving me with a weird state of data.
Aldwin Vlasblom
@Avaq
Dec 28 2015 00:42
Maybe I'm overlooking something. I'm going to sleep on it. Thanks for your thoughts! :)
Hardy Jones
@joneshf
Dec 28 2015 01:38
I think you should be able to do that as well.
I still grapple with STM myuself, but here's a quick mockup of something you could go.
type MySQL = [(Int, String)]
type Elastic = [(Int, String)]

mySQL :: STM (TVar MySQL)
mySQL = newTVar []
elastic :: STM (TVar Elastic)
elastic = newTVar []

readBoth :: TVar MySQL -> TVar Elastic -> STM [(Int, String)]
readBoth m e = (++) <$> readTVar m <*> readTVar e

modifyBoth :: TVar MySQL -> TVar Elastic -> STM ()
modifyBoth m e = do
    modifyTVar m ((1, "wat"):)
    modifyTVar e ((2, "nope"):)

constrainElastic :: (Elastic -> Bool) -> TVar MySQL -> TVar Elastic -> STM [(Int, String)]
constrainElastic constraint m e = do 
    modifyBoth m e
    e' <- readTVar e
    check (constraint e') `orElse` throwSTM Underflow
    readBoth m e

good :: IO [(Int, String)]
good = atomically $ do 
    m <- mySQL
    e <- elastic
    constrainElastic (not . null) m e `catchSTM` (\Underflow -> readBoth m e)

bad :: IO [(Int, String)]
bad = atomically $ do 
    m <- mySQL
    e <- elastic
    constrainElastic null m e `catchSTM` (\Underflow -> readBoth m e)
If you run good you get [(1,"wat"),(2,"nope")], but if you run bad you get [].
Let me try to explain that.
There are two different memory locations, mySQL and elastic
a couple of helpers
modifyBoth attempts to add a row to each tvar.
readBoth just concatenates the rows together.
Hardy Jones
@joneshf
Dec 28 2015 01:43
constrainElastic modifies both tvars, then checks the given constraint against the elastic db, if it fails, it throws some random error.
otherwise it returns the concatenated rows.
good succeeds since the contraint passes--there's at least one new row, so it returns all the added rows.
bad fails since the constraint fails--there's at least one new row, so it rolls back the changes to both the mySQLtvar and the elastic tvar. Hence, you see an empty list (no rows inserted).
Aldwin Vlasblom
@Avaq
Dec 28 2015 09:46
How does it know what actions to take in order to roll back? I'm assuming that logic is taken care of by TVar? Transactional Variable?
Aldwin Vlasblom
@Avaq
Dec 28 2015 11:38
MPJ gave a shout-out to Ramda: https://youtu.be/TbLvrszqi_8?t=178 :)
Scott Sauyet
@CrossEye
Dec 28 2015 12:25
Nice! But somehow I can only watch a few minutes of that video at one sitting.
Hardy Jones
@joneshf
Dec 28 2015 16:37
@Avaq There's a simple implementation here: http://research.microsoft.com/en-us/um/people/simonpj/papers/stm/beautiful.pdf §3.3
@Avaq if that's what it actually uses, I couldn't tell you. I've not looked into it, nor been concerned to.
@Avaq but I guess if you're implementing it, you'd probably want to know :)
I guess with that implementation, "roll back" is a generous term.
It doesn't actually write any memory until it's all validated.
Hardy Jones
@joneshf
Dec 28 2015 16:42
so "roll back" just means, throw away the log.