Using F# Agent For Concurrent Updates on Entities 
Wednesday, May 2, 2012 at 9:22PM
Tony Abell in Agent, F#

Using concurrent processing techniques there will be times multiple threads will update an Entity at the same time.  This update could be on the database level, in memory, or over the network; when there are multiple thread updating entity, bad things can happen.   The typical use case when multiple threads are updated a database record is to throw a Concurrency exception to the user.  Bad UX notwithstanding there are situations where this is not possible. 

Imagine a Use Case where a server ‘process’ is processing large sets of data, and concurrency techniques where used to improve performance.  In this scenario there are a few options when threads collide on an Entity; stop the process and go home, catch the exception and retry. 

At times I feel the first option of stopping the process and going home is the best…

The second option of catching the exception and retrying can introduce more complicated issues.  For example; if there were 5 threads updating an Entity, one thread will win and 4 will get the exception and be forced to retry.   In some rare cases there might be a scenario where two threads deadlock on trying to access the Entity. 

Another solution to this problem is to have all ‘work’ on an Entity be done synchronously.  This trick is for this solution is to have a Thread Safe Queue in which we can en-queue work for an Entity and have it processed in order. 

MailboxProcessor

MSDN Doc

The MailboxProcessor otherwise known as an 'Agent' is a nice abstraction over a queue. The Agent allows different threads to post items into an the queue and using F# Async Workflows each item is processed synchronously.

With ‘Agents' the construction of a solution is fairly straightforward.  In the code block below I have constructed a class which will accept an Id for an Entity and an ‘Action’.  For each Entity it will process each ‘Action’ in sequence. 

Below is a review of the code block.

Line 11

There is an ‘Agent’ controls access to the Dictionary of Agents.   This will insure that only one thread with be adding/reading from the dictionary at a time.

Line 19

So for each Entity there will be an Agent controlling the synchronous execution of ‘Action’.  We can do this for decent number of Entities as the Agents do not require a lot of resources.

F# Code Block 

 1: open System
 2: open System.Collections.Generic
 3: 
 4: type internal AggregateAction = 
 5:     {
 6:         Id: Guid
 7:         Action: Action
 8:     }
 9: 
10: type AggregateAgent() =
11:     let agent = MailboxProcessor<AggregateAction>.Start(fun inbox ->
12:         let dic = new Dictionary<Guid,MailboxProcessor<Action>>()
13:         async{                                                                                                  
14:                 while true do
15:                     let! msg = inbox.Receive()
16:                     if dic.ContainsKey(msg.Id) then 
17:                         dic.Item(msg.Id).Post msg.Action
18:                     else
19:                         let aggAgent = MailboxProcessor<Action>.Start( fun inbox ->
20:                             async{
21:                                 while true do
22:                                     let! action = inbox.Receive()
23:                                     action.Invoke() |> ignore     
24:                             })
25:                         aggAgent.Post msg.Action
26:                         dic.Add(msg.Id,aggAgent)
27:         })
28:         
29:     member this.Process id action = 
30:         agent.Post { Id = id; Action = action}
31: 
namespace System
namespace System.Collections
namespace System.Collections.Generic
type internal AggregateAction =
  {Id: Guid;
   Action: Action;}

Full name: Snippet.AggregateAction

  type: AggregateAction
  implements: IEquatable<AggregateAction>
  implements: Collections.IStructuralEquatable
AggregateAction.Id: Guid
type Guid =
  struct
    new : System.Byte [] -> System.Guid
    new : uint32 * uint16 * uint16 * System.Byte * System.Byte * System.Byte * System.Byte * System.Byte * System.Byte * System.Byte * System.Byte -> System.Guid
    new : int * int16 * int16 * System.Byte [] -> System.Guid
    new : int * int16 * int16 * System.Byte * System.Byte * System.Byte * System.Byte * System.Byte * System.Byte * System.Byte * System.Byte -> System.Guid
    new : string -> System.Guid
    member CompareTo : obj -> int
    member CompareTo : System.Guid -> int
    member Equals : obj -> bool
    member Equals : System.Guid -> bool
    member GetHashCode : unit -> int
    member ToByteArray : unit -> System.Byte []
    member ToString : unit -> string
    member ToString : string -> string
    member ToString : string * System.IFormatProvider -> string
    static val Empty : System.Guid
    static member NewGuid : unit -> System.Guid
    static member Parse : string -> System.Guid
    static member ParseExact : string * string -> System.Guid
    static member TryParse : string * System.Guid -> bool
    static member TryParseExact : string * string * System.Guid -> bool
  end

