@reventlessdev/reventless-aws 3.0.0-alpha.344 → 3.0.0-alpha.345
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/package.json +11 -11
- package/scripts/ProvisionIdentity.res +8 -3
- package/src/Platform.res +327 -328
- package/src/Platform.res.mjs +4 -2
- package/src/Platform_Casts.res +13 -4
- package/src/adapter/Api/AppSyncEventsSigner_Ops.res +8 -4
- package/src/adapter/Api/AppSync_EventsApi.res +12 -14
- package/src/adapter/Api/AppSync_Resolver_Native.res +3 -4
- package/src/adapter/Api/AppSync_Resolver_Retrying.res +78 -70
- package/src/adapter/Api/AppSync_SourceApiAssociation_Retrying.res +11 -8
- package/src/adapter/Api/Platform_AdminScan_Ops.res +15 -14
- package/src/adapter/Api/Platform_ComponentDefinitions_Lambda.res +10 -2
- package/src/adapter/Api/Platform_ComponentDefinitions_Lambda_Ops.res +40 -50
- package/src/adapter/Api/Platform_UIFragments_Lambda.res +39 -32
- package/src/adapter/Api/Platform_UIFragments_Lambda_Ops.res +5 -2
- package/src/adapter/Auth/Auth_ActiveRolePoolAttachment.res +0 -1
- package/src/adapter/Auth/Auth_ActiveRoleStore.res +1 -2
- package/src/adapter/Auth/Auth_ActiveRoleStore_Schema.res +1 -4
- package/src/adapter/Auth/Auth_ActiveRoleTrigger_Ops.res +5 -2
- package/src/adapter/Auth/Auth_Cognito.res +3 -9
- package/src/adapter/CommandGenerator/CommandGeneratorResolvers_AppSync.res +1 -4
- package/src/adapter/CommandGenerator/CommandGeneratorResolvers_AppSync_NoOp.res +3 -1
- package/src/adapter/CommandGenerator/InboundTranslationResolvers_AppSync.res +1 -4
- package/src/adapter/CommandTopic/CommandTopicChannel_SQS.res +7 -4
- package/src/adapter/CommandTopic/CommandTopicChannel_SQS_Async.res +6 -1
- package/src/adapter/CommandTopic/CommandTopicChannel_SQS_FIFO.res +7 -4
- package/src/adapter/CommandTopic/CommandTopicChannel_SQS_Runtime.res +24 -18
- package/src/adapter/CommandTopic/CommandTopicChannel_SQS_Sync.res +9 -4
- package/src/adapter/CommandTopic/CommandTopicRegistry.res +1 -4
- package/src/adapter/Counter/CounterHandler_DynamoDbStream.res +15 -15
- package/src/adapter/DcbEventLog/DcbEventLogStorage_DynamoDb.res +9 -5
- package/src/adapter/DcbEventLog/DcbEventLogStorage_DynamoDb_Runtime.res +179 -213
- package/src/adapter/EventCollector/EventCollectorChannel_DynamoDbStream.res +5 -5
- package/src/adapter/EventCollector/EventCollectorChannel_Helpers.res +6 -2
- package/src/adapter/EventCollector/EventCollectorChannel_SQS.res +14 -7
- package/src/adapter/EventCollector/EventCollectorChannel_SQS_FIFO.res +14 -7
- package/src/adapter/EventCollector/EventCollectorChannel_SQS_Runtime.res +6 -3
- package/src/adapter/EventLog/EventLogStorage_DynamoDbStream.res +6 -1
- package/src/adapter/EventLog/EventLogStorage_DynamoDb_Runtime.res +13 -17
- package/src/adapter/EventLog/EventLogStorage_Postgres.res +7 -4
- package/src/adapter/EventLogSubscription/EventLogSubscription_AppSync.res +12 -7
- package/src/adapter/EventLogSubscription/EventLogSubscription_AppSync_Ops.res +4 -2
- package/src/adapter/EventTopic/EventTopicPublisher_DynamoDbStream.res +3 -2
- package/src/adapter/EventTopic/EventTopicPublisher_SNS.res +20 -12
- package/src/adapter/EventTopic/EventTopicPublisher_SNS_FIFO.res +21 -9
- package/src/adapter/EventTopic/EventTopicPublisher_SNS_Runtime.res +5 -3
- package/src/adapter/Geocoder/Geocoder_AwsLocation_Resolver.res +6 -3
- package/src/adapter/Mcp/MCP_Lambda.res +31 -46
- package/src/adapter/Messaging/Messaging_Ses_Backend.res +11 -7
- package/src/adapter/Messaging/Messaging_Ses_Backend.res.mjs +2 -4
- package/src/adapter/Monitoring/Monitoring_CloudWatch.res +16 -16
- package/src/adapter/Postgres/PgChangeFeedRelay_Builder.res +5 -10
- package/src/adapter/Postgres/PgChangeFeedRelay_Runtime.res +4 -2
- package/src/adapter/Postgres/PgConnection.res +6 -5
- package/src/adapter/Postgres/PgMigration_Builder.res +4 -8
- package/src/adapter/Postgres/PgRuntime.res +4 -7
- package/src/adapter/QueryDb/PgQueryResolver_Builder.res +9 -11
- package/src/adapter/QueryDb/PgQueryResolver_Lambda.res +22 -21
- package/src/adapter/QueryDb/QueryDbResolvers_AppSync.res +114 -111
- package/src/adapter/QueryDb/QueryDbResolvers_Lambda.res +1 -7
- package/src/adapter/QueryDb/QueryDbStorage.res +4 -2
- package/src/adapter/QueryDb/QueryDbStorage_DynamoDb.res +23 -24
- package/src/adapter/QueryDb/QueryDbStorage_DynamoDbStream.res +8 -2
- package/src/adapter/QueryDb/QueryDbStorage_DynamoDb_Runtime.res +71 -73
- package/src/adapter/QueryDb/QueryDbStorage_Postgres.res +5 -2
- package/src/adapter/QueryDb/QueryInterceptor_Provisioning.res +1 -4
- package/src/adapter/QueryEngine/QueryEngine_DynamoDb.res +8 -2
- package/src/adapter/Runtime/AggregateRuntime_Builder_Micro.res +73 -81
- package/src/adapter/Runtime/AggregateRuntime_Builder_Micro_Async.res +72 -78
- package/src/adapter/Runtime/AggregateRuntime_Builder_PerAggregate.res +44 -67
- package/src/adapter/Runtime/AggregateRuntime_Builder_PerAggregate_Async.res +44 -67
- package/src/adapter/Runtime/AggregateRuntime_Builder_Single.res +53 -82
- package/src/adapter/Runtime/AggregateRuntime_Builder_Single_Async.res +47 -77
- package/src/adapter/Runtime/AutomationSliceEntryPoint_Ops.res +18 -11
- package/src/adapter/Runtime/AutomationSliceRuntime_Builder_Single.res +80 -97
- package/src/adapter/Runtime/CounterEntryPoint_Ops.res +2 -7
- package/src/adapter/Runtime/DcbCommandTopicEntryPoint_Ops.res +16 -10
- package/src/adapter/Runtime/EventCollectorEntryPoint_Ops.res +24 -15
- package/src/adapter/Runtime/EventCollectorRuntime_Builder_PerEventCollector.res +17 -37
- package/src/adapter/Runtime/EventCollectorRuntime_Builder_Single.res +31 -57
- package/src/adapter/Runtime/EventMapperEntryPoint_Ops.res +18 -24
- package/src/adapter/Runtime/ExtensionPointEntryPoint_Ops.res +1 -5
- package/src/adapter/Runtime/ExtensionPointRuntime_Builder_PerExtensionPoint.res +4 -3
- package/src/adapter/Runtime/HeartbeatEntryPoint.res +0 -1
- package/src/adapter/Runtime/PgChangeFeedRelayEntryPoint.res +4 -2
- package/src/adapter/Runtime/PgMigrationEntryPoint.res +0 -1
- package/src/adapter/Runtime/PgQueryResolverEntryPoint_Ops.res +3 -2
- package/src/adapter/Runtime/PluginExtensionPointEntryPoint.res +1 -2
- package/src/adapter/Runtime/ProjectionEntryPoint_Ops.res +4 -5
- package/src/adapter/Runtime/RuntimeEnvironment_Lambda.res +22 -10
- package/src/adapter/Runtime/RuntimeExtensionEntryPoint_Ops.res +8 -2
- package/src/adapter/Runtime/SideEffectHandlerRuntime_Builder_Single.res +14 -31
- package/src/adapter/Runtime/StateChangeSliceRuntime_Builder_Single.res +16 -17
- package/src/adapter/Runtime/StateViewSliceEntryPoint_Ops.res +10 -9
- package/src/adapter/Runtime/StateViewSliceRuntime_Builder_Single.res +46 -52
- package/src/adapter/Runtime/StreamRoutedEntryPoint_Ops.res +36 -41
- package/src/adapter/Runtime/TaskBucketEntryPoint_Ops.res +31 -37
- package/src/adapter/Runtime/TaskRuntime_Builder_PerBucket.res +6 -7
- package/src/adapter/ScheduledPublisher/ScheduledPublisher_CloudWatchEvents.res +4 -6
- package/src/adapter/ScheduledPublisher/ScheduledPublisher_CloudWatchEvents_Runtime.res +2 -6
- package/src/adapter/StateTopic/StateTopic_AppSync.res +21 -16
- package/src/adapter/StateTopic/StateTopic_AppSync_Ops.res +36 -32
- package/src/adapter/Task/TaskBucket_S3.res +6 -1
- package/src/adapter/Upload/Upload_Claim_S3.res +17 -20
- package/src/adapter/Upload/Upload_Claim_S3_Ops.res +5 -2
- package/src/adapter/Upload/Upload_Presign_S3.res +51 -49
- package/src/capability/Capability_Geocoding_AwsLocation.res +4 -1
- package/src/capability/Capability_Messaging.res +4 -5
- package/src/capability/Capability_Messaging_Ses.res +3 -1
- package/src/capability/Capability_ObjectStore_S3.res +1 -7
- package/src/components/Api/AppSync_Adapter.res +28 -41
- package/src/components/Api/AppSync_SdlDecorate.res +38 -30
- package/src/components/AutomationSlice_Builder.res +6 -4
- package/src/components/ExtensionPoint_Builder.res +9 -10
- package/src/components/InboundTranslationSlice_Builder.res +7 -8
- package/src/components/OutboundTranslationSlice_Builder.res +14 -8
- package/src/components/OutboundTranslationSlice_Builder.res.mjs +1 -1
- package/src/components/SideEffectHandler_Single.res +4 -4
- package/src/components/StateViewSlice_Builder.res +6 -4
- package/src/components/StateViewSlice_Builder_Stream.res +6 -4
- package/src/components/Task_Builder_PerBucket.res +10 -11
- package/src/plugin/cloner/ClonerRunner_Fargate.res +3 -10
- package/src/plugin/cloner/ClonerRunner_Fargate_Runtime.res +1 -4
- package/src/plugin/heartbeat/HeartbeatRunner_CloudWatchEvents.res +5 -5
- package/src/plugin/runtime/PluginExtensionPointRuntime_Builder.res +19 -21
- package/src/plugin/runtime/PluginRuntime_Builder.res +167 -188
- package/src/plugin/runtime/Util_PluginMessage_Runtime.res +6 -5
- package/src/plugin/stack/Plugin_Stack.res +104 -103
- package/src/util/Util_AlarmSpec.res +2 -1
- package/src/util/Util_AppSync.res +8 -2
- package/src/util/Util_AppSync_Caller.res +15 -14
- package/src/util/Util_Bundle.res +46 -42
- package/src/util/Util_CognitoGroupUser_Runtime.res +32 -34
- package/src/util/Util_Cognito_Runtime.res +11 -7
- package/src/util/Util_DeadLetterQueue.res +12 -2
- package/src/util/Util_DynamoDb.res +5 -3
- package/src/util/Util_DynamoDbStream.res +7 -3
- package/src/util/Util_DynamoDbStream_Runtime.res +1 -2
- package/src/util/Util_DynamoDb_Runtime.res +23 -41
- package/src/util/Util_HostUiDomain.res +6 -1
- package/src/util/Util_Kinesis_Runtime.res +10 -11
- package/src/util/Util_Lambda.res +12 -5
- package/src/util/Util_LambdaEnvBudget.res +5 -5
- package/src/util/Util_LambdaLogging.res +1 -2
- package/src/util/Util_LogGroup_Adopting.res +1 -4
- package/src/util/Util_ResourceNaming.res +1 -2
- package/src/util/Util_SNS.res +8 -3
- package/src/util/Util_SNS_FIFO.res +1 -1
- package/src/util/Util_SNS_Runtime.res +11 -10
- package/src/util/Util_SQS.res +4 -2
- package/src/util/Util_SQS_FIFO.res +5 -2
- package/src/util/Util_SQS_Runtime.res +37 -36
- package/src/util/Util_ShellConfig.res +3 -2
- package/src/util/Util_StaticBundle.res +1 -3
- package/src/util/Util_StoreLayout.res +4 -1
- package/tests/AWS_TagsTest.res +16 -5
- package/tests/AppSyncEventsSigner_OpsTest.res +11 -2
- package/tests/AppSync_AdapterTest.res +203 -224
- package/tests/AppSync_Resolver_RetryingTest.res +64 -44
- package/tests/AppSync_RetirementNarrowingTest.res +59 -46
- package/tests/AppSync_SdlDecorateTest.res +22 -20
- package/tests/AppSync_SourceApiAssociation_RetryingTest.res +40 -22
- package/tests/Auth_ActiveRolePoolAttachmentTest.res +114 -107
- package/tests/Auth_ActiveRoleStoreTest.res +12 -9
- package/tests/Auth_ActiveRoleStore_SchemaTest.res +2 -1
- package/tests/Auth_ActiveRoleTrigger_OpsTest.res +20 -16
- package/tests/Auth_CognitoTest.res +31 -46
- package/tests/AutomationSliceEntryPoint_OpsTest.res +69 -67
- package/tests/BakeConvergenceTest.res +22 -25
- package/tests/Capability_MessagingTest.res +28 -12
- package/tests/CommandTopicQueuePolicyTest.res +1 -2
- package/tests/CounterEntryPoint_OpsTest.res +3 -7
- package/tests/DcbEventLogStorage_DynamoDb_RuntimeTest.res +306 -210
- package/tests/EventCollectorConnectLambdaPreviewTest.res +14 -7
- package/tests/EventCollectorEntryPoint_OpsTest.res +12 -4
- package/tests/EventCollectorPluginDefinitionAssetTest.res +18 -11
- package/tests/EventCollectorReadyGateTest.res +5 -4
- package/tests/EventLogStorage_DynamoDb_RuntimeTest.res +1 -2
- package/tests/EventMapperEntryPoint_OpsTest.res +1 -3
- package/tests/ExtensionPointEntryPoint_OpsTest.res +1 -4
- package/tests/HandlerFactoryDispatchTest.res +97 -70
- package/tests/MCP_LambdaTest.res +16 -27
- package/tests/PgChangeFeedRelay_IntegrationTest.res +123 -93
- package/tests/PgChangeFeedRelay_RuntimeTest.res +13 -10
- package/tests/PgMigrationEntryPoint_IntegrationTest.res +0 -1
- package/tests/PgPipeline_IntegrationTest.res +80 -66
- package/tests/PgQueryDbUnion_IntegrationTest.res +1 -2
- package/tests/PgQueryResolverEntryPoint_OpsTest.res +2 -6
- package/tests/PgQueryResolver_LambdaTest.res +41 -24
- package/tests/Platform_AdminScan_OpsTest.res +4 -1
- package/tests/Platform_ComponentDefinitions_Lambda_OpsTest.res +137 -129
- package/tests/ProjectionEntryPoint_DisplayNameTest.res +7 -3
- package/tests/ProjectionEntryPoint_UnionStampTest.res +3 -2
- package/tests/ProvisionIdentityTest.res +2 -6
- package/tests/QueryDbOwnerIndexTableTest.res +6 -4
- package/tests/QueryDbResolvers_AppSyncTest.res +23 -16
- package/tests/QueryInterceptor_LambdaTest.res +11 -13
- package/tests/ReadModelEntryPoint_OpsTest.res +43 -41
- package/tests/RuntimeExtensionEntryPoint_OpsTest.res +4 -3
- package/tests/SideEffectEntryPoint_OpsTest.res +23 -18
- package/tests/StateChangeDescriptorParityTest.res +28 -26
- package/tests/StateTopicKeySchemaTest.res +18 -15
- package/tests/StateViewSliceEntryPoint_OpsTest.res +55 -44
- package/tests/TaskBucketEntryPoint_OpsTest.res +13 -13
- package/tests/Upload_ClaimTest.res +3 -3
- package/tests/Util_AwsErrorTest.res +4 -7
- package/tests/Util_BundleDependencyClosureTest.res +15 -3
- package/tests/Util_BundleEsmLoaderTest.res +3 -3
- package/tests/Util_BundleResolveTest.res +4 -6
- package/tests/Util_BundleRuntimeExtensionTest.res +17 -18
- package/tests/Util_DcbMetricsTest.res +6 -6
- package/tests/Util_DynamoDbStreamTest.res +4 -5
- package/tests/Util_DynamoDbTest.res +3 -2
- package/tests/Util_LambdaEnvBudgetTest.res +22 -18
- package/tests/Util_LambdaLoggingTest.res +9 -8
- package/tests/Util_LogGroup_AdoptingTest.res +10 -5
- package/tests/Util_LogRetentionTest.res +6 -2
- package/tests/Util_OwnerScopeEnvTest.res +10 -8
- package/tests/Util_SQS_RuntimeTest.res +5 -7
- package/tests/Util_ShellConfigTest.res +7 -17
- package/tests/Util_StaticBundleTest.res +3 -8
- package/tests/Util_StoreLayoutTest.res +17 -12
- package/tests/integration/AggregateEntryPoint_IntegrationTest.res +1 -4
- package/tests/integration/DcbCommandTopicEntryPoint_IntegrationTest.res +15 -15
- package/tests/integration/DcbEventLogStorage_DynamoDb_IntegrationTest.res +296 -232
- package/tests/integration/DcbIntegrationHarness.res +8 -8
- package/tests/integration/EpCompositeSlice.res +16 -8
- package/tests/integration/QueryDbUnion_DynamoDb_IntegrationTest.res +8 -10
- package/tests/integration/StateViewSliceEntryPoint_IntegrationTest.res +10 -10
- package/tests/integration/SvsTestSlice_Projection.res +1 -3
- package/tests/util/Util_AlarmSpecTest.res +62 -49
|
@@ -34,10 +34,9 @@ let make: ReventlessCore.DcbEventLog_Adapter.storageMaker = (
|
|
|
34
34
|
let globalSecondaryIndexes =
|
|
35
35
|
indexes
|
|
36
36
|
->Array.map(indexName => {
|
|
37
|
-
let projectionType =
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
: PulumiAws.DynamoDb.Table.KEYS_ONLY
|
|
37
|
+
let projectionType = DcbEventLogStorage_DynamoDb_Runtime.indexKeepsFullProjection(indexName)
|
|
38
|
+
? PulumiAws.DynamoDb.Table.ALL
|
|
39
|
+
: PulumiAws.DynamoDb.Table.KEYS_ONLY
|
|
41
40
|
{
|
|
42
41
|
PulumiAws.DynamoDb.Table.name: indexName,
|
|
43
42
|
hashKey: indexName,
|
|
@@ -50,7 +49,12 @@ let make: ReventlessCore.DcbEventLog_Adapter.storageMaker = (
|
|
|
50
49
|
|
|
51
50
|
// Create DynamoDB table with stream enabled — EventTopicPublisher_DynamoDbStream
|
|
52
51
|
// needs a DynamoDbStream resource to connect the EventTopic.
|
|
53
|
-
let tags = AWS.Tags.make(
|
|
52
|
+
let tags = AWS.Tags.make(
|
|
53
|
+
~name,
|
|
54
|
+
~kind=ReventlessCore.ComponentType.Plugin,
|
|
55
|
+
~role=DcbEventLog,
|
|
56
|
+
~scope=Plugin,
|
|
57
|
+
)
|
|
54
58
|
let table = Util_DynamoDbStream.makeTable(
|
|
55
59
|
name,
|
|
56
60
|
~attributes,
|
|
@@ -78,8 +78,7 @@ let derivePartitionKey = (
|
|
|
78
78
|
| None => tags->Array.getUnsafe(0)
|
|
79
79
|
}
|
|
80
80
|
`${tag.key}:${tag.value}`
|
|
81
|
-
| Some(Composite(spec)) =>
|
|
82
|
-
Reventless.DcbTag.getCompositePartitionKeyValue(tags, spec)
|
|
81
|
+
| Some(Composite(spec)) => Reventless.DcbTag.getCompositePartitionKeyValue(tags, spec)
|
|
83
82
|
}
|
|
84
83
|
}
|
|
85
84
|
}
|
|
@@ -102,10 +101,7 @@ let toItem = (
|
|
|
102
101
|
|
|
103
102
|
// Add tags array
|
|
104
103
|
let tagsJson = event.tags->Array.map(tag =>
|
|
105
|
-
[
|
|
106
|
-
("key", tag.key->JSON.Encode.string),
|
|
107
|
-
("value", tag.value->JSON.Encode.string),
|
|
108
|
-
]
|
|
104
|
+
[("key", tag.key->JSON.Encode.string), ("value", tag.value->JSON.Encode.string)]
|
|
109
105
|
->Dict.fromArray
|
|
110
106
|
->JSON.Encode.object
|
|
111
107
|
)
|
|
@@ -150,36 +146,39 @@ let fromItem = (item: JSON.t): ReventlessCore.DcbEventLog_Adapter.rawSequencedEv
|
|
|
150
146
|
switch item->JSON.Decode.object {
|
|
151
147
|
| None => JsError.throwWithMessage("Invalid DcbEventLog item: not an object")
|
|
152
148
|
| Some(obj) =>
|
|
153
|
-
let position =
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
149
|
+
let position =
|
|
150
|
+
obj
|
|
151
|
+
->Dict.get("position")
|
|
152
|
+
->Option.flatMap(JSON.Decode.string)
|
|
153
|
+
->Option.getOrThrow
|
|
157
154
|
|
|
158
|
-
let eventType =
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
155
|
+
let eventType =
|
|
156
|
+
obj
|
|
157
|
+
->Dict.get("event")
|
|
158
|
+
->Option.flatMap(JSON.Decode.string)
|
|
159
|
+
->Option.getOrThrow
|
|
162
160
|
|
|
163
161
|
let data = obj->Dict.get("data")->Option.getOrThrow
|
|
164
162
|
|
|
165
|
-
let tags =
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
163
|
+
let tags =
|
|
164
|
+
obj
|
|
165
|
+
->Dict.get("tags")
|
|
166
|
+
->Option.flatMap(JSON.Decode.array)
|
|
167
|
+
->Option.mapOr([], tagArray =>
|
|
168
|
+
tagArray->Array.filterMap(tagJson =>
|
|
169
|
+
switch tagJson->JSON.Decode.object {
|
|
170
|
+
| None => None
|
|
171
|
+
| Some(tagObj) => {
|
|
172
|
+
let key = tagObj->Dict.get("key")->Option.flatMap(JSON.Decode.string)
|
|
173
|
+
let value = tagObj->Dict.get("value")->Option.flatMap(JSON.Decode.string)
|
|
174
|
+
switch (key, value) {
|
|
175
|
+
| (Some(k), Some(v)) => Some({Reventless.DcbTag.key: k, value: v})
|
|
176
|
+
| _ => None
|
|
177
|
+
}
|
|
178
178
|
}
|
|
179
179
|
}
|
|
180
|
-
|
|
180
|
+
)
|
|
181
181
|
)
|
|
182
|
-
)
|
|
183
182
|
|
|
184
183
|
let recordedAt =
|
|
185
184
|
obj
|
|
@@ -213,9 +212,7 @@ let queryByCompositeTags = async (
|
|
|
213
212
|
let composite = compositeTagKey(tags)
|
|
214
213
|
let indexName = "tag_composite"
|
|
215
214
|
|
|
216
|
-
let expressionAttributeValues = Dict.fromArray([
|
|
217
|
-
(":composite", composite->JSON.Encode.string),
|
|
218
|
-
])
|
|
215
|
+
let expressionAttributeValues = Dict.fromArray([(":composite", composite->JSON.Encode.string)])
|
|
219
216
|
|
|
220
217
|
let (keyConditionExpression, expressionAttributeNames) = switch after {
|
|
221
218
|
| None => ("tag_composite = :composite", None)
|
|
@@ -227,9 +224,9 @@ let queryByCompositeTags = async (
|
|
|
227
224
|
|
|
228
225
|
let queryParams: QueryCommand.input = {
|
|
229
226
|
tableName: table.name,
|
|
230
|
-
indexName
|
|
231
|
-
keyConditionExpression
|
|
232
|
-
expressionAttributeValues
|
|
227
|
+
indexName,
|
|
228
|
+
keyConditionExpression,
|
|
229
|
+
expressionAttributeValues,
|
|
233
230
|
?expressionAttributeNames,
|
|
234
231
|
}
|
|
235
232
|
|
|
@@ -315,9 +312,7 @@ let buildQueryByPartitionKeyInput = (
|
|
|
315
312
|
~after: option<string>=?,
|
|
316
313
|
~strongConsistency: bool=false,
|
|
317
314
|
): QueryCommand.input => {
|
|
318
|
-
let expressionAttributeValues = Dict.fromArray([
|
|
319
|
-
(":pk", partitionKey->JSON.Encode.string),
|
|
320
|
-
])
|
|
315
|
+
let expressionAttributeValues = Dict.fromArray([(":pk", partitionKey->JSON.Encode.string)])
|
|
321
316
|
let (keyConditionExpression, expressionAttributeNames) = switch after {
|
|
322
317
|
| None => ("id = :pk", None)
|
|
323
318
|
| Some(afterPos) => {
|
|
@@ -401,16 +396,13 @@ let queryBySingleTagCrossPartitionStreamIndexed = (
|
|
|
401
396
|
expressionAttributeNames,
|
|
402
397
|
}
|
|
403
398
|
Stream.paginateEffect((None: option<dict<JSON.t>>), cursor =>
|
|
404
|
-
Effect.tryPromise(
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
QueryCommand.send(params->QueryCommand.make)
|
|
412
|
-
},
|
|
413
|
-
)
|
|
399
|
+
Effect.tryPromise(~catch=DynamoDb_Error.classify, () => {
|
|
400
|
+
let params = switch cursor {
|
|
401
|
+
| None => baseParams
|
|
402
|
+
| Some(key) => {...baseParams, exclusiveStartKey: key}
|
|
403
|
+
}
|
|
404
|
+
QueryCommand.send(params->QueryCommand.make)
|
|
405
|
+
})
|
|
414
406
|
->Effect.retry(DynamoDb_Error.retrySchedule)
|
|
415
407
|
->Effect.catchAll(err => Effect.fail(DynamoDb_Error.message(err)))
|
|
416
408
|
->Effect.map(result => (
|
|
@@ -458,12 +450,10 @@ let executeQueryItem = async (
|
|
|
458
450
|
await queryBySingleTagCrossPartitionStream(table, tag, ~after?)
|
|
459
451
|
->Stream.runCollect
|
|
460
452
|
->Effect.runPromise
|
|
461
|
-
| Some([tag]) =>
|
|
462
|
-
await queryByPartitionKey(table, `${tag.key}:${tag.value}`, ~after?)
|
|
453
|
+
| Some([tag]) => await queryByPartitionKey(table, `${tag.key}:${tag.value}`, ~after?)
|
|
463
454
|
|
|
464
455
|
// Multiple tags: use composite GSI
|
|
465
|
-
| Some(tags) if tags->Array.length > 1 =>
|
|
466
|
-
await queryByCompositeTags(table, tags, ~after?)
|
|
456
|
+
| Some(tags) if tags->Array.length > 1 => await queryByCompositeTags(table, tags, ~after?)
|
|
467
457
|
|
|
468
458
|
// No tags (or empty): fall back to scan
|
|
469
459
|
| None | Some([]) | Some(_) =>
|
|
@@ -513,17 +503,13 @@ let initSlot = (
|
|
|
513
503
|
Queue.bounded(1)->Effect.flatMap(queue => {
|
|
514
504
|
let producer =
|
|
515
505
|
stream
|
|
516
|
-
->Stream.runForEach(event =>
|
|
517
|
-
Queue.offer(queue, Some(event))->Effect.map(_ => ())
|
|
518
|
-
)
|
|
506
|
+
->Stream.runForEach(event => Queue.offer(queue, Some(event))->Effect.map(_ => ()))
|
|
519
507
|
// Always push the None sentinel so the merge loop can detect exhaustion,
|
|
520
508
|
// even if the stream fails (stream error is suppressed to prevent an
|
|
521
509
|
// unhandled-fiber-failure warning).
|
|
522
510
|
->Effect.ensuring(Queue.offer(queue, None)->Effect.map(_ => ()))
|
|
523
|
-
->Effect.catchAll(_ => Effect.succeed(
|
|
524
|
-
Effect.fork(producer)->Effect.flatMap(_ =>
|
|
525
|
-
Queue.take(queue)->Effect.map(head => {queue, head})
|
|
526
|
-
)
|
|
511
|
+
->Effect.catchAll(_ => Effect.succeed())
|
|
512
|
+
Effect.fork(producer)->Effect.flatMap(_ => Queue.take(queue)->Effect.map(head => {queue, head}))
|
|
527
513
|
})
|
|
528
514
|
|
|
529
515
|
// Returns the index of the slot with the smallest position, or None if all exhausted.
|
|
@@ -536,8 +522,7 @@ let findMinSlotIdx = (slots: array<mergeSlot>) =>
|
|
|
536
522
|
let minSlot = slots->Array.getUnsafe(minIdx)
|
|
537
523
|
switch minSlot.head {
|
|
538
524
|
| None => Some(idx)
|
|
539
|
-
| Some(minHead) =>
|
|
540
|
-
String.compare(head.position, minHead.position) < 0. ? Some(idx) : minOpt
|
|
525
|
+
| Some(minHead) => String.compare(head.position, minHead.position) < 0. ? Some(idx) : minOpt
|
|
541
526
|
}
|
|
542
527
|
}
|
|
543
528
|
)
|
|
@@ -550,7 +535,7 @@ let dedupByPosition = (
|
|
|
550
535
|
let lastPos: ref<option<string>> = ref(None)
|
|
551
536
|
stream->Stream.filter(event =>
|
|
552
537
|
switch lastPos.contents {
|
|
553
|
-
| Some(pos)
|
|
538
|
+
| Some(pos) if pos == event.position => false
|
|
554
539
|
| _ =>
|
|
555
540
|
lastPos := Some(event.position)
|
|
556
541
|
true
|
|
@@ -576,17 +561,20 @@ let mergeSortedEvents = (
|
|
|
576
561
|
| Some(idx) =>
|
|
577
562
|
let slot = slots->Array.getUnsafe(idx)
|
|
578
563
|
let emitEvent = slot.head->Option.getOrThrow
|
|
579
|
-
Queue.take(slot.queue)->Effect.map(
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
564
|
+
Queue.take(slot.queue)->Effect.map(
|
|
565
|
+
nextHead => {
|
|
566
|
+
let newSlots = slots->Array.mapWithIndex(
|
|
567
|
+
(s, i) =>
|
|
568
|
+
if i == idx {
|
|
569
|
+
{...s, head: nextHead}
|
|
570
|
+
} else {
|
|
571
|
+
s
|
|
572
|
+
},
|
|
573
|
+
)
|
|
574
|
+
let hasActive = newSlots->Array.some(s => s.head->Option.isSome)
|
|
575
|
+
([emitEvent], hasActive ? Some(newSlots) : None)
|
|
576
|
+
},
|
|
577
|
+
)
|
|
590
578
|
}
|
|
591
579
|
)
|
|
592
580
|
)
|
|
@@ -594,10 +582,7 @@ let mergeSortedEvents = (
|
|
|
594
582
|
// --- Main Operations ---
|
|
595
583
|
|
|
596
584
|
let read = (table: resolvedTable, ~crossPartitionTagKeys: array<string>=[]) =>
|
|
597
|
-
async (
|
|
598
|
-
~query: Reventless.DcbTag.query,
|
|
599
|
-
~after=?,
|
|
600
|
-
) => {
|
|
585
|
+
async (~query: Reventless.DcbTag.query, ~after=?) => {
|
|
601
586
|
// Execute queries for each queryItem
|
|
602
587
|
let queryResults = await query
|
|
603
588
|
->Array.map(queryItem => executeQueryItem(table, queryItem, ~after?, ~crossPartitionTagKeys))
|
|
@@ -609,15 +594,15 @@ let read = (table: resolvedTable, ~crossPartitionTagKeys: array<string>=[]) =>
|
|
|
609
594
|
let deduplicatedEvents = deduplicateByPosition(allEvents)
|
|
610
595
|
|
|
611
596
|
// Sort by position
|
|
612
|
-
let sortedEvents =
|
|
613
|
-
String.compare(a.position, b.position)
|
|
614
|
-
)
|
|
597
|
+
let sortedEvents =
|
|
598
|
+
deduplicatedEvents->Array.toSorted((a, b) => String.compare(a.position, b.position))
|
|
615
599
|
|
|
616
600
|
// Get head position (latest position)
|
|
617
|
-
let headPosition =
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
601
|
+
let headPosition =
|
|
602
|
+
sortedEvents
|
|
603
|
+
->Array.toReversed
|
|
604
|
+
->Array.at(0)
|
|
605
|
+
->Option.map(event => event.position)
|
|
621
606
|
|
|
622
607
|
{
|
|
623
608
|
ReventlessCore.DcbEventLog_Adapter.events: sortedEvents,
|
|
@@ -679,9 +664,9 @@ let collectQueryTags = (query: Reventless.DcbTag.query): array<Reventless.DcbTag
|
|
|
679
664
|
acc
|
|
680
665
|
}
|
|
681
666
|
|
|
682
|
-
let collectEventTags = (
|
|
683
|
-
|
|
684
|
-
|
|
667
|
+
let collectEventTags = (events: array<ReventlessCore.DcbEventLog_Adapter.rawStoredEvent>): array<
|
|
668
|
+
Reventless.DcbTag.tag,
|
|
669
|
+
> => {
|
|
685
670
|
let seen = Set.make()
|
|
686
671
|
let acc = []
|
|
687
672
|
events->Array.forEach(event =>
|
|
@@ -722,9 +707,9 @@ let dedupStrings = (xs: array<string>): array<string> => {
|
|
|
722
707
|
// tagKey ("key:value") -> distinct event types among `events` that CARRY the tag.
|
|
723
708
|
// Used to advance the fences of composite query tags and cross-partition tags
|
|
724
709
|
// (bumped by every carrier), and by `appendUnconditional`.
|
|
725
|
-
let carriedTypesByTag = (
|
|
726
|
-
|
|
727
|
-
|
|
710
|
+
let carriedTypesByTag = (events: array<ReventlessCore.DcbEventLog_Adapter.rawStoredEvent>): Dict.t<
|
|
711
|
+
array<string>,
|
|
712
|
+
> => {
|
|
728
713
|
let d = Dict.make()
|
|
729
714
|
events->Array.forEach(e =>
|
|
730
715
|
e.tags->Array.forEach((t: Reventless.DcbTag.tag) => {
|
|
@@ -897,7 +882,7 @@ let buildEventPuts = (
|
|
|
897
882
|
let recordedAt = ReventlessCore.Message.nowAsISOString()
|
|
898
883
|
let item = toItem(position, event, ~partitionTag?, ~recordedAt)
|
|
899
884
|
let put: TransactWriteCommand.put = {
|
|
900
|
-
TransactWriteCommand.item
|
|
885
|
+
TransactWriteCommand.item,
|
|
901
886
|
tableName: table.name,
|
|
902
887
|
}
|
|
903
888
|
{TransactWriteCommand.put: put}
|
|
@@ -908,7 +893,9 @@ let runTransactWrite = async (
|
|
|
908
893
|
basePosition: string,
|
|
909
894
|
~errorPrefix: string,
|
|
910
895
|
) =>
|
|
911
|
-
await Effect.tryPromise(~catch=DynamoDb_Error.classify, () =>
|
|
896
|
+
await Effect.tryPromise(~catch=DynamoDb_Error.classify, () =>
|
|
897
|
+
input->TransactWriteCommand.make->TransactWriteCommand.send
|
|
898
|
+
)
|
|
912
899
|
->Effect.map(_ => Ok(basePosition))
|
|
913
900
|
->Effect.catchAll(err =>
|
|
914
901
|
switch err {
|
|
@@ -945,20 +932,19 @@ let appendUnconditional = async (
|
|
|
945
932
|
let basePosition = generatePosition()
|
|
946
933
|
let carriedMap = carriedTypesByTag(events)
|
|
947
934
|
let putItems = buildEventPuts(table, events, basePosition, ~partitionTag?)
|
|
948
|
-
let updateItems =
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
})
|
|
935
|
+
let updateItems = eventTags->Array.filterMap(tag => {
|
|
936
|
+
let producedTypes = carriedMap->Dict.get(`${tag.key}:${tag.value}`)->Option.getOr([])
|
|
937
|
+
producedTypes->Array.length == 0
|
|
938
|
+
? None
|
|
939
|
+
: Some({
|
|
940
|
+
TransactWriteCommand.update: buildUnconditionalFenceUpdate(
|
|
941
|
+
table.name,
|
|
942
|
+
tag,
|
|
943
|
+
~producedTypes,
|
|
944
|
+
~newPosition=basePosition,
|
|
945
|
+
),
|
|
946
|
+
})
|
|
947
|
+
})
|
|
962
948
|
let input: TransactWriteCommand.input = {
|
|
963
949
|
transactItems: Array.concat(putItems, updateItems),
|
|
964
950
|
}
|
|
@@ -1002,7 +988,8 @@ let eventPartitionTags = (
|
|
|
1002
988
|
| Some(Simple(pt)) =>
|
|
1003
989
|
switch event.tags->Array.find(t => t.key == pt.key) {
|
|
1004
990
|
| Some(t) => [t]
|
|
1005
|
-
| None =>
|
|
991
|
+
| None =>
|
|
992
|
+
switch event.tags->Array.get(0) {
|
|
1006
993
|
| Some(t) => [t]
|
|
1007
994
|
| None => []
|
|
1008
995
|
}
|
|
@@ -1246,54 +1233,51 @@ let buildConditionalTransactItems = (
|
|
|
1246
1233
|
})
|
|
1247
1234
|
|
|
1248
1235
|
let putItems = buildEventPuts(table, events, basePosition, ~partitionTag?)
|
|
1249
|
-
let updateItems =
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
),
|
|
1295
|
-
})
|
|
1296
|
-
})
|
|
1236
|
+
let updateItems = conditionalUpdateTags->Array.filterMap(tag => {
|
|
1237
|
+
let producedTypes = producedTypesFor(tag)
|
|
1238
|
+
// A conditional Update must advance ≥1 produced type; partition / composite /
|
|
1239
|
+
// cross-partition carriers always have one. If none (a misconfigured clause),
|
|
1240
|
+
// skip rather than emit an empty `SET`.
|
|
1241
|
+
producedTypes->Array.length == 0
|
|
1242
|
+
? None
|
|
1243
|
+
: Some({
|
|
1244
|
+
TransactWriteCommand.update: buildConditionalFenceUpdate(
|
|
1245
|
+
table.name,
|
|
1246
|
+
tag,
|
|
1247
|
+
~consumedTypes=consumedTypesFor(tag),
|
|
1248
|
+
~producedTypes,
|
|
1249
|
+
~newPosition=basePosition,
|
|
1250
|
+
~after=cond.after,
|
|
1251
|
+
),
|
|
1252
|
+
})
|
|
1253
|
+
})
|
|
1254
|
+
let checkItems = conditionCheckTags->Array.filterMap(tag => {
|
|
1255
|
+
let consumedTypes = consumedTypesFor(tag)
|
|
1256
|
+
// A vacuous clause (no consumed type carries the tag) matches nothing — skip.
|
|
1257
|
+
consumedTypes->Array.length == 0
|
|
1258
|
+
? None
|
|
1259
|
+
: Some({
|
|
1260
|
+
TransactWriteCommand.conditionCheck: buildFenceConditionCheck(
|
|
1261
|
+
table.name,
|
|
1262
|
+
tag,
|
|
1263
|
+
~consumedTypes,
|
|
1264
|
+
~after=cond.after,
|
|
1265
|
+
),
|
|
1266
|
+
})
|
|
1267
|
+
})
|
|
1268
|
+
let bumpItems = bumpTags->Array.filterMap(tag => {
|
|
1269
|
+
let producedTypes = producedTypesFor(tag)
|
|
1270
|
+
producedTypes->Array.length == 0
|
|
1271
|
+
? None
|
|
1272
|
+
: Some({
|
|
1273
|
+
TransactWriteCommand.update: buildUnconditionalFenceUpdate(
|
|
1274
|
+
table.name,
|
|
1275
|
+
tag,
|
|
1276
|
+
~producedTypes,
|
|
1277
|
+
~newPosition=basePosition,
|
|
1278
|
+
),
|
|
1279
|
+
})
|
|
1280
|
+
})
|
|
1297
1281
|
|
|
1298
1282
|
Array.concat(putItems, Array.concat(updateItems, Array.concat(checkItems, bumpItems)))
|
|
1299
1283
|
}
|
|
@@ -1315,7 +1299,14 @@ let appendConditional = async (
|
|
|
1315
1299
|
)
|
|
1316
1300
|
} else {
|
|
1317
1301
|
let basePosition = generatePosition()
|
|
1318
|
-
let transactItems = buildConditionalTransactItems(
|
|
1302
|
+
let transactItems = buildConditionalTransactItems(
|
|
1303
|
+
table,
|
|
1304
|
+
events,
|
|
1305
|
+
cond,
|
|
1306
|
+
basePosition,
|
|
1307
|
+
~partitionTag?,
|
|
1308
|
+
~crossPartitionTagKeys,
|
|
1309
|
+
)
|
|
1319
1310
|
let totalItems = transactItems->Array.length
|
|
1320
1311
|
if totalItems > transactWriteItemsLimit {
|
|
1321
1312
|
Error(
|
|
@@ -1331,13 +1322,11 @@ let appendConditional = async (
|
|
|
1331
1322
|
}
|
|
1332
1323
|
|
|
1333
1324
|
let append = (table: resolvedTable, ~partitionTag=?, ~crossPartitionTagKeys: array<string>=[]) =>
|
|
1334
|
-
async (
|
|
1335
|
-
events: array<ReventlessCore.DcbEventLog_Adapter.rawStoredEvent>,
|
|
1336
|
-
~condition=?,
|
|
1337
|
-
) => {
|
|
1325
|
+
async (events: array<ReventlessCore.DcbEventLog_Adapter.rawStoredEvent>, ~condition=?) => {
|
|
1338
1326
|
switch condition {
|
|
1339
1327
|
| None => await appendUnconditional(table, events, ~partitionTag?)
|
|
1340
|
-
| Some(cond) =>
|
|
1328
|
+
| Some(cond) =>
|
|
1329
|
+
await appendConditional(table, events, cond, ~partitionTag?, ~crossPartitionTagKeys)
|
|
1341
1330
|
}
|
|
1342
1331
|
}
|
|
1343
1332
|
|
|
@@ -1349,23 +1338,15 @@ let queryByPartitionKeyStream = (
|
|
|
1349
1338
|
~after: option<string>=?,
|
|
1350
1339
|
~strongConsistency: bool=false,
|
|
1351
1340
|
) => {
|
|
1352
|
-
let baseParams = buildQueryByPartitionKeyInput(
|
|
1353
|
-
table,
|
|
1354
|
-
partitionKey,
|
|
1355
|
-
~after?,
|
|
1356
|
-
~strongConsistency,
|
|
1357
|
-
)
|
|
1341
|
+
let baseParams = buildQueryByPartitionKeyInput(table, partitionKey, ~after?, ~strongConsistency)
|
|
1358
1342
|
Stream.paginateEffect((None: option<dict<JSON.t>>), cursor =>
|
|
1359
|
-
Effect.tryPromise(
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
QueryCommand.send(params->QueryCommand.make)
|
|
1367
|
-
},
|
|
1368
|
-
)
|
|
1343
|
+
Effect.tryPromise(~catch=DynamoDb_Error.classify, () => {
|
|
1344
|
+
let params = switch cursor {
|
|
1345
|
+
| None => baseParams
|
|
1346
|
+
| Some(key) => {...baseParams, exclusiveStartKey: key}
|
|
1347
|
+
}
|
|
1348
|
+
QueryCommand.send(params->QueryCommand.make)
|
|
1349
|
+
})
|
|
1369
1350
|
->Effect.retry(DynamoDb_Error.retrySchedule)
|
|
1370
1351
|
->Effect.catchAll(err => Effect.fail(DynamoDb_Error.message(err)))
|
|
1371
1352
|
->Effect.map(result => (
|
|
@@ -1381,9 +1362,7 @@ let queryByCompositeTagsStream = (
|
|
|
1381
1362
|
~after: option<string>=?,
|
|
1382
1363
|
) => {
|
|
1383
1364
|
let composite = compositeTagKey(tags)
|
|
1384
|
-
let expressionAttributeValues = Dict.fromArray([
|
|
1385
|
-
(":composite", composite->JSON.Encode.string),
|
|
1386
|
-
])
|
|
1365
|
+
let expressionAttributeValues = Dict.fromArray([(":composite", composite->JSON.Encode.string)])
|
|
1387
1366
|
let (keyConditionExpression, expressionAttributeNames) = switch after {
|
|
1388
1367
|
| None => ("tag_composite = :composite", None)
|
|
1389
1368
|
| Some(afterPos) => {
|
|
@@ -1394,21 +1373,18 @@ let queryByCompositeTagsStream = (
|
|
|
1394
1373
|
let baseParams: QueryCommand.input = {
|
|
1395
1374
|
tableName: table.name,
|
|
1396
1375
|
indexName: "tag_composite",
|
|
1397
|
-
keyConditionExpression
|
|
1398
|
-
expressionAttributeValues
|
|
1376
|
+
keyConditionExpression,
|
|
1377
|
+
expressionAttributeValues,
|
|
1399
1378
|
?expressionAttributeNames,
|
|
1400
1379
|
}
|
|
1401
1380
|
Stream.paginateEffect((None: option<dict<JSON.t>>), cursor =>
|
|
1402
|
-
Effect.tryPromise(
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
QueryCommand.send(params->QueryCommand.make)
|
|
1410
|
-
},
|
|
1411
|
-
)
|
|
1381
|
+
Effect.tryPromise(~catch=DynamoDb_Error.classify, () => {
|
|
1382
|
+
let params = switch cursor {
|
|
1383
|
+
| None => baseParams
|
|
1384
|
+
| Some(key) => {...baseParams, exclusiveStartKey: key}
|
|
1385
|
+
}
|
|
1386
|
+
QueryCommand.send(params->QueryCommand.make)
|
|
1387
|
+
})
|
|
1412
1388
|
->Effect.retry(DynamoDb_Error.retrySchedule)
|
|
1413
1389
|
->Effect.catchAll(err => Effect.fail(DynamoDb_Error.message(err)))
|
|
1414
1390
|
->Effect.map(result => (
|
|
@@ -1435,16 +1411,13 @@ let scanWithFilterStream = (
|
|
|
1435
1411
|
// position cannot appear in FilterExpression (it is the table's sort key).
|
|
1436
1412
|
// Filter by position in application code after each page is fetched.
|
|
1437
1413
|
let baseStream = Stream.paginateEffect((None: option<dict<JSON.t>>), cursor =>
|
|
1438
|
-
Effect.tryPromise(
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
ScanCommand.send(ScanCommand.make(params))
|
|
1446
|
-
},
|
|
1447
|
-
)
|
|
1414
|
+
Effect.tryPromise(~catch=DynamoDb_Error.classify, () => {
|
|
1415
|
+
let params = switch cursor {
|
|
1416
|
+
| None => baseParams
|
|
1417
|
+
| Some(key) => {...baseParams, exclusiveStartKey: key}
|
|
1418
|
+
}
|
|
1419
|
+
ScanCommand.send(ScanCommand.make(params))
|
|
1420
|
+
})
|
|
1448
1421
|
->Effect.retry(DynamoDb_Error.retrySchedule)
|
|
1449
1422
|
->Effect.catchAll(err => Effect.fail(DynamoDb_Error.message(err)))
|
|
1450
1423
|
->Effect.map(result => (
|
|
@@ -1483,16 +1456,10 @@ let executeQueryItemStream = (
|
|
|
1483
1456
|
| Some([tag]) if crossPartitionTagKeys->Array.includes(tag.key) =>
|
|
1484
1457
|
queryBySingleTagCrossPartitionStream(table, tag, ~after?)
|
|
1485
1458
|
| Some([tag]) =>
|
|
1486
|
-
queryByPartitionKeyStream(
|
|
1487
|
-
table,
|
|
1488
|
-
`${tag.key}:${tag.value}`,
|
|
1489
|
-
~after?,
|
|
1490
|
-
~strongConsistency,
|
|
1491
|
-
)
|
|
1459
|
+
queryByPartitionKeyStream(table, `${tag.key}:${tag.value}`, ~after?, ~strongConsistency)
|
|
1492
1460
|
|
|
1493
1461
|
// Multiple tags: use composite GSI
|
|
1494
|
-
| Some(tags) if tags->Array.length > 1 =>
|
|
1495
|
-
queryByCompositeTagsStream(table, tags, ~after?)
|
|
1462
|
+
| Some(tags) if tags->Array.length > 1 => queryByCompositeTagsStream(table, tags, ~after?)
|
|
1496
1463
|
|
|
1497
1464
|
// No tags (or empty): fall back to scan
|
|
1498
1465
|
| None | Some([]) | Some(_) =>
|
|
@@ -1511,7 +1478,7 @@ let readStream = (table: resolvedTable, ~crossPartitionTagKeys: array<string>=[]
|
|
|
1511
1478
|
)
|
|
1512
1479
|
switch streams->Array.length {
|
|
1513
1480
|
| 0 => Stream.empty
|
|
1514
|
-
| 1 =>
|
|
1481
|
+
| 1 => streams->Array.getUnsafe(0)->Stream.map(fromItem)
|
|
1515
1482
|
| _ =>
|
|
1516
1483
|
// Scan sub-queries (tags = None) return items in unspecified order, so
|
|
1517
1484
|
// a lazy k-way merge is not possible. Fall back to eager collect + sort.
|
|
@@ -1531,8 +1498,7 @@ let readStream = (table: resolvedTable, ~crossPartitionTagKeys: array<string>=[]
|
|
|
1531
1498
|
// All sub-streams are tag-based GSI queries — items arrive in position
|
|
1532
1499
|
// order (rangeKey = "position"). Merge lazily: DynamoDB pages are only
|
|
1533
1500
|
// fetched as the consumer requests more elements.
|
|
1534
|
-
mergeSortedEvents(streams->Array.map(s => s->Stream.map(fromItem)))
|
|
1535
|
-
->dedupByPosition
|
|
1501
|
+
mergeSortedEvents(streams->Array.map(s => s->Stream.map(fromItem)))->dedupByPosition
|
|
1536
1502
|
}
|
|
1537
1503
|
}
|
|
1538
1504
|
}
|
|
@@ -27,11 +27,11 @@ let connect = (
|
|
|
27
27
|
lambda->connectLambda(name, lambdaRole, queues, eventTopics, resources, opts)
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
let make: ReventlessCore.EventCollector_Adapter.channelMaker<
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
) => {
|
|
30
|
+
let make: ReventlessCore.EventCollector_Adapter.channelMaker<
|
|
31
|
+
callbackEvent,
|
|
32
|
+
'context,
|
|
33
|
+
channelParts,
|
|
34
|
+
> = (~name as _, ~eventTopics, ~owner as _, ~opts as _) => {
|
|
35
35
|
// Postgres-backed event logs have NO storage resources (no table, no stream) —
|
|
36
36
|
// skip them instead of producing undefined entries; their events arrive via the
|
|
37
37
|
// PgProjectionFeed SQS queue (B3.0), not a stream ESM.
|