@@ -68,22 +68,22 @@ defmodule Agent do
6868 ## A word on distributed agents
6969
7070 It is important to consider the limitations of distributed agents. Agents
71- work by sending anonymous functions between the caller and the agent.
72- In a distributed setup with multiple nodes, agents only work if the caller
73- (client) and the agent have the same version of a given module.
74-
75- This setup may exhibit issues when doing "rolling upgrades". By rolling
76- upgrades we mean the following situation: you wish to deploy a new version of
77- your software by *shutting down* some of your nodes and replacing them with
78- nodes running a new version of the software. In this setup, part of your
79- environment will have one version of a given module and the other part
80- another version (the newer one) of the same module; this may cause agents to
81- crash. That said, if you plan to run in distributed environments, agents
82- should likely be avoided.
83-
84- Note, however, that agents work fine if you want to perform hot code
85- swapping, as it keeps both the old and new versions of a given module.
86- We detail how to do hot code swapping with agents in the next section .
71+ provides two APIs, one that works with anonymous functions and another
72+ that expects explicit module, function and arguments.
73+
74+ In a distributed setup with multiple nodes, the API that accepts anonymous
75+ functions only works if the caller (client) and the agent have the same
76+ version of the caller module.
77+
78+ Keep in mind this issue also shows up when performing "rolling upgrades"
79+ with agents. By rolling upgrades we mean the following situation: you wish
80+ to deploy a new version of your software by *shutting down* some of your
81+ nodes and replacing them with nodes running a new version of the software.
82+ In this setup, part of your environment will have one version of a given
83+ module and the other part another version (the newer one) of the same module.
84+
85+ The best solution is to simply use the explicit module, function and arguments
86+ APIs when working with distributed agents .
8787
8888 ## Hot code swapping
8989
@@ -111,7 +111,7 @@ defmodule Agent do
111111 @ type state :: term
112112
113113 @ doc """
114- Starts an agent linked to the current process.
114+ Starts an agent linked to the current process with the given function .
115115
116116 This is often used to start the agent as part of a supervision tree.
117117
@@ -149,6 +149,18 @@ defmodule Agent do
149149 GenServer . start_link ( Agent.Server , fun , options )
150150 end
151151
152+ @ doc """
153+ Starts an agent linked to the current process with the given module
154+ function and arguments.
155+
156+ Same as `start_link/2` but a module, function and args are expected
157+ instead of an anonymous function.
158+ """
159+ @ spec start_link ( module , atom , [ any ] , GenServer . options ) :: on_start
160+ def start_link ( module , fun , args , options \\ [ ] ) do
161+ GenServer . start_link ( Agent.Server , { module , fun , args } , options )
162+ end
163+
152164 @ doc """
153165 Starts an agent process without links (outside of a supervision tree).
154166
@@ -160,7 +172,18 @@ defmodule Agent do
160172 end
161173
162174 @ doc """
163- Gets the agent value and executes the given function.
175+ Starts an agent with the given module function and arguments.
176+
177+ Similar to `start/2` but a module, function and args are expected
178+ instead of an anonymous function.
179+ """
180+ @ spec start ( module , atom , [ any ] , GenServer . options ) :: on_start
181+ def start ( module , fun , args , options \\ [ ] ) do
182+ GenServer . start ( Agent.Server , { module , fun , args } , options )
183+ end
184+
185+ @ doc """
186+ Gets an agent value via the given function.
164187
165188 The function `fun` is sent to the `agent` which invokes the function
166189 passing the agent state. The result of the function invocation is
@@ -173,6 +196,18 @@ defmodule Agent do
173196 GenServer . call ( agent , { :get , fun } , timeout )
174197 end
175198
199+ @ doc """
200+ Gets an agent value via the given function.
201+
202+ Same as `get/3` but a module, function and args are expected
203+ instead of an anonymous function. The state is added as first
204+ argument to the given list of args.
205+ """
206+ @ spec get ( agent , module , atom , [ term ] , timeout ) :: any
207+ def get ( agent , module , fun , args , timeout \\ 5000 ) do
208+ GenServer . call ( agent , { :get , { module , fun , args } } , timeout )
209+ end
210+
176211 @ doc """
177212 Gets and updates the agent state in one operation.
178213
@@ -188,6 +223,18 @@ defmodule Agent do
188223 GenServer . call ( agent , { :get_and_update , fun } , timeout )
189224 end
190225
226+ @ doc """
227+ Gets and updates the agent state in one operation.
228+
229+ Same as `get_and_update/3` but a module, function and args are expected
230+ instead of an anonymous function. The state is added as first
231+ argument to the given list of args.
232+ """
233+ @ spec get_and_update ( agent , module , atom , [ term ] , timeout ) :: any
234+ def get_and_update ( agent , module , fun , args , timeout \\ 5000 ) do
235+ GenServer . call ( agent , { :get_and_update , { module , fun , args } } , timeout )
236+ end
237+
191238 @ doc """
192239 Updates the agent state.
193240
@@ -197,11 +244,23 @@ defmodule Agent do
197244 A timeout can also be specified (it has a default value of 5000).
198245 This function always returns `:ok`.
199246 """
200- @ spec update ( agent , ( state -> state ) ) :: :ok
247+ @ spec update ( agent , ( state -> state ) , timeout ) :: :ok
201248 def update ( agent , fun , timeout \\ 5000 ) when is_function ( fun , 1 ) do
202249 GenServer . call ( agent , { :update , fun } , timeout )
203250 end
204251
252+ @ doc """
253+ Updates the agent state.
254+
255+ Same as `update/3` but a module, function and args are expected
256+ instead of an anonymous function. The state is added as first
257+ argument to the given list of args.
258+ """
259+ @ spec update ( agent , module , atom , [ term ] , timeout ) :: :ok
260+ def update ( agent , module , fun , args , timeout \\ 5000 ) do
261+ GenServer . call ( agent , { :update , { module , fun , args } } , timeout )
262+ end
263+
205264 @ doc """
206265 Performs a cast (fire and forget) operation on the agent state.
207266
@@ -213,7 +272,19 @@ defmodule Agent do
213272 """
214273 @ spec cast ( agent , ( state -> state ) ) :: :ok
215274 def cast ( agent , fun ) when is_function ( fun , 1 ) do
216- GenServer . cast ( agent , fun )
275+ GenServer . cast ( agent , { :cast , fun } )
276+ end
277+
278+ @ doc """
279+ Performs a cast (fire and forget) operation on the agent state.
280+
281+ Same as `cast/2` but a module, function and args are expected
282+ instead of an anonymous function. The state is added as first
283+ argument to the given list of args.
284+ """
285+ @ spec cast ( agent , module , atom , [ term ] ) :: :ok
286+ def cast ( agent , module , fun , args ) do
287+ GenServer . cast ( agent , { :cast , { module , fun , args } } )
217288 end
218289
219290 @ doc """
0 commit comments