Skip to content

Commit fc464cb

Browse files
committed
Merge branch 'master' into cosmos-linq
2 parents efb752d + 31334e0 commit fc464cb

File tree

10 files changed

+35
-31
lines changed

10 files changed

+35
-31
lines changed

CHANGELOG.md

+4-8
Original file line numberDiff line numberDiff line change
@@ -22,21 +22,17 @@ The `Unreleased` section name is replaced by the expected version of next releas
2222
- `eqx dump`, `eqx query`: `-sl` Support for specifying streams to dump via a [CosmosDB `LIKE` expression](https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/query/keywords#like) [#473](https://github.com/jet/equinox/pull/473)
2323
- `eqx dump`: `-Q` strips intervals, regularizes snapshots, logs stream names [#473](https://github.com/jet/equinox/pull/473)
2424
- `eqx stats`: `-O`, `-N` flags extract oldest and newest `_ts` within a store [#459](https://github.com/jet/equinox/pull/459)
25-
- `eqx stats`: `-U` flag to count streams with unfolds and total number thereof; `-I` alias relabel Documents as Items [#464](https://github.com/jet/equinox/pull/464)
26-
- `eqx stats`: `-I` flag; relabel Documents as Items, retaining existing `-D` flag [#464](https://github.com/jet/equinox/pull/464)
27-
- `eqx`: `-Q` flag omits timestamps from console output logging [#459](https://github.com/jet/equinox/pull/459)
25+
- `eqx stats`: `-U` flag to count streams with unfolds and total number thereof [#473](https://github.com/jet/equinox/pull/473)
26+
- `eqx stats`: `-I` flag; relabel Documents as Items, retaining existing `-D` flag [#473](https://github.com/jet/equinox/pull/473)
2827
- `Equinox.CosmosStore.Linq`: Add LINQ querying support for Indexed `u`nfolds (`AccessStrategy.Custom`+`CosmosStoreCategory.shouldCompress`) [#450](https://github.com/jet/equinox/pull/450)
29-
- `eqx dump`, `eqx query`: `-sl` Support for specifying streams to dump via a [CosmosDB `LIKE` expression](https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/query/keywords#like) [#450](https://github.com/jet/equinox/pull/450)
30-
- `eqx dump`: `-Q` strips intervals, regularizes snapshots, logs stream names [#450](https://github.com/jet/equinox/pull/450)
31-
- `eqx top`: Support for analyzing space usage for event and view containers by category and/or stream [#450](https://github.com/jet/equinox/pull/450)
32-
- `eqx destroy`: Support for deleting the items(documents) underlying a category/stream/arbitrary `WHERE` clause [#450](https://github.com/jet/equinox/pull/450)
3328

3429
### Changed
3530

36-
- `Equinox.*Store`,`Equinox.*Store.Prometheus`: Pin `Equinox` dependencies to `[4.0.3, 5.0.0)`] [#450](https://github.com/jet/equinox/pull/450)
31+
- `Equinox.*Store`,`Equinox.*Store.Prometheus`: Pin `Equinox` dependencies to `[4.0.3, 5.0.0)`] [#448](https://github.com/jet/equinox/pull/448)
3732
- `Equinox.CosmosStore`: Update `System.Text.Json` dep to `6.0.10` per [CVE-2024-43485](https://github.com/advisories/GHSA-8g4q-xg66-9fp4) [#470](https://github.com/jet/equinox/pull/470)
3833
- `Equinox.CosmosStore`: Minimum `Microsoft.Azure.Cosmos` requirement updated to `3.43.1` to avail of integrated `System.Text.Json` support [#467](https://github.com/jet/equinox/pull/467)
3934
- `Equinox.CosmosStore.CosmosStoreConnector`: Removed mandatory `requestTimeout` argument [#467](https://github.com/jet/equinox/pull/467)
35+
- `Equinox.DynamoStore`: Up min `FSharp.AWS.DynamoDB` to v `0.12.3-beta` to handle breaking change [#474](https://github.com/jet/equinox/pull/474)
4036
- `Equinox.MessageDb`: Up min `Npgsql` to v `7.0.7` as `7.0.0` is on CVE blacklist
4137
- `Equinox.MemoryStore`: Remove unused TrySync args [#466](https://github.com/jet/equinox/pull/466)
4238

Directory.Build.props

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
<WarningLevel>5</WarningLevel>
1212
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
13-
<!-- NU5104: Warning As Error: A stable release of a package should not have a prerelease dependency. Either modify the version spec of dependency "FSharp.AWS.DynamoDB [0.12.0-beta, )-->
13+
<!-- NU5104: Warning As Error: A stable release of a package should not have a prerelease dependency. Either modify the version spec of dependency "FSharp.AWS.DynamoDB [0...., )-->
1414
<NoWarn>NU5104</NoWarn>
1515
<!-- For packages we produce, we want to be explicit and conservative in what we depend on
1616
Tools and Tests flip this to false in order to ensure we validate with the recent one embdedded in the SDK-->

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ The components within this repository are delivered as multi-targeted Nuget pack
172172
- `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`)
173173
- `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` v `4.0.3`, `Equinox`, `Microsoft.Azure.Cosmos` >= `3.43.1`, `System.Text.Json`, `FSharp.Control.TaskSeq`)
174174
- `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`)
175-
- `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.12.0-beta`, `FSharp.Control.TaskSeq`)
175+
- `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.12.3-beta`, `FSharp.Control.TaskSeq`)
176176
- `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`)
177177
- `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`, `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**
178178
- `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)

src/Equinox.DynamoStore/DynamoStore.fs

+22-14
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ module Unfold =
9999
/// NOTE names are intended to generally align with CosmosStore naming. Key Diffs:
100100
/// - no mandatory `id` and/or requirement for it to be a `string` -> replaced with `i` as an int64
101101
/// (also Tip magic value is tipMagicI: Int32.MaxValue, not "-1")
102-
/// - etag is managed explicitly (on Cosmos DB, its managed by the service and named "_etag")
102+
/// - etag is managed explicitly (on Cosmos DB, it's managed by the service and named "_etag")
103103
[<NoEquality; NoComparison>]
104104
type Batch =
105105
{ p: string // "{streamName}"
@@ -180,7 +180,7 @@ module Batch =
180180
// If we're loading from a nominated position, we need to discard items in the batch before/after the start on the start page
181181
x.e |> Seq.filter (fun e -> let i = int64 e.i in i >= indexMin && int64 i < indexMax)
182182

183-
/// Computes base Index for the Item (`i` can bear the the magic value TipI when the Item is the Tip)
183+
/// Computes base Index for the Item (`i` can bear the magic value TipI when the Item is the Tip)
184184
let baseIndex (x: Batch) = x.n - x.e.LongLength
185185
let bytesUnfolds (x: Batch) = Unfold.arrayBytes x.u
186186
let bytesBase (x: Batch) = 80 + x.p.Length + String.length x.etag + Event.arrayBytes x.e
@@ -493,7 +493,7 @@ type Position =
493493
override x.ToString() = sprintf "{ n=%d; etag=%s; e=%d; b=%d+%d }" x.index x.etag x.events.Length x.baseBytes x.unfoldsBytes
494494
module internal Position =
495495

496-
// NOTE a write of Some 0 to x.b round-trips as None
496+
// NOTE writing Some 0 to x.b round-trips as None
497497
let fromTip (x: Batch) = { index = x.n; etag = x.etag; events = x.e; calvedBytes = defaultArg x.b 0; baseBytes = Batch.bytesBase x; unfoldsBytes = Batch.bytesUnfolds x }
498498
let fromElements (p, b, n, e, u, etag) = fromTip { p = p; b = Some b; i = Unchecked.defaultof<_>; n = n; e = e; u = u; etag = etag }
499499
let tryFromBatch (x: Batch) = if Batch.isTip x.i then fromTip x |> Some else None
@@ -508,9 +508,11 @@ module internal Sync =
508508

509509
let private (|DynamoDbConflict|_|): exn -> _ = function
510510
| Precondition.CheckFailed
511-
| TransactWriteItemsRequest.TransactionCanceledConditionalCheckFailed -> Some ()
511+
| Transaction.TransactionCanceledConditionalCheckFailed -> Some ()
512512
| _ -> None
513-
513+
type [<NoEquality; NoComparison>] TransactWrite<'T> =
514+
| Put of item: Batch.Schema * ConditionExpression<'T> option
515+
| Update of TableKey * ConditionExpression<'T> option * UpdateExpression<Batch.Schema>
514516
let private cce: Quotations.Expr<Batch.Schema -> bool> -> ConditionExpression<Batch.Schema> = template.PrecomputeConditionalExpr
515517
let private cue (ue: Quotations.Expr<Batch.Schema -> Batch.Schema>) = template.PrecomputeUpdateExpr ue
516518

@@ -567,11 +569,17 @@ module internal Sync =
567569
let etag' = let g = Guid.NewGuid() in g.ToString "N"
568570
let actions = generateRequests stream requestArgs etag'
569571
let rm = Metrics()
570-
try do! let context = table.CreateContext(rm.Add)
571-
match actions with
572+
try let context = table.CreateContext(rm.Add)
573+
do! match actions with
572574
| [ TransactWrite.Put (item, Some cond) ] -> context.PutItemAsync(item, cond) |> Async.Ignore
573575
| [ TransactWrite.Update (key, Some cond, updateExpr) ] -> context.UpdateItemAsync(key, updateExpr, cond) |> Async.Ignore
574-
| actions -> context.TransactWriteItems actions |> Async.Ignore
576+
| actions ->
577+
let req = context.CreateTransaction()
578+
for x in actions do
579+
match x with
580+
| TransactWrite.Put (item, cond) -> req.Put(context, item, ?precondition = cond)
581+
| TransactWrite.Update (key, cond, updateExpr) -> req.Update(context, key, updateExpr, ?precondition = cond)
582+
req.TransactWriteItems()
575583
|> Async.executeAsTask ct
576584
return struct (rm.Consumed, Res.Written etag')
577585
with DynamoDbConflict ->
@@ -867,15 +875,15 @@ module internal Query =
867875

868876
match primary, fallback with
869877
| Some { found = true }, _ -> return toPosition res, events // origin found in primary, no need to look in fallback
870-
| Some { minIndex = i }, _ when i <= minI -> return toPosition res, events // primary had required earliest event Index, no need to look at fallback
878+
| Some { minIndex = i }, _ when i <= minI -> return toPosition res, events // primary had required min event Index, no need to look at fallback
871879
| None, _ when Option.isNone tip -> return toPosition res, events // initial load where no documents present in stream
872880
| _, Choice1Of2 allowMissing ->
873881
logMissing (minIndex, i) "Origin event not found; no Archive Table supplied"
874882
if allowMissing then return toPosition res, events
875883
else return failwith "Origin event not found; no Archive Table supplied"
876884
| _, Choice2Of2 fallback ->
877885

878-
let maxIndex = match primary with Some p -> Some p.minIndex | None -> maxIndex // if no batches in primary, high water mark from tip is max
886+
let maxIndex = match primary with Some p -> Some p.minIndex | None -> maxIndex // if no batches in primary, high-water mark from tip is max
879887
let! fallback = fallback (minIndex, maxIndex, ct)
880888
let events =
881889
match fallback with
@@ -935,7 +943,7 @@ module internal Prune =
935943
for x in batches |> Seq.takeWhile (fun x -> isRelevant x || Option.isNone lwm) do
936944
let batchSize = x.n - x.index |> int
937945
let eligibleEvents = max 0 (min batchSize (int (indexInclusive + 1L - x.index)))
938-
if x.isTip then // Even if we remove the last event from the Tip, we need to retain a) unfolds b) position (n)
946+
if x.isTip then // Even if we remove the last event from the Tip, we need to retain a. unfolds b. position (n)
939947
if eligibleEvents <> 0 then
940948
let! charge = trimTip x.n eligibleEvents
941949
trimCharges <- trimCharges + charge.total
@@ -1017,7 +1025,7 @@ module Internal =
10171025
// - TransactWriteItems is more than twice the cost in Write RU vs a normal UpdateItem, with associated latency impacts
10181026
// - various other considerations, e.g. we need to re-read the Tip next time around see https://stackoverflow.com/a/71706015/11635
10191027
let defaultTipMaxBytes = 32 * 1024
1020-
// In general we want to minimize round-trips, but we'll get better diagnostic feedback under load if we constrain our
1028+
// In general, we want to minimize round-trips, but we'll get better diagnostic feedback under load if we constrain our
10211029
// queries to shorter pages. The effect of this is of course highly dependent on the max Item size, which is
10221030
// dictated by the TipOptions - in the default configuration that's controlled by defaultTipMaxBytes
10231031
let defaultMaxItems = 32
@@ -1178,7 +1186,7 @@ type DynamoStoreConnector(clientConfig: Amazon.DynamoDBv2.AmazonDynamoDBConfig,
11781186
let clientConfig = Amazon.DynamoDBv2.AmazonDynamoDBConfig(ServiceURL = serviceUrl, RetryMode = m, MaxErrorRetry = retries, Timeout = timeout)
11791187
DynamoStoreConnector(clientConfig, Amazon.Runtime.BasicAWSCredentials(accessKey, secretKey))
11801188

1181-
/// Connect to a nominated SystemName with endpoints and credentials gathered implicitly from well-known environment variables and/or configuration etc
1189+
/// Connect to a nominated SystemName with endpoints and credentials gathered implicitly from well-known environment variables and/or configuration etc.
11821190
/// systemName: Amazon SystemName, e.g. "us-west-1"
11831191
/// timeout: Required; AWS SDK Default: 100s
11841192
/// maxRetries: Required; AWS SDK Default: 10
@@ -1270,7 +1278,7 @@ type AccessStrategy<'event, 'state> =
12701278
/// enable efficient reading without having to query the Event documents.
12711279
| Snapshot of isOrigin: ('event -> bool) * toSnapshot: ('state -> 'event)
12721280
/// Allow any events that pass the `isOrigin` test to be used in lieu of folding all the events from the start of the stream
1273-
/// When writing, uses `toSnapshots` to 'unfold' the <c>'state</c>, representing it as one or more Event records to be stored in
1281+
/// When writing, uses `toSnapshots` to 'unfold' the <c>&apos;state</c>, representing it as one or more Event records to be stored in
12741282
/// the Tip with efficient read cost.
12751283
| MultiSnapshot of isOrigin: ('event -> bool) * toSnapshots: ('state -> 'event[])
12761284
/// Instead of actually storing the events representing the decisions, only ever update a snapshot stored in the Tip document

src/Equinox.DynamoStore/Equinox.DynamoStore.fsproj

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,15 @@
1515

1616
<ItemGroup>
1717
<ProjectReference Condition=" '$(Configuration)' == 'Debug' " Include="..\Equinox\Equinox.fsproj" />
18-
<PackageReference Condition=" '$(Configuration)' == 'Release' " Include="Equinox" Version="[4.0.0, 5.0.0)" />
18+
<PackageReference Condition=" '$(Configuration)' == 'Release' " Include="Equinox" Version="[4.0.3, 5.0.0)" />
1919
</ItemGroup>
2020

2121
<ItemGroup>
2222
<PackageReference Include="MinVer" Version="5.0.0" PrivateAssets="All" />
2323

2424
<PackageReference Include="FSharp.Core" Version="6.0.7" ExcludeAssets="contentfiles" />
2525

26-
<PackageReference Include="FSharp.AWS.DynamoDB" Version="0.12.0-beta" />
26+
<PackageReference Include="FSharp.AWS.DynamoDB" Version="0.12.3-beta" />
2727
<PackageReference Include="FSharp.Control.TaskSeq" Version="0.4.0" />
2828
</ItemGroup>
2929

src/Equinox.EventStore/Equinox.EventStore.fsproj

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
<ItemGroup>
1717
<ProjectReference Condition=" '$(Configuration)' == 'Debug' " Include="..\Equinox\Equinox.fsproj" />
18-
<PackageReference Condition=" '$(Configuration)' == 'Release' " Include="Equinox" Version="[4.0.0, 5.0.0)" />
18+
<PackageReference Condition=" '$(Configuration)' == 'Release' " Include="Equinox" Version="[4.0.3, 5.0.0)" />
1919
</ItemGroup>
2020

2121
<ItemGroup>

src/Equinox.EventStoreDb/Equinox.EventStoreDb.fsproj

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
<ItemGroup>
1717
<ProjectReference Condition=" '$(Configuration)' == 'Debug' " Include="..\Equinox\Equinox.fsproj" />
18-
<PackageReference Condition=" '$(Configuration)' == 'Release' " Include="Equinox" Version="[4.0.0, 5.0.0)" />
18+
<PackageReference Condition=" '$(Configuration)' == 'Release' " Include="Equinox" Version="[4.0.3, 5.0.0)" />
1919
</ItemGroup>
2020

2121
<ItemGroup>

src/Equinox.MemoryStore/Equinox.MemoryStore.fsproj

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
<ItemGroup>
1616
<ProjectReference Condition=" '$(Configuration)' == 'Debug' " Include="..\Equinox\Equinox.fsproj" />
17-
<PackageReference Condition=" '$(Configuration)' == 'Release' " Include="Equinox" Version="[4.0.0, 5.0.0)" />
17+
<PackageReference Condition=" '$(Configuration)' == 'Release' " Include="Equinox" Version="[4.0.3, 5.0.0)" />
1818
</ItemGroup>
1919

2020
<ItemGroup>

src/Equinox.MessageDb/Equinox.MessageDb.fsproj

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
<ItemGroup>
1919
<ProjectReference Condition=" '$(Configuration)' == 'Debug' " Include="..\Equinox\Equinox.fsproj" />
20-
<PackageReference Condition=" '$(Configuration)' == 'Release' " Include="Equinox" Version="[4.0.0, 5.0.0)" />
20+
<PackageReference Condition=" '$(Configuration)' == 'Release' " Include="Equinox" Version="[4.0.3, 5.0.0)" />
2121
</ItemGroup>
2222

2323
<ItemGroup>

src/Equinox.SqlStreamStore/Equinox.SqlStreamStore.fsproj

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
<ItemGroup>
1717
<ProjectReference Condition=" '$(Configuration)' == 'Debug' " Include="..\Equinox\Equinox.fsproj" />
18-
<PackageReference Condition=" '$(Configuration)' == 'Release' " Include="Equinox" Version="[4.0.0, 5.0.0)" />
18+
<PackageReference Condition=" '$(Configuration)' == 'Release' " Include="Equinox" Version="[4.0.3, 5.0.0)" />
1919
</ItemGroup>
2020

2121
<ItemGroup>

0 commit comments

Comments
 (0)