clojure state management introduction
Before dive into those state management, let us first understand what are two key reference types are:
Coordination
Coordinated
means different actors must work on the same workspace. Thus, they need to cooperate according to specific sequence in order to accomplish task.Uncoordinated
in other hand, means different actors could work on their own workspace, without worrying about interfere others.
Synchronization
Synchronous
operations involves some levels of lock or latch. The actor that owns the lock will block subsequent requirement.Asynchronous
means different actors will no be blocked.
1. ref
Ref
are Clojure’s implementation of synchronous
and 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.
1 |
|
2. atom
Atom
are Clojure’s implementation of synchronous
, 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.
1 |
|
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.
1 |
|
- 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 ref
agant
atom
Validators
validate variable data and throw exception while violated:
1 |
|
Watches
Supervise variable data in any point:
1 |
|