clojure state management introduction
Before dive into those state management, let us first understand what are two key reference types are:
Coordinatedmeans different actors must work on the same workspace. Thus, they need to cooperate according to specific sequence in order to accomplish task.
Uncoordinatedin other hand, means different actors could work on their own workspace, without worrying about interfere others.
Synchronousoperations involves some levels of lock or latch. The actor that owns the lock will block subsequent requirement.
Asynchronousmeans different actors will no be blocked.
Ref are Clojure’s implementation of
coordinated identities. Each is a distinct identity, but operations on them must be run inside a transaction, guaranteeing that multiple identities whose values depend on each other are always in a consistent state.
Atom are Clojure’s implementation of
uncoordinated identities. When updated then change is applied before proceeding with the current thread and the update occurs atomically. All future dereferences to the atom from all threads will resolve to the new value.
One example of a case where atoms are very useful is for caching values. Cached values need to be accessible quickly, but are not dependent on the rest of the system’s state.
3. Asynchronous Agent
Like refs and atoms,
agent are identities and adhere to Clojure’s philosophy of identity and state. Unlike refs and atoms, however, updates to their values occur asynchronously in a separate system managed thread pool dedicated to managing agent state.
- Actions to any individual agent are applied serially, not concurrently. Multiple updates to the same agent won’t overwrite each other or encounter race conditions.
- Multiple actions sent to an agent from the same thread will be applied in the order in which they were sent. Obviously, no such guarantees can be made about actions sent from different threads.
- If an action function contains additional dispatches to agents, either to itself or other agents, dispatches are saved and are not actually called until after the action function returns and the agent’s value has been updated. This allows actions on an agent to trigger further actions without having the updates conflict.
- If an update is dispatched to an agent within a STM transaction (for example, a dosync expression), the dispatch is not sent until the transaction is committed. This means that it is safe to dispatch updates to atoms from within STM transactions.
Keeping Track of Identities
Both validator and watcher can do job on
validate variable data and throw exception while violated:
Supervise variable data in any point: