Question

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.

  1. Campfire sends EnterMessage and LeaveMessage events to the streaming api. My bot reacts to these by fetching the the current users list from the Campfire API, then calling reset! on the users atom with the new list.

  2. Those same Enter/Leave events trigger random interactions, such as picking a random user from the users atom 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?!

Was it helpful?

Solution

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:

  • continuing to use an atom
  • use somethign like (swap! users assoc name user) to incrementally build the user list.
  • use a watch function on the atom to handle the state changes, because watches will catch every state change.

or

  • switch to a ref
  • use an agent to sent the actual messages to users.
  • using a watch could make this simpler but is not required.

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 commute function 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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top