Full name: System.Guid

  type: Guid
  implements: IFormattable
  implements: IComparable
  implements: IComparable<Guid>
  implements: IEquatable<Guid>
  inherits: ValueType
Multiple items
AggregateAction.Action: Action

--------------------

type Action<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'T9,'T10,'T11,'T12,'T13,'T14,'T15,'T16> =
  delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 * 'T7 * 'T8 * 'T9 * 'T10 * 'T11 * 'T12 * 'T13 * 'T14 * 'T15 * 'T16 -> unit

Full name: System.Action<_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_>

  type: Action<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'T9,'T10,'T11,'T12,'T13,'T14,'T15,'T16>
  implements: ICloneable
  implements: Runtime.Serialization.ISerializable
  inherits: MulticastDelegate
  inherits: Delegate


--------------------

type Action<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'T9,'T10,'T11,'T12,'T13,'T14,'T15> =
  delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 * 'T7 * 'T8 * 'T9 * 'T10 * 'T11 * 'T12 * 'T13 * 'T14 * 'T15 -> unit

Full name: System.Action<_,_,_,_,_,_,_,_,_,_,_,_,_,_,_>

  type: Action<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'T9,'T10,'T11,'T12,'T13,'T14,'T15>
  implements: ICloneable
  implements: Runtime.Serialization.ISerializable
  inherits: MulticastDelegate
  inherits: Delegate


--------------------

type Action<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'T9,'T10,'T11,'T12,'T13,'T14> =
  delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 * 'T7 * 'T8 * 'T9 * 'T10 * 'T11 * 'T12 * 'T13 * 'T14 -> unit

Full name: System.Action<_,_,_,_,_,_,_,_,_,_,_,_,_,_>

  type: Action<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'T9,'T10,'T11,'T12,'T13,'T14>
  implements: ICloneable
  implements: Runtime.Serialization.ISerializable
  inherits: MulticastDelegate
  inherits: Delegate


--------------------

type Action<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'T9,'T10,'T11,'T12,'T13> =
  delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 * 'T7 * 'T8 * 'T9 * 'T10 * 'T11 * 'T12 * 'T13 -> unit

Full name: System.Action<_,_,_,_,_,_,_,_,_,_,_,_,_>

  type: Action<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'T9,'T10,'T11,'T12,'T13>
  implements: ICloneable
  implements: Runtime.Serialization.ISerializable
  inherits: MulticastDelegate
  inherits: Delegate


--------------------

type Action<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'T9,'T10,'T11,'T12> =
  delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 * 'T7 * 'T8 * 'T9 * 'T10 * 'T11 * 'T12 -> unit

Full name: System.Action<_,_,_,_,_,_,_,_,_,_,_,_>

  type: Action<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'T9,'T10,'T11,'T12>
  implements: ICloneable
  implements: Runtime.Serialization.ISerializable
  inherits: MulticastDelegate
  inherits: Delegate


--------------------

type Action<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'T9,'T10,'T11> =
  delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 * 'T7 * 'T8 * 'T9 * 'T10 * 'T11 -> unit

Full name: System.Action<_,_,_,_,_,_,_,_,_,_,_>

  type: Action<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'T9,'T10,'T11>
  implements: ICloneable
  implements: Runtime.Serialization.ISerializable
  inherits: MulticastDelegate
  inherits: Delegate


--------------------

type Action<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'T9,'T10> =
  delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 * 'T7 * 'T8 * 'T9 * 'T10 -> unit

