Skip to content

Commit 255db1f

Browse files
committed
Refactored AsyncVal to use PooledResizeArray under the hood
1 parent 44040fa commit 255db1f

File tree

6 files changed

+75
-27
lines changed

6 files changed

+75
-27
lines changed

src/FSharp.Data.GraphQL.Server.AspNetCore/GraphQLWebsocketMiddleware.fs

+1-1
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ type GraphQLWebSocketMiddleware<'Root>
8282
let receiveMessageViaSocket (cancellationToken : CancellationToken) (serializerOptions : JsonSerializerOptions) (socket : WebSocket) = taskResult {
8383
let buffer = ArrayPool.Shared.Rent options.ReadBufferSize
8484
try
85-
let completeMessage = new PooledList<byte> ()
85+
use completeMessage = new PooledResizeArray<byte> ()
8686
let mutable segmentResponse : WebSocketReceiveResult = null
8787
while (not cancellationToken.IsCancellationRequested)
8888
&& socket |> isSocketOpen

src/FSharp.Data.GraphQL.Server/Execution.fs

+2-4
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ let deferResults path (res : ResolverResult<obj>) : IObservable<GQLDeferredRespo
300300
| Error errs -> Observable.singleton <| DeferredErrors (null, errs, formattedPath)
301301

302302
/// Collect together an array of results using the appropriate execution strategy.
303-
let collectFields (strategy : ExecutionStrategy) (rs : AsyncVal<ResolverResult<KeyValuePair<string, obj>>> []) : AsyncVal<ResolverResult<KeyValuePair<string, obj> []>> = asyncVal {
303+
let collectFields (strategy : ExecutionStrategy) (rs : AsyncVal<ResolverResult<KeyValuePair<string, obj>>> seq) : AsyncVal<ResolverResult<KeyValuePair<string, obj> []>> = asyncVal {
304304
let! collected =
305305
match strategy with
306306
| Parallel -> AsyncVal.collectParallel rs
@@ -354,8 +354,7 @@ let rec private direct (returnDef : OutputDef) (ctx : ResolveFieldContext) (path
354354
| :? System.Collections.IEnumerable as enumerable ->
355355
enumerable
356356
|> Seq.cast<obj>
357-
|> Seq.toArray
358-
|> Array.mapi resolveItem
357+
|> Seq.mapi resolveItem
359358
|> collectFields Parallel
360359
|> AsyncVal.map(ResolverResult.mapValue(fun items -> KeyValuePair(name, items |> Array.map(fun d -> d.Value) |> box)))
361360
| _ -> raise <| GQLMessageException (ErrorMessages.expectedEnumerableValue ctx.ExecutionInfo.Identifier (value.GetType()))
@@ -543,7 +542,6 @@ and executeObjectFields (fields : ExecutionInfo list) (objName : string) (objDef
543542
let! res =
544543
fields
545544
|> Seq.map executeField
546-
|> Seq.toArray
547545
|> collectFields Parallel
548546
match res with
549547
| Error errs -> return Error errs

src/FSharp.Data.GraphQL.Server/FSharp.Data.GraphQL.Server.fsproj

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
</ItemGroup>
2424

2525
<ItemGroup>
26+
<PackageReference Include="Collections.Pooled" />
2627
<PackageReference Include="FSharp.Control.Reactive" />
2728
<PackageReference Include="System.Reactive" />
2829
</ItemGroup>

src/FSharp.Data.GraphQL.Shared/AsyncVal.fs

+30-22
Original file line numberDiff line numberDiff line change
@@ -130,49 +130,55 @@ module AsyncVal =
130130
/// executed asynchronously, one by one with regard to their order in array.
131131
/// Returned array maintain order of values.
132132
/// If the array contains a Failure, then the entire array will not resolve
133-
let collectSequential (values : AsyncVal<'T>[]) : AsyncVal<'T[]> =
134-
if values.Length = 0 then Value [||]
135-
elif values |> Array.exists isAsync then
133+
let collectSequential (values : AsyncVal<'T> seq) : AsyncVal<'T[]> =
134+
let values = new PooledResizeArray<_> (values)
135+
let length = values.Count
136+
if length = 0 then Value [||]
137+
elif values.Exists isAsync then
136138
Async (async {
137-
let results = Array.zeroCreate values.Length
138-
let exceptions = ResizeArray values.Length
139-
for i = 0 to values.Length - 1 do
139+
let results = Array.zeroCreate length
140+
use exceptions = new PooledResizeArray<_> (length)
141+
for i = 0 to length - 1 do
140142
let v = values.[i]
141143
match v with
142144
| Value v -> results.[i] <- v
143145
| Async a ->
144146
let! r = a
145147
results.[i] <- r
146148
| Failure f -> exceptions.Add f
149+
values.Dispose()
147150
match exceptions.Count with
148151
| 0 -> return results
149152
| 1 -> return exceptions.First().Reraise ()
150-
| _ -> return AggregateException exceptions |> raise
153+
| _ -> return AggregateException (exceptions.AsReadOnly()) |> raise
151154
})
152155
else
153-
let exceptions =
156+
use values = values
157+
use exceptions =
154158
values
155-
|> Array.choose (function
156-
| Failure f -> Some f
157-
| _ -> None)
158-
match exceptions.Length with
159-
| 0 -> Value (values |> Array.map (fun (Value v) -> v))
159+
|> PooledResizeArray.vChoose (function
160+
| Failure f -> ValueSome f
161+
| _ -> ValueNone)
162+
match exceptions.Count with
163+
| 0 -> Value (values |> Seq.map (fun (Value v) -> v) |> Seq.toArray)
160164
| 1 -> Failure (exceptions.First ())
161-
| _ -> Failure (AggregateException exceptions)
165+
| _ -> Failure (AggregateException (exceptions.AsReadOnly()))
162166

163167
/// Converts array of AsyncVals into AsyncVal with array results.
164168
/// In case when are non-immediate values in provided array, they are
165169
/// executed all in parallel, in unordered fashion. Order of values
166170
/// inside returned array is maintained.
167171
/// If the array contains a Failure, then the entire array will not resolve
168-
let collectParallel (values : AsyncVal<'T>[]) : AsyncVal<'T[]> =
169-
if values.Length = 0 then Value [||]
172+
let collectParallel (values : AsyncVal<'T> seq) : AsyncVal<'T[]> =
173+
use values = new PooledResizeArray<_> (values)
174+
let length = values.Count
175+
if length = 0 then Value [||]
170176
else
171-
let indexes = List<_> (0)
172-
let continuations = List<_> (0)
173-
let results = Array.zeroCreate values.Length
174-
let exceptions = ResizeArray values.Length
175-
for i = 0 to values.Length - 1 do
177+
let indexes = new PooledResizeArray<_> (length)
178+
let continuations = new PooledResizeArray<_> (length)
179+
let results = Array.zeroCreate length
180+
use exceptions = new PooledResizeArray<_> (length)
181+
for i = 0 to length - 1 do
176182
let value = values.[i]
177183
match value with
178184
| Value v -> results.[i] <- v
@@ -182,13 +188,15 @@ module AsyncVal =
182188
| Failure f -> exceptions.Add f
183189
match exceptions.Count with
184190
| 1 -> AsyncVal.Failure (exceptions.First ())
185-
| count when count > 1 -> AsyncVal.Failure (AggregateException exceptions)
191+
| count when count > 1 -> AsyncVal.Failure (AggregateException (exceptions.AsReadOnly()))
186192
| _ ->
187193
if indexes.Count = 0 then Value (results)
188194
else Async (async {
189195
let! vals = continuations |> Async.Parallel
190196
for i = 0 to indexes.Count - 1 do
191197
results.[indexes.[i]] <- vals.[i]
198+
indexes.Dispose()
199+
continuations.Dispose()
192200
return results
193201
})
194202

src/FSharp.Data.GraphQL.Shared/FSharp.Data.GraphQL.Shared.fsproj

+5
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleToAttribute">
1818
<_Parameter1>FSharp.Data.GraphQL.Server</_Parameter1>
1919
</AssemblyAttribute>
20+
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleToAttribute">
21+
<_Parameter1>FSharp.Data.GraphQL.Server.AspNetCore</_Parameter1>
22+
</AssemblyAttribute>
2023
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleToAttribute">
2124
<_Parameter1>FSharp.Data.GraphQL.Client</_Parameter1>
2225
</AssemblyAttribute>
@@ -29,6 +32,7 @@
2932
</ItemGroup>
3033

3134
<ItemGroup>
35+
<PackageReference Include="Collections.Pooled" />
3236
<PackageReference Include="FParsec" />
3337
<PackageReference Include="FSharp.SystemTextJson" />
3438
<PackageReference Include="FsToolkit.ErrorHandling" />
@@ -44,6 +48,7 @@
4448
<Compile Include="Helpers\Extensions.fs" />
4549
<Compile Include="Helpers\Reflection.fs" />
4650
<Compile Include="Helpers\MemoryCache.fs" />
51+
<Compile Include="Helpers\PooledResizeArray.fs" />
4752
<Compile Include="Errors.fs" />
4853
<Compile Include="Exception.fs" />
4954
<Compile Include="ValidationTypes.fs" />
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
namespace FSharp.Data.GraphQL
2+
3+
open Collections.Pooled
4+
5+
type internal PooledResizeArray<'T> = PooledList<'T>
6+
7+
module internal PooledResizeArray =
8+
9+
let ofSeqWithCapacity capacity items =
10+
let list = new PooledResizeArray<'T> (capacity = capacity)
11+
list.AddRange (collection = items)
12+
list
13+
14+
let exists f (list : PooledResizeArray<'T>) = list.Exists (f)
15+
16+
let length (list : PooledResizeArray<'T>) = list.Count
17+
18+
let vChoose f (list : PooledResizeArray<'T>) =
19+
let res = new PooledResizeArray<_> (list.Count)
20+
21+
for i = 0 to length list - 1 do
22+
match f list[i] with
23+
| ValueNone -> ()
24+
| ValueSome b -> res.Add (b)
25+
26+
res
27+
28+
let choose f (list : PooledResizeArray<'T>) =
29+
let res = new PooledResizeArray<_> (list.Count)
30+
31+
for i = 0 to length list - 1 do
32+
match f list[i] with
33+
| None -> ()
34+
| Some b -> res.Add (b)
35+
36+
res

0 commit comments

Comments
 (0)