@@ -68,22 +68,22 @@ defmodule Agent do
68
68
## A word on distributed agents
69
69
70
70
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 .
87
87
88
88
## Hot code swapping
89
89
@@ -111,7 +111,7 @@ defmodule Agent do
111
111
@ type state :: term
112
112
113
113
@ doc """
114
- Starts an agent linked to the current process.
114
+ Starts an agent linked to the current process with the given function .
115
115
116
116
This is often used to start the agent as part of a supervision tree.
117
117
@@ -149,6 +149,18 @@ defmodule Agent do
149
149
GenServer . start_link ( Agent.Server , fun , options )
150
150
end
151
151
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
+
152
164
@ doc """
153
165
Starts an agent process without links (outside of a supervision tree).
154
166
@@ -160,7 +172,18 @@ defmodule Agent do
160
172
end
161
173
162
174
@ 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.
164
187
165
188
The function `fun` is sent to the `agent` which invokes the function
166
189
passing the agent state. The result of the function invocation is
@@ -173,6 +196,18 @@ defmodule Agent do
173
196
GenServer . call ( agent , { :get , fun } , timeout )
174
197
end
175
198
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
+
176
211
@ doc """
177
212
Gets and updates the agent state in one operation.
178
213
@@ -188,6 +223,18 @@ defmodule Agent do
188
223
GenServer . call ( agent , { :get_and_update , fun } , timeout )
189
224
end
190
225
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
+
191
238
@ doc """
192
239
Updates the agent state.
193
240
@@ -197,11 +244,23 @@ defmodule Agent do
197
244
A timeout can also be specified (it has a default value of 5000).
198
245
This function always returns `:ok`.
199
246
"""
200
- @ spec update ( agent , ( state -> state ) ) :: :ok
247
+ @ spec update ( agent , ( state -> state ) , timeout ) :: :ok
201
248
def update ( agent , fun , timeout \\ 5000 ) when is_function ( fun , 1 ) do
202
249
GenServer . call ( agent , { :update , fun } , timeout )
203
250
end
204
251
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
+
205
264
@ doc """
206
265
Performs a cast (fire and forget) operation on the agent state.
207
266
@@ -213,7 +272,19 @@ defmodule Agent do
213
272
"""
214
273
@ spec cast ( agent , ( state -> state ) ) :: :ok
215
274
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 } } )
217
288
end
218
289
219
290
@ doc """
0 commit comments