Full name: System.Action<_,_,_,_,_,_,_,_,_,_>

  type: Action<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'T9,'T10>
  implements: ICloneable
  implements: Runtime.Serialization.ISerializable
  inherits: MulticastDelegate
  inherits: Delegate


--------------------

type Action<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'T9> =
  delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 * 'T7 * 'T8 * 'T9 -> unit

Full name: System.Action<_,_,_,_,_,_,_,_,_>

  type: Action<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'T9>
  implements: ICloneable
  implements: Runtime.Serialization.ISerializable
  inherits: MulticastDelegate
  inherits: Delegate


--------------------

type Action<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8> =
  delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 * 'T7 * 'T8 -> unit

Full name: System.Action<_,_,_,_,_,_,_,_>

  type: Action<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8>
  implements: ICloneable
  implements: Runtime.Serialization.ISerializable
  inherits: MulticastDelegate
  inherits: Delegate


--------------------

type Action<'T1,'T2,'T3,'T4,'T5,'T6,'T7> =
  delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 * 'T7 -> unit

Full name: System.Action<_,_,_,_,_,_,_>

  type: Action<'T1,'T2,'T3,'T4,'T5,'T6,'T7>
  implements: ICloneable
  implements: Runtime.Serialization.ISerializable
  inherits: MulticastDelegate
  inherits: Delegate


--------------------

type Action<'T1,'T2,'T3,'T4,'T5,'T6> =
  delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 -> unit

Full name: System.Action<_,_,_,_,_,_>

  type: Action<'T1,'T2,'T3,'T4,'T5,'T6>
  implements: ICloneable
  implements: Runtime.Serialization.ISerializable
  inherits: MulticastDelegate
  inherits: Delegate


--------------------

type Action<'T1,'T2,'T3,'T4,'T5> =
  delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 -> unit

Full name: System.Action<_,_,_,_,_>

  type: Action<'T1,'T2,'T3,'T4,'T5>
  implements: ICloneable
  implements: Runtime.Serialization.ISerializable
  inherits: MulticastDelegate
  inherits: Delegate


--------------------

type Action<'T1,'T2,'T3,'T4> =
  delegate of 'T1 * 'T2 * 'T3 * 'T4 -> unit

Full name: System.Action<_,_,_,_>

  type: Action<'T1,'T2,'T3,'T4>
  implements: ICloneable
  implements: Runtime.Serialization.ISerializable
  inherits: MulticastDelegate
  inherits: Delegate


--------------------

type Action<'T1,'T2,'T3> =
  delegate of 'T1 * 'T2 * 'T3 -> unit

Full name: System.Action<_,_,_>

  type: Action<'T1,'T2,'T3>
  implements: ICloneable
  implements: Runtime.Serialization.ISerializable
  inherits: MulticastDelegate
  inherits: Delegate


--------------------

type Action<'T1,'T2> =
  delegate of 'T1 * 'T2 -> unit

Full name: System.Action<_,_>

  type: Action<'T1,'T2>
  implements: ICloneable
  implements: Runtime.Serialization.ISerializable
  inherits: MulticastDelegate
  inherits: Delegate


--------------------

type Action<'T> =
  delegate of 'T -> unit

Full name: System.Action<_>

  type: Action<'T>
  implements: ICloneable
  implements: Runtime.Serialization.ISerializable
  inherits: MulticastDelegate
  inherits: Delegate


--------------------

type Action =
  delegate of unit -> unit

Full name: System.Action

  type: Action
  implements: ICloneable
  implements: Runtime.Serialization.ISerializable
  inherits: MulticastDelegate
  inherits: Delegate


--------------------

Action('T1 -> 'T2 -> 'T3 -> 'T4 -> 'T5 -> 'T6 -> 'T7 -> 'T8 -> 'T9 -> 'T10 -> 'T11 -> 'T12 -> 'T13 -> 'T14 -> 'T15 -> 'T16 -> unit)

--------------------

