From 4c1da33356ce7a95dd1d84d5d8cf63ce21e9e066 Mon Sep 17 00:00:00 2001 From: Ruben Bartelink Date: Wed, 9 Aug 2023 10:12:32 +0100 Subject: [PATCH] Remove Equinox.Core deps; add Equinox.Tests --- Equinox.sln | 6 +++++ README.md | 12 ++++----- src/Equinox.Core/AsyncCacheCell.fs | 8 +++++- src/Equinox.Core/Equinox.Core.fsproj | 11 ++++---- .../Equinox.CosmosStore.fsproj | 6 +++-- .../Equinox.DynamoStore.fsproj | 2 +- .../Equinox.EventStore.fsproj | 2 +- .../Equinox.EventStoreDb.fsproj | 2 +- .../Equinox.MemoryStore.fsproj | 2 +- .../Equinox.MessageDb.fsproj | 8 ++---- .../Equinox.SqlStreamStore.MsSql.fsproj | 2 +- .../Equinox.SqlStreamStore.MySql.fsproj | 2 +- .../Equinox.SqlStreamStore.Postgres.fsproj | 2 +- .../Equinox.SqlStreamStore.fsproj | 2 +- .../Equinox.Tests}/CachingTests.fs | 6 ----- src/Equinox.Tests/Equinox.Tests.fsproj | 25 +++++++++++++++++++ src/Equinox/AsyncLazy.fs | 9 +++++-- src/Equinox/Equinox.fsproj | 2 +- .../Infrastructure.fs | 0 .../Equinox.Core.Tests/AsyncCacheCellTests.fs | 11 -------- tests/Equinox.Core.Tests/AsyncLazyTests.fs | 21 ++++++++++++++++ .../Equinox.Core.Tests.fsproj | 4 +-- .../Equinox.CosmosStore.Integration.fsproj | 5 ++-- 23 files changed, 96 insertions(+), 54 deletions(-) rename {tests/Equinox.Core.Tests => src/Equinox.Tests}/CachingTests.fs (97%) create mode 100644 src/Equinox.Tests/Equinox.Tests.fsproj rename src/{Equinox.Core => Equinox}/Infrastructure.fs (100%) create mode 100644 tests/Equinox.Core.Tests/AsyncLazyTests.fs diff --git a/Equinox.sln b/Equinox.sln index eff3a34c4..72584af27 100644 --- a/Equinox.sln +++ b/Equinox.sln @@ -109,6 +109,8 @@ Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Equinox.MessageDb.Integrati EndProject Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Equinox.Core.Tests", "tests\Equinox.Core.Tests\Equinox.Core.Tests.fsproj", "{FB376808-7B32-4724-98BE-D9458EB0F68B}" EndProject +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Equinox.Tests", "src\Equinox.Tests\Equinox.Tests.fsproj", "{D8677ABB-1D7C-4FC3-ABF9-5A49AF377372}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -247,6 +249,10 @@ Global {FB376808-7B32-4724-98BE-D9458EB0F68B}.Debug|Any CPU.Build.0 = Debug|Any CPU {FB376808-7B32-4724-98BE-D9458EB0F68B}.Release|Any CPU.ActiveCfg = Release|Any CPU {FB376808-7B32-4724-98BE-D9458EB0F68B}.Release|Any CPU.Build.0 = Release|Any CPU + {D8677ABB-1D7C-4FC3-ABF9-5A49AF377372}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D8677ABB-1D7C-4FC3-ABF9-5A49AF377372}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D8677ABB-1D7C-4FC3-ABF9-5A49AF377372}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D8677ABB-1D7C-4FC3-ABF9-5A49AF377372}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/README.md b/README.md index c56601a16..0a5a5d684 100644 --- a/README.md +++ b/README.md @@ -151,7 +151,7 @@ The components within this repository are delivered as multi-targeted Nuget pack ## Core library -- `Equinox` [![NuGet](https://img.shields.io/nuget/v/Equinox.svg)](https://www.nuget.org/packages/Equinox/): Store-agnostic decision flow runner that manages the optimistic concurrency protocol and application-level API surface. ([depends](https://www.fuget.org/packages/Equinox) only on `FSharp.Core` v `6.0.7`, `FsCodec` v `3.0.0`, `Serilog` (but not specific Serilog sinks, i.e. you configure to emit to `NLog` etc) +- `Equinox` [![NuGet](https://img.shields.io/nuget/v/Equinox.svg)](https://www.nuget.org/packages/Equinox/): Store-agnostic decision flow runner that manages the optimistic concurrency protocol and application-level API surface, together with the default [`System.Runtime.Caching.MemoryCache`-based] `Cache` implementation. ([depends](https://www.fuget.org/packages/Equinox) only on `FSharp.Core` v `6.0.7`, `FsCodec` v `3.0.0`, `System.Runtime.Caching`, `Serilog` (but not specific Serilog sinks, i.e. you configure to emit to `NLog` etc) ## Serialization support @@ -168,16 +168,16 @@ The components within this repository are delivered as multi-targeted Nuget pack ## Data Store libraries -- `Equinox.Core` [![NuGet](https://img.shields.io/nuget/v/Equinox.Core.svg)](https://www.nuget.org/packages/Equinox.Core/): Interfaces and helpers used in the concrete Store implementations, together with the default [`System.Runtime.Caching.MemoryCache`-based] `Cache` implementation. Hosts generic utility types frequently useful alongside Equinox: [`AsyncCacheCell`](https://github.com/jet/equinox/blob/master/src/Equinox.Core/AsyncCacheCell.fs#L36), [`Batcher`, `BatcherCache`, `BatcherDictionary`](https://github.com/jet/equinox/blob/master/src/Equinox.Core/Batching.fs#L44). ([depends](https://www.fuget.org/packages/Equinox.Core) on `Equinox`, `System.Runtime.Caching`) -- `Equinox.MemoryStore` [![MemoryStore NuGet](https://img.shields.io/nuget/v/Equinox.MemoryStore.svg)](https://www.nuget.org/packages/Equinox.MemoryStore/): In-memory store for integration testing/performance base-lining/providing out-of-the-box zero dependency storage for examples. ([depends](https://www.fuget.org/packages/Equinox.MemoryStore) on `Equinox`, `FsCodec`) -- `Equinox.CosmosStore` [![CosmosStore NuGet](https://img.shields.io/nuget/v/Equinox.CosmosStore.svg)](https://www.nuget.org/packages/Equinox.CosmosStore/): Azure CosmosDB Adapter with integrated 'unfolds' feature, facilitating optimal read performance in terms of latency and RU costs, instrumented to meet Jet's production monitoring requirements. ([depends](https://www.fuget.org/packages/Equinox.CosmosStore) on `Equinox.Core`, `Microsoft.Azure.Cosmos` >= `3.27`, `FsCodec`, `System.Text.Json`, `FSharp.Control.TaskSeq`) +- `Equinox.Core` [![NuGet](https://img.shields.io/nuget/v/Equinox.Core.svg)](https://www.nuget.org/packages/Equinox.Core/): Hosts generic utility types frequently useful alongside Equinox: [`AsyncCacheCell`](https://github.com/jet/equinox/blob/master/src/Equinox.Core/AsyncCacheCell.fs#L36), [`Batcher`, `BatcherCache`, `BatcherDictionary`](https://github.com/jet/equinox/blob/master/src/Equinox.Core/Batching.fs#L44). ([depends](https://www.fuget.org/packages/Equinox.Core) on `System.Runtime.Caching`) +- `Equinox.MemoryStore` [![MemoryStore NuGet](https://img.shields.io/nuget/v/Equinox.MemoryStore.svg)](https://www.nuget.org/packages/Equinox.MemoryStore/): In-memory store for integration testing/performance base-lining/providing out-of-the-box zero dependency storage for examples. ([depends](https://www.fuget.org/packages/Equinox.MemoryStore) on `Equinox`) +- `Equinox.CosmosStore` [![CosmosStore NuGet](https://img.shields.io/nuget/v/Equinox.CosmosStore.svg)](https://www.nuget.org/packages/Equinox.CosmosStore/): Azure CosmosDB Adapter with integrated 'unfolds' feature, facilitating optimal read performance in terms of latency and RU costs, instrumented to meet Jet's production monitoring requirements. ([depends](https://www.fuget.org/packages/Equinox.CosmosStore) on `Equinox`, `Equinox.Core`, `Microsoft.Azure.Cosmos` >= `3.27`, `System.Text.Json`, `FSharp.Control.TaskSeq`) - `Equinox.CosmosStore.Prometheus` [![CosmosStore.Prometheus NuGet](https://img.shields.io/nuget/v/Equinox.CosmosStore.Prometheus.svg)](https://www.nuget.org/packages/Equinox.CosmosStore.Prometheus/): Integration package providing a `Serilog.Core.ILogEventSink` that extracts detailed metrics information attached to the `LogEvent`s and feeds them to the `prometheus-net`'s `Prometheus.Metrics` static instance. ([depends](https://www.fuget.org/packages/Equinox.CosmosStore.Prometheus) on `Equinox.CosmosStore`, `prometheus-net >= 3.6.0`) -- `Equinox.DynamoStore` [![DynamoStore NuGet](https://img.shields.io/nuget/v/Equinox.DynamoStore.svg)](https://www.nuget.org/packages/Equinox.DynamoStore/): Amazon DynamoDB Adapter with integrated 'unfolds' feature, facilitating optimal read performance in terms of latency and RC costs, patterned after `Equinox.CosmosStore`. ([depends](https://www.fuget.org/packages/Equinox.DynamoStore) on `Equinox`, `FSharp.AWS.DynamoDB` >= `0.11.2-beta`, `FsCodec`, `FSharp.Control.TaskSeq`) +- `Equinox.DynamoStore` [![DynamoStore NuGet](https://img.shields.io/nuget/v/Equinox.DynamoStore.svg)](https://www.nuget.org/packages/Equinox.DynamoStore/): Amazon DynamoDB Adapter with integrated 'unfolds' feature, facilitating optimal read performance in terms of latency and RC costs, patterned after `Equinox.CosmosStore`. ([depends](https://www.fuget.org/packages/Equinox.DynamoStore) on `Equinox`, `FSharp.AWS.DynamoDB` >= `0.11.2-beta`, `FSharp.Control.TaskSeq`) - `Equinox.DynamoStore.Prometheus` [![DynamoStore.Prometheus NuGet](https://img.shields.io/nuget/v/Equinox.DynamoStore.Prometheus.svg)](https://www.nuget.org/packages/Equinox.DynamoStore.Prometheus/): Integration package providing a `Serilog.Core.ILogEventSink` that extracts detailed metrics information attached to the `LogEvent`s and feeds them to the `prometheus-net`'s `Prometheus.Metrics` static instance. ([depends](https://www.fuget.org/packages/Equinox.CosmosStore.Prometheus) on `Equinox.DynamoStore`, `prometheus-net >= 3.6.0`) - `Equinox.EventStore` [![EventStore NuGet](https://img.shields.io/nuget/v/Equinox.EventStore.svg)](https://www.nuget.org/packages/Equinox.EventStore/): [EventStoreDB](https://eventstore.org/) Adapter designed to meet Jet's production monitoring requirements. ([depends](https://www.fuget.org/packages/Equinox.EventStore) on `Equinox`, `EventStore.Client >= 22.0.0-preview`, `FSharp.Control.TaskSeq`), EventStore Server version `21.10` or later). **NO NOT use for new projects - the TCP interface to EventStoreDB has long been deprecated, this package is only provided to ease migration scenarios and will be removed in due course** - `Equinox.EventStoreDb` [![EventStoreDb NuGet](https://img.shields.io/nuget/v/Equinox.EventStoreDb.svg)](https://www.nuget.org/packages/Equinox.EventStoreDb/): Production-strength [EventStoreDB](https://eventstore.org/) Adapter. ([depends](https://www.fuget.org/packages/Equinox.EventStoreDb) on `Equinox`, `EventStore.Client.Grpc.Streams` >= `22.0.0`, `FSharp.Control.TaskSeq`, EventStore Server version `21.10` or later) - `Equinox.MessageDb` [![MessageDb NuGet](https://img.shields.io/nuget/v/Equinox.MessageDb.svg)](https://www.nuget.org/packages/Equinox.MessageDb/): [MessageDb](http://docs.eventide-project.org/user-guide/message-db/) Adapter. ([depends](https://www.fuget.org/packages/Equinox.MessageDb) on `Equinox`, `Npgsql` >= `7.0.0`, `FSharp.Control.TaskSeq`)) -- `Equinox.SqlStreamStore` [![SqlStreamStore NuGet](https://img.shields.io/nuget/v/Equinox.SqlStreamStore.svg)](https://www.nuget.org/packages/Equinox.SqlStreamStore/): [SqlStreamStore](https://github.com/SQLStreamStore/SQLStreamStore) Adapter derived from `Equinox.EventStore` - provides core facilities (but does not connect to a specific database; see sibling `SqlStreamStore`.* packages). ([depends](https://www.fuget.org/packages/Equinox.SqlStreamStore) on `Equinox.Core`, `FsCodec`, `SqlStreamStore >= 1.2.0-beta.8`, `FSharp.Control.TaskSeq`) +- `Equinox.SqlStreamStore` [![SqlStreamStore NuGet](https://img.shields.io/nuget/v/Equinox.SqlStreamStore.svg)](https://www.nuget.org/packages/Equinox.SqlStreamStore/): [SqlStreamStore](https://github.com/SQLStreamStore/SQLStreamStore) Adapter derived from `Equinox.EventStore` - provides core facilities (but does not connect to a specific database; see sibling `SqlStreamStore`.* packages). ([depends](https://www.fuget.org/packages/Equinox.SqlStreamStore) on `Equinox`, `SqlStreamStore >= 1.2.0-beta.8`, `FSharp.Control.TaskSeq`) - `Equinox.SqlStreamStore.MsSql` [![MsSql NuGet](https://img.shields.io/nuget/v/Equinox.SqlStreamStore.MsSql.svg)](https://www.nuget.org/packages/Equinox.SqlStreamStore.MsSql/): [SqlStreamStore.MsSql](https://sqlstreamstore.readthedocs.io/en/latest/sqlserver) Sql Server `Connector` implementation for `Equinox.SqlStreamStore` package). ([depends](https://www.fuget.org/packages/Equinox.SqlStreamStore.MsSql) on `Equinox.SqlStreamStore`, `SqlStreamStore.MsSql >= 1.2.0-beta.8`) - `Equinox.SqlStreamStore.MySql` [![MySql NuGet](https://img.shields.io/nuget/v/Equinox.SqlStreamStore.MySql.svg)](https://www.nuget.org/packages/Equinox.SqlStreamStore.MySql/): `SqlStreamStore.MySql` MySQL `Connector` implementation for `Equinox.SqlStreamStore` package). ([depends](https://www.fuget.org/packages/Equinox.SqlStreamStore.MySql) on `Equinox.SqlStreamStore`, `SqlStreamStore.MySql >= 1.2.0-beta.8`) - `Equinox.SqlStreamStore.Postgres` [![Postgres NuGet](https://img.shields.io/nuget/v/Equinox.SqlStreamStore.Postgres.svg)](https://www.nuget.org/packages/Equinox.SqlStreamStore.Postgres/): [SqlStreamStore.Postgres](https://sqlstreamstore.readthedocs.io/en/latest/postgres) PostgreSQL `Connector` implementation for `Equinox.SqlStreamStore` package). ([depends](https://www.fuget.org/packages/Equinox.SqlStreamStore.Postgres) on `Equinox.SqlStreamStore`, `SqlStreamStore.Postgres >= 1.2.0-beta.8`) diff --git a/src/Equinox.Core/AsyncCacheCell.fs b/src/Equinox.Core/AsyncCacheCell.fs index 4d3f8d124..fc4b9f22a 100755 --- a/src/Equinox.Core/AsyncCacheCell.fs +++ b/src/Equinox.Core/AsyncCacheCell.fs @@ -3,7 +3,13 @@ namespace Equinox.Core /// Generic async lazy caching implementation that admits expiration/recomputation/retry on exception semantics. /// If `workflow` fails, all readers entering while the load/refresh is in progress will share the failure /// The first caller through the gate triggers a recomputation attempt if the previous attempt ended in failure -type AsyncCacheCell<'T>(startWorkflow : System.Func>, []?isExpired: System.Func<'T, bool>) = +type +#if !EQUINOX_CORE + // NOT PUBLIC in Equinox.CosmosStore library - used internally to ensure the stored proc init is checked on first append once per container per process + // PUBLIC in Equinox.Core (it can also be used independent of Equinox) + internal +#endif + AsyncCacheCell<'T>(startWorkflow : System.Func>, []?isExpired: System.Func<'T, bool>) = let isValid = match isExpired with Some f -> not << f.Invoke | None -> fun _ -> true let mutable cell = AsyncLazy<'T>.Empty diff --git a/src/Equinox.Core/Equinox.Core.fsproj b/src/Equinox.Core/Equinox.Core.fsproj index 04ae606b1..b0ef1dadd 100644 --- a/src/Equinox.Core/Equinox.Core.fsproj +++ b/src/Equinox.Core/Equinox.Core.fsproj @@ -2,24 +2,23 @@ net6.0 + EQUINOX_CORE - + + - - - - - + + diff --git a/src/Equinox.CosmosStore/Equinox.CosmosStore.fsproj b/src/Equinox.CosmosStore/Equinox.CosmosStore.fsproj index 768d45791..905cf9f11 100644 --- a/src/Equinox.CosmosStore/Equinox.CosmosStore.fsproj +++ b/src/Equinox.CosmosStore/Equinox.CosmosStore.fsproj @@ -5,14 +5,16 @@ - + + + - + diff --git a/src/Equinox.DynamoStore/Equinox.DynamoStore.fsproj b/src/Equinox.DynamoStore/Equinox.DynamoStore.fsproj index eda813e77..33def17b5 100644 --- a/src/Equinox.DynamoStore/Equinox.DynamoStore.fsproj +++ b/src/Equinox.DynamoStore/Equinox.DynamoStore.fsproj @@ -5,7 +5,7 @@ - + diff --git a/src/Equinox.EventStore/Equinox.EventStore.fsproj b/src/Equinox.EventStore/Equinox.EventStore.fsproj index 89604e900..2ee5569e1 100644 --- a/src/Equinox.EventStore/Equinox.EventStore.fsproj +++ b/src/Equinox.EventStore/Equinox.EventStore.fsproj @@ -5,7 +5,7 @@ - + diff --git a/src/Equinox.EventStoreDb/Equinox.EventStoreDb.fsproj b/src/Equinox.EventStoreDb/Equinox.EventStoreDb.fsproj index b6f99b1c3..ca00458b4 100644 --- a/src/Equinox.EventStoreDb/Equinox.EventStoreDb.fsproj +++ b/src/Equinox.EventStoreDb/Equinox.EventStoreDb.fsproj @@ -5,7 +5,7 @@ - + diff --git a/src/Equinox.MemoryStore/Equinox.MemoryStore.fsproj b/src/Equinox.MemoryStore/Equinox.MemoryStore.fsproj index 51457c91d..821a341c4 100644 --- a/src/Equinox.MemoryStore/Equinox.MemoryStore.fsproj +++ b/src/Equinox.MemoryStore/Equinox.MemoryStore.fsproj @@ -5,7 +5,7 @@ - + diff --git a/src/Equinox.MessageDb/Equinox.MessageDb.fsproj b/src/Equinox.MessageDb/Equinox.MessageDb.fsproj index 6a1a3543f..8dc63386f 100644 --- a/src/Equinox.MessageDb/Equinox.MessageDb.fsproj +++ b/src/Equinox.MessageDb/Equinox.MessageDb.fsproj @@ -5,12 +5,8 @@ - - Infrastructure.fs - - - Internal.fs - + + diff --git a/src/Equinox.SqlStreamStore.MsSql/Equinox.SqlStreamStore.MsSql.fsproj b/src/Equinox.SqlStreamStore.MsSql/Equinox.SqlStreamStore.MsSql.fsproj index fe9b8d7d6..9dc9c805f 100644 --- a/src/Equinox.SqlStreamStore.MsSql/Equinox.SqlStreamStore.MsSql.fsproj +++ b/src/Equinox.SqlStreamStore.MsSql/Equinox.SqlStreamStore.MsSql.fsproj @@ -5,7 +5,7 @@ - + diff --git a/src/Equinox.SqlStreamStore.MySql/Equinox.SqlStreamStore.MySql.fsproj b/src/Equinox.SqlStreamStore.MySql/Equinox.SqlStreamStore.MySql.fsproj index 77225c5ad..a4e552552 100644 --- a/src/Equinox.SqlStreamStore.MySql/Equinox.SqlStreamStore.MySql.fsproj +++ b/src/Equinox.SqlStreamStore.MySql/Equinox.SqlStreamStore.MySql.fsproj @@ -5,7 +5,7 @@ - + diff --git a/src/Equinox.SqlStreamStore.Postgres/Equinox.SqlStreamStore.Postgres.fsproj b/src/Equinox.SqlStreamStore.Postgres/Equinox.SqlStreamStore.Postgres.fsproj index 4d3ea6649..1d1c75710 100644 --- a/src/Equinox.SqlStreamStore.Postgres/Equinox.SqlStreamStore.Postgres.fsproj +++ b/src/Equinox.SqlStreamStore.Postgres/Equinox.SqlStreamStore.Postgres.fsproj @@ -5,7 +5,7 @@ - + diff --git a/src/Equinox.SqlStreamStore/Equinox.SqlStreamStore.fsproj b/src/Equinox.SqlStreamStore/Equinox.SqlStreamStore.fsproj index dda5cdd4f..cc632715d 100644 --- a/src/Equinox.SqlStreamStore/Equinox.SqlStreamStore.fsproj +++ b/src/Equinox.SqlStreamStore/Equinox.SqlStreamStore.fsproj @@ -5,7 +5,7 @@ - + diff --git a/tests/Equinox.Core.Tests/CachingTests.fs b/src/Equinox.Tests/CachingTests.fs similarity index 97% rename from tests/Equinox.Core.Tests/CachingTests.fs rename to src/Equinox.Tests/CachingTests.fs index 75783c67f..3190f156e 100644 --- a/tests/Equinox.Core.Tests/CachingTests.fs +++ b/src/Equinox.Tests/CachingTests.fs @@ -53,12 +53,6 @@ let write sn (sut: Equinox.Core.ICategory<_, _, _>) = task { let wState' = trap <@ match wr with Equinox.Core.SyncResult.Written (_token, state') -> state' | _ -> failwith "unexpected" @> test <@ expectedWriteState = wState' @> } -// Pinning the fact that the algorithm is not sensitive to the reuse of the initial value of a cache entry -let [] ``AsyncLazy.Empty is a true singleton, does not allocate`` () = - let i1 = Equinox.Core.AsyncLazy.Empty - let i2 = Equinox.Core.AsyncLazy.Empty - test <@ obj.ReferenceEquals(i1, i2) @> - let [] ``NoCaching strategy does no wrapping`` () = let cat = SpyCategory() let sut = Equinox.Core.Caching.apply isStale Equinox.CachingStrategy.NoCaching cat diff --git a/src/Equinox.Tests/Equinox.Tests.fsproj b/src/Equinox.Tests/Equinox.Tests.fsproj new file mode 100644 index 000000000..8d79a573e --- /dev/null +++ b/src/Equinox.Tests/Equinox.Tests.fsproj @@ -0,0 +1,25 @@ + + + + net6.0 + + + + + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers + + + diff --git a/src/Equinox/AsyncLazy.fs b/src/Equinox/AsyncLazy.fs index 92ca8495f..d046c6e54 100644 --- a/src/Equinox/AsyncLazy.fs +++ b/src/Equinox/AsyncLazy.fs @@ -1,8 +1,13 @@ namespace Equinox.Core /// Asynchronous Lazy<'T> used to gate a workflow to ensure at most once execution of a computation. -type AsyncLazy<'T>(startTask: System.Func>) = - +type +#if !EQUINOX_CORE + // NOT PUBLIC in Equinox library - used internally in the impl of CacheEntry + // PUBLIC in Equinox.Core (which also uses it in the impl of AsyncCacheCell) + internal +#endif + AsyncLazy<'T>(startTask: System.Func>) = // NOTE due to `Lazy` semantics, failed attempts will cache any exception; AsyncCacheCell compensates for this by rolling over to a new instance let workflow = lazy startTask.Invoke() diff --git a/src/Equinox/Equinox.fsproj b/src/Equinox/Equinox.fsproj index ad9421b87..91b05631b 100644 --- a/src/Equinox/Equinox.fsproj +++ b/src/Equinox/Equinox.fsproj @@ -5,7 +5,7 @@ - + diff --git a/src/Equinox.Core/Infrastructure.fs b/src/Equinox/Infrastructure.fs similarity index 100% rename from src/Equinox.Core/Infrastructure.fs rename to src/Equinox/Infrastructure.fs diff --git a/tests/Equinox.Core.Tests/AsyncCacheCellTests.fs b/tests/Equinox.Core.Tests/AsyncCacheCellTests.fs index 79f7725fe..e2b2c9db8 100644 --- a/tests/Equinox.Core.Tests/AsyncCacheCellTests.fs +++ b/tests/Equinox.Core.Tests/AsyncCacheCellTests.fs @@ -5,17 +5,6 @@ open Swensen.Unquote open System open Xunit -[] -let ``AsyncLazy correctness`` () = async { - // ensure that the encapsulated computation fires only once - let mutable count = 0 - let cell = AsyncLazy(fun () -> task { return Interlocked.Increment &count }) - test <@ cell.TryCompleted() |> ValueOption.isNone @> - let! accessResult = [|1 .. 100|] |> Array.map (fun _ -> cell.Await() |> Async.AwaitTaskCorrect) |> Async.Parallel - test <@ cell.TryCompleted() |> ValueOption.isSome @> - test <@ accessResult |> Array.forall ((=) 1) @> -} - [] let ``AsyncCacheCell correctness`` () = async { // ensure that the encapsulated computation fires only once and that expiry functions as expected diff --git a/tests/Equinox.Core.Tests/AsyncLazyTests.fs b/tests/Equinox.Core.Tests/AsyncLazyTests.fs new file mode 100644 index 000000000..d6c77ed35 --- /dev/null +++ b/tests/Equinox.Core.Tests/AsyncLazyTests.fs @@ -0,0 +1,21 @@ +module Equinox.Tests.AsyncLazyTests + +open Equinox.Core +open Swensen.Unquote +open Xunit + +[] +let ``AsyncLazy correctness`` () = async { + // ensure that the encapsulated computation fires only once + let mutable count = 0 + let cell = AsyncLazy(fun () -> task { return System.Threading.Interlocked.Increment &count }) + test <@ cell.TryCompleted() |> ValueOption.isNone @> + let! accessResult = [|1 .. 100|] |> Array.map (fun _ -> cell.Await() |> Async.AwaitTask) |> Async.Parallel + test <@ cell.TryCompleted() |> ValueOption.isSome @> + test <@ accessResult |> Array.forall ((=) 1) @> } + +// Pinning the fact that the algorithm is not sensitive to the reuse of the initial value of a cache entry +let [] ``AsyncLazy.Empty is a true singleton, does not allocate`` () = + let i1 = AsyncLazy.Empty + let i2 = AsyncLazy.Empty + test <@ obj.ReferenceEquals(i1, i2) @> diff --git a/tests/Equinox.Core.Tests/Equinox.Core.Tests.fsproj b/tests/Equinox.Core.Tests/Equinox.Core.Tests.fsproj index c4e4ccc8c..9a6ae88e1 100644 --- a/tests/Equinox.Core.Tests/Equinox.Core.Tests.fsproj +++ b/tests/Equinox.Core.Tests/Equinox.Core.Tests.fsproj @@ -5,10 +5,10 @@ - + + - diff --git a/tests/Equinox.CosmosStore.Integration/Equinox.CosmosStore.Integration.fsproj b/tests/Equinox.CosmosStore.Integration/Equinox.CosmosStore.Integration.fsproj index 1f4e9ed8b..cc76ab6de 100644 --- a/tests/Equinox.CosmosStore.Integration/Equinox.CosmosStore.Integration.fsproj +++ b/tests/Equinox.CosmosStore.Integration/Equinox.CosmosStore.Integration.fsproj @@ -5,9 +5,8 @@ - - - + +