Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Transact overloads experience #325

Merged
merged 1 commit into from
May 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Rework Transact overloads
  • Loading branch information
bartelink committed May 16, 2022
commit 5dd026a5a64ed72890a2114ecc196c0fc7438f70
8 changes: 1 addition & 7 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,13 @@ The `Unreleased` section name is replaced by the expected version of next releas

### Added

- `Equinox`: `Decider.Transact(interpret : 'state -> Async<'event list>)` [#314](https://github.com/jet/equinox/pull/314)
- `Equinox`: `Decider.Transact(interpret : 'state -> 'event list, render : 'state -> 'view)` [#320](https://github.com/jet/equinox/pull/320)
- `Equinox`: `Decider.Transact(interpret : 'state -> Async<'event list>, render : 'state -> 'view)` [#320](https://github.com/jet/equinox/pull/320)
- `Equinox`: `Decider.TransactEx(interpret : 'state -> 'event list, render : ISyncContext<'state> -> 'view)` [#320](https://github.com/jet/equinox/pull/320)
- `Equinox`: `Decider.TransactEx(interpret : ISyncContext<'state> -> 'event list)` [#320](https://github.com/jet/equinox/pull/320)
- `Equinox`: `Decider.TransactEx(decide : ISyncContext<'state> -> 'result * 'event list)` [#320](https://github.com/jet/equinox/pull/320)
- `Equinox`: `Decider.Transact`, `TransactAsync`, `TransactExAsync` overloads [#325](https://github.com/jet/equinox/pull/320)
- `CosmosStore.Prometheus`: Add `rut` tag to enable filtering/grouping by Read vs Write activity as per `DynamoDB` [#321](https://github.com/jet/equinox/pull/321)
- `DynamoDb`/`DynamoDb.Prometheus`: Implements the majority of the `CosmosStore` functionality via `FSharp.AWS.DynamoDB` [#321](https://github.com/jet/equinox/pull/321)
- `EventStoreDb`: As per `EventStore` module, but using the modern `EventStore.Client.Grpc.Streams` client [#196](https://github.com/jet/equinox/pull/196)

### Changed

- `Equinox`: rename `Decider.TransactAsync` to `Transact` [#314](https://github.com/jet/equinox/pull/314)
- `Equinox`: Merge `ResolveOption` and `XXXStoreCategory.FromMemento` as `LoadOption` [#308](https://github.com/jet/equinox/pull/308)
- `Equinox`: Merge `XXXStoreCategory.Resolve(sn, ?ResolveOption)` and `XXXStoreCategory.FromMemento` as option `LoadOption` parameter on all `Transact` and `Query` methods [#308](https://github.com/jet/equinox/pull/308)
- `CosmosStore`: Require `Microsoft.Azure.Cosmos` v `3.27.0` [#310](https://github.com/jet/equinox/pull/310)
Expand Down
2 changes: 1 addition & 1 deletion samples/Store/Domain/Cart.fs
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ type Service internal (resolve : CartId -> Equinox.Decider<Events.Event, Fold.St
#endif
let decider = resolve cartId
let opt = if optimistic then Equinox.AllowStale else Equinox.RequireLoad
decider.Transact(interpret, opt)
decider.TransactAsync(interpret, opt)

member x.ExecuteManyAsync(cartId, optimistic, commands : Command seq, ?prepare) : Async<unit> =
x.Run(cartId, optimistic, commands, ?prepare=prepare) |> Async.Ignore
Expand Down
2 changes: 1 addition & 1 deletion samples/Store/Domain/Favorites.fs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ type Service internal (resolve : ClientId -> Equinox.Decider<Events.Event, Fold.
// NOTE not a real world example - used for an integration test; TODO get a better example where it's actually relevant
member _.UnfavoriteWithPostVersion(clientId, sku) =
let decider = resolve clientId
decider.TransactEx(decideUnfavorite sku, fun c -> c.Version)
decider.TransactEx((fun c -> (), decideUnfavorite sku c.State), fun () c -> c.Version)

let create log resolveStream =
let resolve id = Equinox.Decider(log, resolveStream (streamName id), maxAttempts = 3)
Expand Down
2 changes: 1 addition & 1 deletion samples/Store/Domain/SavedForLater.fs
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ type Service internal (resolve : ClientId -> Equinox.Decider<Events.Event, Fold.

let remove clientId (resolveCommand : (SkuId->bool) -> Async<Command>) : Async<unit> =
let decider = resolve clientId
decider.Transact(fun (state : Fold.State) -> async {
decider.TransactAsync(fun (state : Fold.State) -> async {
let contents = seq { for item in state -> item.skuId } |> set
let! cmd = resolveCommand contents.Contains
let _, events = decide maxSavedItems cmd state
Expand Down
72 changes: 40 additions & 32 deletions src/Equinox/Decider.fs
Original file line number Diff line number Diff line change
Expand Up @@ -66,62 +66,70 @@ type Decider<'event, 'state>
member _.Transact(interpret : 'state -> 'event list, render : 'state -> 'view, ?option) : Async<'view> =
transact option (fun (_token, state) -> async { return (), interpret state }) (fun () (_token, state) -> render state)

/// 1. Invoke the supplied <c>Async</c> <c>interpret</c> function with the present state
/// 2. (if events yielded) Attempt to sync the yielded events to the stream.
/// (Restarts up to <c>maxAttempts</c> times with updated state per attempt, throwing <c>MaxResyncsExhaustedException</c> on failure of final attempt.)
/// 3. Uses <c>render</c> to generate a 'view from the persisted final state
member _.Transact(interpret : 'state -> Async<'event list>, render : 'state -> 'view, ?option) : Async<'view> =
transact option (fun (_token, state) -> async { let! es = interpret state in return (), es }) (fun () (_token, state) -> render state)

/// 1. Invoke the supplied <c>decide</c> function with the present state, holding the <c>'result</c>
/// 2. (if events yielded) Attempt to sync the yielded events to the stream.
/// (Restarts up to <c>maxAttempts</c> times with updated state per attempt, throwing <c>MaxResyncsExhaustedException</c> on failure of final attempt.)
/// 3. Yield result
member _.Transact(decide : 'state -> 'result * 'event list, ?option) : Async<'result> =
transact option (fun (_token, state) -> async { return decide state }) (fun result _context -> result)

/// 1. Invoke the supplied <c>Async</c> <c>decide</c> function with the present state, holding the <c>'result</c>
/// 2. (if events yielded) Attempt to sync the yielded events to the stream.
/// (Restarts up to <c>maxAttempts</c> times with updated state per attempt, throwing <c>MaxResyncsExhaustedException</c> on failure of final attempt.)
/// 3. Yield result
member _.Transact(decide : 'state -> Async<'result * 'event list>, ?option) : Async<'result> =
transact option (fun (_token, state) -> decide state) (fun result _context -> result)

/// 1. Invoke the supplied <c>interpret</c> function with the present state (including extended context)
/// 1. Invoke the supplied <c>decide</c> function with the present state, holding the <c>'result</c>
/// 2. (if events yielded) Attempt to sync the yielded events to the stream.
/// (Restarts up to <c>maxAttempts</c> times with updated state per attempt, throwing <c>MaxResyncsExhaustedException</c> on failure of final attempt.)
member _.TransactEx(interpret : ISyncContext<'state> -> 'event list, ?option) : Async<unit> =
transact option (fun (Context c) -> async { return (), interpret c }) (fun () _context -> ())
/// 3. Yields a final 'view produced by <c>mapResult</c> from the <c>'result</c> and/or the final persisted <c>'state</c>
member _.Transact(decide : 'state -> 'result * 'event list, mapResult : 'result -> 'state -> 'view, ?option) : Async<'view> =
transact option (fun (_token, state) -> async { return decide state }) (fun r (_token, state) -> mapResult r state)

/// 1. Invoke the supplied <c>interpret</c> function with the present state
/// 1. Invoke the supplied <c>decide</c> function with the current complete context, holding the <c>'result</c>
/// 2. (if events yielded) Attempt to sync the yielded events to the stream.
/// (Restarts up to <c>maxAttempts</c> times with updated state per attempt, throwing <c>MaxResyncsExhaustedException</c> on failure of final attempt.)
/// 3. Yields a final 'view produced by <c>render</c> from the final persisted <c>ISyncContext</c>
member _.TransactEx(interpret : 'state -> 'event list, render : ISyncContext<'state> -> 'view, ?option) : Async<'view> =
transact option (fun (_token, state) -> async { return (), interpret state }) (fun () (Context c) -> render c)
/// 3. Yields <c>result</c>
member _.TransactEx(decide : ISyncContext<'state> -> 'result * 'event list, ?option) : Async<'result> =
transact option (fun (Context c) -> async { return decide c }) (fun result _context -> result)

/// 1. Invoke the supplied <c>Async</c> <c>decide</c> function with the present state (including extended context), holding the <c>'result</c>
/// 1. Invoke the supplied <c>decide</c> function with the current complete context, holding the <c>'result</c>
/// 2. (if events yielded) Attempt to sync the yielded events to the stream.
/// (Restarts up to <c>maxAttempts</c> times with updated state per attempt, throwing <c>MaxResyncsExhaustedException</c> on failure of final attempt.)
/// 3. Yields a final 'view produced by <c>mapResult</c> from the <c>'result</c> and/or the final persisted <c>ISyncContext</c>
member _.TransactEx(decide : ISyncContext<'state> -> Async<'result * 'event list>, mapResult : 'result -> ISyncContext<'state> -> 'view, ?option) : Async<'view> =
transact option (fun (Context c) -> decide c) (fun r (Context c) -> mapResult r c)

/// 1. Invoke the supplied <c>decide</c> function with the present state (including extended context), holding the <c>'result</c>
/// 2. (if events yielded) Attempt to sync the yielded events to the stream.
/// (Restarts up to <c>maxAttempts</c> times with updated state per attempt, throwing <c>MaxResyncsExhaustedException</c> on failure of final attempt.)
/// 3. Yields <c>result</c>
member _.TransactEx(decide : ISyncContext<'state> -> 'result * 'event list, ?option) : Async<'result> =
transact option (fun (Context c) -> async { return decide c }) (fun result _context -> result)
member _.TransactEx(decide : ISyncContext<'state> -> 'result * 'event list, mapResult : 'result -> ISyncContext<'state> -> 'view, ?option) : Async<'view> =
transact option (fun (Context c) -> async { return decide c }) (fun r (Context c) -> mapResult r c)

/// Project from the folded <c>'state</c>, but without executing a decision flow as <c>Transact</c> does
member _.Query(render : 'state -> 'view, ?option) : Async<'view> =
query option (fun (_token, state) -> render state)

/// Project from the stream's <c>'state<c> (including extended context), but without executing a decision flow as <c>TransactEx<c> does
/// Project from the stream's complete context, but without executing a decision flow as <c>TransactEx<c> does
member _.QueryEx(render : ISyncContext<'state> -> 'view, ?option) : Async<'view> =
query option (fun (Context c) -> render c)

/// 1. Invoke the supplied <c>Async</c> <c>interpret</c> function with the present state
/// 2. (if events yielded) Attempt to sync the yielded events to the stream.
/// (Restarts up to <c>maxAttempts</c> times with updated state per attempt, throwing <c>MaxResyncsExhaustedException</c> on failure of final attempt.)
/// 3. Uses <c>render</c> to generate a 'view from the persisted final state
member _.TransactAsync(interpret : 'state -> Async<'event list>, render : 'state -> 'view, ?option) : Async<'view> =
transact option (fun (_token, state) -> async { let! es = interpret state in return (), es }) (fun () (_token, state) -> render state)

/// 1. Invoke the supplied <c>Async</c> <c>decide</c> function with the present state, holding the <c>'result</c>
/// 2. (if events yielded) Attempt to sync the yielded events to the stream.
/// (Restarts up to <c>maxAttempts</c> times with updated state per attempt, throwing <c>MaxResyncsExhaustedException</c> on failure of final attempt.)
/// 3. Yield result
member _.TransactAsync(decide : 'state -> Async<'result * 'event list>, ?option) : Async<'result> =
transact option (fun (_token, state) -> decide state) (fun result _context -> result)

/// 1. Invoke the supplied <c>Async</c> <c>decide</c> function with the current complete context, holding the <c>'result</c>
/// 2. (if events yielded) Attempt to sync the yielded events to the stream.
/// (Restarts up to <c>maxAttempts</c> times with updated state per attempt, throwing <c>MaxResyncsExhaustedException</c> on failure of final attempt.)
/// 3. Yield result
member _.TransactExAsync(decide : ISyncContext<'state> -> Async<'result * 'event list>, ?option) : Async<'result> =
transact option (fun (Context c) -> decide c) (fun r _c -> r)

/// 1. Invoke the supplied <c>Async</c> <c>decide</c> function with the current complete context, holding the <c>'result</c>
/// 2. (if events yielded) Attempt to sync the yielded events to the stream.
/// (Restarts up to <c>maxAttempts</c> times with updated state per attempt, throwing <c>MaxResyncsExhaustedException</c> on failure of final attempt.)
/// 3. Yields a final 'view produced by <c>mapResult</c> from the <c>'result</c> and/or the final persisted <c>ISyncContext</c>
member _.TransactExAsync(decide : ISyncContext<'state> -> Async<'result * 'event list>, mapResult : 'result -> ISyncContext<'state> -> 'view, ?option) : Async<'view> =
transact option (fun (Context c) -> decide c) (fun r (Context c) -> mapResult r c)

/// Store-agnostic Loading Options
and [<NoComparison; NoEquality>] LoadOption<'state> =
/// Default policy; Obtain latest state from store based on consistency level configured
Expand Down