Action('T1 -> 'T2 -> 'T3 -> 'T4 -> 'T5 -> 'T6 -> 'T7 -> 'T8 -> 'T9 -> 'T10 -> 'T11 -> 'T12 -> 'T13 -> 'T14 -> 'T15 -> unit)

--------------------

Action('T1 -> 'T2 -> 'T3 -> 'T4 -> 'T5 -> 'T6 -> 'T7 -> 'T8 -> 'T9 -> 'T10 -> 'T11 -> 'T12 -> 'T13 -> 'T14 -> unit)

--------------------

Action('T1 -> 'T2 -> 'T3 -> 'T4 -> 'T5 -> 'T6 -> 'T7 -> 'T8 -> 'T9 -> 'T10 -> 'T11 -> 'T12 -> 'T13 -> unit)

--------------------

Action('T1 -> 'T2 -> 'T3 -> 'T4 -> 'T5 -> 'T6 -> 'T7 -> 'T8 -> 'T9 -> 'T10 -> 'T11 -> 'T12 -> unit)

--------------------

Action('T1 -> 'T2 -> 'T3 -> 'T4 -> 'T5 -> 'T6 -> 'T7 -> 'T8 -> 'T9 -> 'T10 -> 'T11 -> unit)

--------------------

Action('T1 -> 'T2 -> 'T3 -> 'T4 -> 'T5 -> 'T6 -> 'T7 -> 'T8 -> 'T9 -> 'T10 -> unit)

--------------------

Action('T1 -> 'T2 -> 'T3 -> 'T4 -> 'T5 -> 'T6 -> 'T7 -> 'T8 -> 'T9 -> unit)

--------------------

Action('T1 -> 'T2 -> 'T3 -> 'T4 -> 'T5 -> 'T6 -> 'T7 -> 'T8 -> unit)

--------------------

Action('T1 -> 'T2 -> 'T3 -> 'T4 -> 'T5 -> 'T6 -> 'T7 -> unit)

--------------------

Action('T1 -> 'T2 -> 'T3 -> 'T4 -> 'T5 -> 'T6 -> unit)

--------------------

Action('T1 -> 'T2 -> 'T3 -> 'T4 -> 'T5 -> unit)

--------------------

Action('T1 -> 'T2 -> 'T3 -> 'T4 -> unit)

--------------------

Action('T1 -> 'T2 -> 'T3 -> unit)

--------------------

Action('T1 -> 'T2 -> unit)

--------------------

Action('T -> unit)

--------------------

Action(unit -> unit)
type AggregateAgent =
  class
    new : unit -> AggregateAgent
    member Process : id:Guid -> action:Action -> unit
  end

Full name: Snippet.AggregateAgent
val agent : MailboxProcessor<AggregateAction>

  type: MailboxProcessor<AggregateAction>
  implements: IDisposable
type MailboxProcessor<'Msg> =
  class
    interface IDisposable
    new : body:(MailboxProcessor<'Msg> -> Async<unit>) * ?cancellationToken:Threading.CancellationToken -> MailboxProcessor<'Msg>
    member Post : message:'Msg -> unit
    member PostAndAsyncReply : buildMessage:(AsyncReplyChannel<'Reply> -> 'Msg) * ?timeout:int -> Async<'Reply>
    member PostAndReply : buildMessage:(AsyncReplyChannel<'Reply> -> 'Msg) * ?timeout:int -> 'Reply
    member PostAndTryAsyncReply : buildMessage:(AsyncReplyChannel<'Reply> -> 'Msg) * ?timeout:int -> Async<'Reply option>
    member Receive : ?timeout:int -> Async<'Msg>
    member Scan : scanner:('Msg -> Async<'T> option) * ?timeout:int -> Async<'T>
    member Start : unit -> unit
    member TryPostAndReply : buildMessage:(AsyncReplyChannel<'Reply> -> 'Msg) * ?timeout:int -> 'Reply option
    member TryReceive : ?timeout:int -> Async<'Msg option>
    member TryScan : scanner:('Msg -> Async<'T> option) * ?timeout:int -> Async<'T option>
    member add_Error : Handler<Exception> -> unit
    member CurrentQueueLength : int
    member DefaultTimeout : int
    member Error : IEvent<Exception>
    member remove_Error : Handler<Exception> -> unit
    member DefaultTimeout : int with set
    static member Start : body:(MailboxProcessor<'Msg> -> Async<unit>) * ?cancellationToken:Threading.CancellationToken -> MailboxProcessor<'Msg>
  end

Full name: Microsoft.FSharp.Control.MailboxProcessor<_>

  type: MailboxProcessor<'Msg>
  implements: IDisposable
val inbox : MailboxProcessor<AggregateAction>

  type: MailboxProcessor<AggregateAction>
  implements: IDisposable
val dic : Dictionary<Guid,MailboxProcessor<Action>>

  type: Dictionary<Guid,MailboxProcessor<Action>>
  implements: IDictionary<Guid,MailboxProcessor<Action>>
  implements: ICollection<KeyValuePair<Guid,MailboxProcessor<Action>>>
  implements: seq<KeyValuePair<Guid,MailboxProcessor<Action>>>
  implements: Collections.IDictionary
  implements: Collections.ICollection
  implements: Collections.IEnumerable
  implements: Runtime.Serialization.ISerializable
  implements: Runtime.Serialization.IDeserializationCallback
type Dictionary<'TKey,'TValue> =
  class
    new : unit -> System.Collections.Generic.Dictionary<'TKey,'TValue>
    new : int -> System.Collections.Generic.Dictionary<'TKey,'TValue>
    new : System.Collections.Generic.IEqualityComparer<'TKey> -> System.Collections.Generic.Dictionary<'TKey,'TValue>
    new : int * System.Collections.Generic.IEqualityComparer<'TKey> -> System.Collections.Generic.Dictionary<'TKey,'TValue>
    new : System.Collections.Generic.IDictionary<'TKey,'TValue> -> System.Collections.Generic.Dictionary<'TKey,'TValue>
    new : System.Collections.Generic.IDictionary<'TKey,'TValue> * System.Collections.Generic.IEqualityComparer<'TKey> -> System.Collections.Generic.Dictionary<'TKey,'TValue>
    member Add : 'TKey * 'TValue -> unit
    member Clear : unit -> unit
    member Comparer : System.Collections.Generic.IEqualityComparer<'TKey>
    member ContainsKey : 'TKey -> bool
    member ContainsValue : 'TValue -> bool
    member Count : int
    member GetEnumerator : unit -> Enumerator<'TKey,'TValue>
    member GetObjectData : System.Runtime.Serialization.SerializationInfo * System.Runtime.Serialization.StreamingContext -> unit
    member Item : 'TKey -> 'TValue with get, set
    member Keys : KeyCollection<'TKey,'TValue>
    member OnDeserialization : obj -> unit
    member Remove : 'TKey -> bool
    member TryGetValue : 'TKey * 'TValue -> bool
    member Values : ValueCollection<'TKey,'TValue>
    type Enumerator =
      struct
        member Current : System.Collections.Generic.KeyValuePair<'TKey,'TValue>
        member Dispose : unit -> unit
        member MoveNext : unit -> bool
      end
    type KeyCollection =
      class
        new : System.Collections.Generic.Dictionary<'TKey,'TValue> -> KeyCollection
        member CopyTo : 'TKey [] * int -> unit
        member Count : int
        member GetEnumerator : unit -> Enumerator<'TKey,'TValue>
        type Enumerator =
          struct
            member Current : 'TKey
            member Dispose : unit -> unit
            member MoveNext : unit -> bool
          end
      end
    type ValueCollection =
      class
        new : System.Collections.Generic.Dictionary<'TKey,'TValue> -> ValueCollection
        member CopyTo : 'TValue [] * int -> unit
        member Count : int
        member GetEnumerator : unit -> Enumerator<'TKey,'TValue>
        type Enumerator =
          struct
            member Current : 'TValue
            member Dispose : unit -> unit
            member MoveNext : unit -> bool
          end
      end
  end

Full name: System.Collections.Generic.Dictionary<_,_>

  type: Dictionary<'TKey,'TValue>
  implements: IDictionary<'TKey,'TValue>
  implements: ICollection<KeyValuePair<'TKey,'TValue>>
  implements: seq<KeyValuePair<'TKey,'TValue>>
  implements: Collections.IDictionary
  implements: Collections.ICollection
  implements: Collections.IEnumerable
  implements: Runtime.Serialization.ISerializable
  implements: Runtime.Serialization.IDeserializationCallback
Multiple items
type Action<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'T9,'T10,'T11,'T12,'T13,'T14,'T15,'T16> =
  delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 * 'T7 * 'T8 * 'T9 * 'T10 * 'T11 * 'T12 * 'T13 * 'T14 * 'T15 * 'T16 -> unit

Full name: System.Action<_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_>

  type: Action<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'T9,'T10,'T11,'T12,'T13,'T14,'T15,'T16>
  implements: ICloneable
  implements: Runtime.Serialization.ISerializable
  inherits: MulticastDelegate
  inherits: Delegate


--------------------

type Action<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'T9,'T10,'T11,'T12,'T13,'T14,'T15> =
  delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 * 'T7 * 'T8 * 'T9 * 'T10 * 'T11 * 'T12 * 'T13 * 'T14 * 'T15 -> unit

Full name: System.Action<_,_,_,_,_,_,_,_,_,_,_,_,_,_,_>

  type: Action<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'T9,'T10,'T11,'T12,'T13,'T14,'T15>
  implements: ICloneable
  implements: Runtime.Serialization.ISerializable
  inherits: MulticastDelegate
  inherits: Delegate


--------------------

type Action<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'T9,'T10,'T11,'T12,'T13,'T14> =
  delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 * 'T7 * 'T8 * 'T9 * 'T10 * 'T11 * 'T12 * 'T13 * 'T14 -> unit

Full name: System.Action<_,_,_,_,_,_,_,_,_,_,_,_,_,_>

  type: Action<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'T9,'T10,'T11,'T12,'T13,'T14>
  implements: ICloneable
  implements: Runtime.Serialization.ISerializable
  inherits: MulticastDelegate
  inherits: Delegate


--------------------

type Action<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'T9,'T10,'T11,'T12,'T13> =
  delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 * 'T7 * 'T8 * 'T9 * 'T10 * 'T11 * 'T12 * 'T13 -> unit

Full name: System.Action<_,_,_,_,_,_,_,_,_,_,_,_,_>

  type: Action<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'T9,'T10,'T11,'T12,'T13>
  implements: ICloneable
  implements: Runtime.Serialization.ISerializable
  inherits: MulticastDelegate
  inherits: Delegate


--------------------

type Action<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'T9,'T10,'T11,'T12> =
  delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 * 'T7 * 'T8 * 'T9 * 'T10 * 'T11 * 'T12 -> unit

Full name: System.Action<_,_,_,_,_,_,_,_,_,_,_,_>

  type: Action<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'T9,'T10,'T11,'T12>
  implements: ICloneable
  implements: Runtime.Serialization.ISerializable
  inherits: MulticastDelegate
  inherits: Delegate


--------------------

type Action<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'T9,'T10,'T11> =
  delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 * 'T7 * 'T8 * 'T9 * 'T10 * 'T11 -> unit

Full name: System.Action<_,_,_,_,_,_,_,_,_,_,_>

  type: Action<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'T9,'T10,'T11>
  implements: ICloneable
  implements: Runtime.Serialization.ISerializable
  inherits: MulticastDelegate
  inherits: Delegate


--------------------

type Action<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'T9,'T10> =
  delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 * 'T7 * 'T8 * 'T9 * 'T10 -> unit

Full name: System.Action<_,_,_,_,_,_,_,_,_,_>

  type: Action<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'T9,'T10>
  implements: ICloneable
  implements: Runtime.Serialization.ISerializable
  inherits: MulticastDelegate
  inherits: Delegate


--------------------

type Action<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'T9> =
  delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 * 'T7 * 'T8 * 'T9 -> unit

Full name: System.Action<_,_,_,_,_,_,_,_,_>

  type: Action<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'T9>
  implements: ICloneable
  implements: Runtime.Serialization.ISerializable
  inherits: MulticastDelegate
  inherits: Delegate


--------------------

type Action<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8> =
  delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 * 'T7 * 'T8 -> unit

Full name: System.Action<_,_,_,_,_,_,_,_>

  type: Action<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8>
  implements: ICloneable
  implements: Runtime.Serialization.ISerializable
  inherits: MulticastDelegate
  inherits: Delegate


--------------------

type Action<'T1,'T2,'T3,'T4,'T5,'T6,'T7> =
  delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 * 'T7 -> unit

Full name: System.Action<_,_,_,_,_,_,_>

  type: Action<'T1,'T2,'T3,'T4,'T5,'T6,'T7>
  implements: ICloneable
  implements: Runtime.Serialization.ISerializable
  inherits: MulticastDelegate
  inherits: Delegate


--------------------

type Action<'T1,'T2,'T3,'T4,'T5,'T6> =
  delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 -> unit

Full name: System.Action<_,_,_,_,_,_>

  type: Action<'T1,'T2,'T3,'T4,'T5,'T6>
  implements: ICloneable
  implements: Runtime.Serialization.ISerializable
  inherits: MulticastDelegate
  inherits: Delegate


--------------------

type Action<'T1,'T2,'T3,'T4,'T5> =
  delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 -> unit

Full name: System.Action<_,_,_,_,_>

  type: Action<'T1,'T2,'T3,'T4,'T5>
  implements: ICloneable
  implements: Runtime.Serialization.ISerializable
  inherits: MulticastDelegate
  inherits: Delegate


--------------------

type Action<'T1,'T2,'T3,'T4> =
  delegate of 'T1 * 'T2 * 'T3 * 'T4 -> unit

Full name: System.Action<_,_,_,_>

  type: Action<'T1,'T2,'T3,'T4>
  implements: ICloneable
  implements: Runtime.Serialization.ISerializable
  inherits: MulticastDelegate
  inherits: Delegate


--------------------

type Action<'T1,'T2,'T3> =
  delegate of 'T1 * 'T2 * 'T3 -> unit

Full name: System.Action<_,_,_>

  type: Action<'T1,'T2,'T3>
  implements: ICloneable
  implements: Runtime.Serialization.ISerializable
  inherits: MulticastDelegate
  inherits: Delegate


--------------------

type Action<'T1,'T2> =
  delegate of 'T1 * 'T2 -> unit

Full name: System.Action<_,_>

  type: Action<'T1,'T2>
  implements: ICloneable
  implements: Runtime.Serialization.ISerializable
  inherits: MulticastDelegate
  inherits: Delegate


--------------------

type Action<'T> =
  delegate of 'T -> unit

Full name: System.Action<_>

  type: Action<'T>
  implements: ICloneable
  implements: Runtime.Serialization.ISerializable
  inherits: MulticastDelegate
  inherits: Delegate


--------------------

type Action =
  delegate of unit -> unit

Full name: System.Action

  type: Action
  implements: ICloneable
  implements: Runtime.Serialization.ISerializable
  inherits: MulticastDelegate
  inherits: Delegate


--------------------

Action('T1 -> 'T2 -> 'T3 -> 'T4 -> 'T5 -> 'T6 -> 'T7 -> 'T8 -> 'T9 -> 'T10 -> 'T11 -> 'T12 -> 'T13 -> 'T14 -> 'T15 -> 'T16 -> unit)

--------------------

Action('T1 -> 'T2 -> 'T3 -> 'T4 -> 'T5 -> 'T6 -> 'T7 -> 'T8 -> 'T9 -> 'T10 -> 'T11 -> 'T12 -> 'T13 -> 'T14 -> 'T15 -> unit)

--------------------

Action('T1 -> 'T2 -> 'T3 -> 'T4 -> 'T5 -> 'T6 -> 'T7 -> 'T8 -> 'T9 -> 'T10 -> 'T11 -> 'T12 -> 'T13 -> 'T14 -> unit)

--------------------

Action('T1 -> 'T2 -> 'T3 -> 'T4 -> 'T5 -> 'T6 -> 'T7 -> 'T8 -> 'T9 -> 'T10 -> 'T11 -> 'T12 -> 'T13 -> unit)

--------------------

Action('T1 -> 'T2 -> 'T3 -> 'T4 -> 'T5 -> 'T6 -> 'T7 -> 'T8 -> 'T9 -> 'T10 -> 'T11 -> 'T12 -> unit)

--------------------

Action('T1 -> 'T2 -> 'T3 -> 'T4 -> 'T5 -> 'T6 -> 'T7 -> 'T8 -> 'T9 -> 'T10 -> 'T11 -> unit)

--------------------

Action('T1 -> 'T2 -> 'T3 -> 'T4 -> 'T5 -> 'T6 -> 'T7 -> 'T8 -> 'T9 -> 'T10 -> unit)

--------------------

Action('T1 -> 'T2 -> 'T3 -> 'T4 -> 'T5 -> 'T6 -> 'T7 -> 'T8 -> 'T9 -> unit)

--------------------

Action('T1 -> 'T2 -> 'T3 -> 'T4 -> 'T5 -> 'T6 -> 'T7 -> 'T8 -> unit)

--------------------

Action('T1 -> 'T2 -> 'T3 -> 'T4 -> 'T5 -> 'T6 -> 'T7 -> unit)

--------------------

Action('T1 -> 'T2 -> 'T3 -> 'T4 -> 'T5 -> 'T6 -> unit)

--------------------

Action('T1 -> 'T2 -> 'T3 -> 'T4 -> 'T5 -> unit)

--------------------

Action('T1 -> 'T2 -> 'T3 -> 'T4 -> unit)

--------------------

Action('T1 -> 'T2 -> 'T3 -> unit)

--------------------

Action('T1 -> 'T2 -> unit)

--------------------

Action('T -> unit)

--------------------

Action(unit -> unit)
val async : AsyncBuilder

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.async
val msg : AggregateAction

  type: AggregateAction
  implements: IEquatable<AggregateAction>
  implements: Collections.IStructuralEquatable
member MailboxProcessor.Receive : ?timeout:int -> Async<'Msg>
Dictionary.ContainsKey(key: Guid) : bool
property Dictionary.Item: Guid -> MailboxProcessor<Action>
val aggAgent : MailboxProcessor<Action>

  type: MailboxProcessor<Action>
  implements: IDisposable
val inbox : MailboxProcessor<Action>

  type: MailboxProcessor<Action>
  implements: IDisposable
val action : Action

  type: Action
  implements: ICloneable
  implements: Runtime.Serialization.ISerializable
  inherits: MulticastDelegate
  inherits: Delegate
val ignore : 'T -> unit

Full name: Microsoft.FSharp.Core.Operators.ignore
member MailboxProcessor.Post : message:'Msg -> unit
AggregateAction.Action: Action
Dictionary.Add(key: Guid, value: MailboxProcessor<Action>) : unit
val this : AggregateAgent
member AggregateAgent.Process : id:Guid -> action:Action -> unit

Full name: Snippet.AggregateAgent.Process
val id : Guid

  type: Guid
  implements: IFormattable
  implements: IComparable
  implements: IComparable<Guid>
  implements: IEquatable<Guid>
  inherits: ValueType

 

One thing to note, each ‘Action’ type being processed should handle any errors it encounters as the Aggregate Agent will not know how to proceed. 

Article originally appeared on Tony Abell Blog (http://www.tonyabell.com/).
See website for complete article licensing information.