I’m working on a chatbot for Campfire that holds the current users list in an atom, (defonce users (atom {})).
I originally picked this reference type due to its simplicity, and it’s worked well up till now, but that might need to change.
-
Campfire sends
EnterMessageandLeaveMessageevents to the streaming api. My bot reacts to these by fetching the the current users list from the Campfire API, then callingreset!on theusersatom with the new list. -
Those same Enter/Leave events trigger random interactions, such as picking a random user from the
usersatom and asking it a question.
Problem
Number 2 above often asks the user who just left or never asks the user who just entered because the users atom hasn’t yet been !reset. I think I need to use a ref, but these docs say “Writers will never block commuters, or readers.” The thing is, I want that writer to block my reader, right?!
refs are primarily designed for coordinated access to several data structures if you only have one identity (the user list) then the main advantage of refs is not really an advantage to you, although it is not really a problem either. Refs have a cost as well in that transactions may be run more than once, so if your actions have side effects like sending messages then messages could be sent twice on transaction retries. You can get around this by using both a ref and an agent because messages sent to an agent from a transactions are guaranteed to send exactly once and only with the final committed values.in your case you could do well by:
(swap! users assoc name user)to incrementally build the user list.or
The quote "Writers will never block commuters, or readers." may not be directly relevant to you though it’s worth explaining a bit. In the case of a single ref, a thread that simply reads the value of the ref never waits, it gets the current value and continues. When there are more than one ref, it can read them both within a transaction and get a guarantee that it will either get a consistent set of values from both of them or will be rerun until it does get a consistent set of values. Threads that need to update the values will similarly need to rerun if they don’t get a consistent set of values, unless they are using the
commutefunction to update them in which case the STM will know that it is safe to commit the new value even is some other thread also did the same (or also commutable) operation to the value.