You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
`AsyncSeq.groupBy` partitions the input sequence into sub-sequences based on a key returned by a projection function.
57
+
The resulting sub-sequences emit elements when the source sequence emits an element corresponding to the key of the
58
+
sub-sequence. Elements of the resulting sequence are pairs of keys and sub-sequences, in this case `int * AsyncSeq<Event>`. Since by definition, these sub-sequences are independent, they can be processed in parallel. In fact, the sub-sequences *must* be processed in parallel, because it isn't possible to complete the processing of a sub-sequence until all elements of the source sequence are exhausted.
59
+
60
+
To continue improving the efficiency of our workflow, we can make use of batching. Specifically, we can read the incoming
61
+
events in batches and we can perform actions on entire batches of events.
62
+
63
+
*)
64
+
65
+
letbatchStream:AsyncSeq<Event[]>=
66
+
failwith "undefined"
67
+
68
+
letbatchAction(es:Event[]):Async<unit>=
69
+
failwith "undefined"
70
+
71
+
72
+
(**
73
+
74
+
Ordering is still important. For example, the batch action could write events into a full-text search index. We would like the full-text search index to be sequentially consistent. As such, the events need to be applied in the order they were emitted. The following workflow has the desired properties:
75
+
76
+
*)
77
+
78
+
batchStream
79
+
|> AsyncSeq.concatSeq // flatten the sequence of event arrays
80
+
|> AsyncSeq.groupBy (fun e -> int e.entityId %4)// partition into 4 groups
>> AsyncSeq.iterAsync batchAction)// perform the batch operation
84
+
|> AsyncSeq.iter ignore
85
+
86
+
87
+
(**
88
+
89
+
The above workflow:
90
+
91
+
1. Reads events in batches.
92
+
2. Flattens the batches.
93
+
3. Partitions the events into mutually exclusive sub-sequences.
94
+
4. Buffers elements of each sub-sequence by time and space.
95
+
5. Processes the sub-sequences in parallel, but individual sub-sequences sequentially.
96
+
97
+
*)
98
+
99
+
100
+
101
+
(**
102
+
103
+
### Merge
104
+
105
+
`AsyncSeq.merge` non-deterministically merges two async sequences into one. It is non-deterministic in the sense that the resulting sequence emits elements
106
+
whenever *either* input sequence emits a value. Since it isn't always known which will emit a value first, if at all, the operation is non-deterministic. This operation
107
+
is in contrast to `AsyncSeq.zip` which also takes two async sequences and returns a single async sequence, but as opposed to emitting an element when *either* input
108
+
sequence produces a value, it emits an element when *both* sequences emit a value.
109
+
110
+
Suppose we would like to trigger an operation whenever a change occurs. We can represent changes as an `AsyncSeq`. To gain intuition for this, consider the [Consul](https://www.consul.io/)
111
+
configuration management system. It stores configuration information in a tree-like structure. For this purpose of this discussion, it can be thought of as a key-value store
112
+
exposed via HTTP. In addition, `Consul` supports change notifications using HTTP long-polling - when an HTTP GET request is made to retrieve the value of a key,
113
+
if the request specified a modify-index, `Consul` won't respond to the request until a change has occurred *since* the modify-index. We can represent this operation using
114
+
the type `Key * ModifyIndex -> Async<Value * ModifyIndex>`. Next, we can take this operation and turn it into an `AsyncSeq` of changes as follows:
The function `changes` produces an async sequence which emits elements whenever the value corresponding to the key changes. Suppose also that we would like to trigger an operation
136
+
whenever the key changes or based on a fixed interval. We can represent a fixed interval as an async sequence as follows:
We can now consume this async sequence and use it to trigger downstream operations, such as updating the configuration of a running program, in flight.
0 commit comments