@reventlessdev/reventless-core 3.0.0-alpha.138 → 3.0.0-alpha.139
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 +7 -0
- package/package.json +4 -4
- package/src/components/DcbEventLog/DcbEventLog_Adapter.res +1 -1
- package/src/components/StateChangeSlice/StateChangeSlice_Callback.res +10 -5
- package/src/components/StateChangeSlice/StateChangeSlice_Callback.res.mjs +24 -14
- package/tests/dcb/DcbFixtures.res +2 -2
- package/tests/dcb/DcbFixtures.res.mjs +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,13 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# 3.0.0-alpha.139 (2026-07-05)
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* **postgres:** add reventless-postgres backend + local-platform integration ([6913200](https://github.com/ReventlessDev/reventless-core/commit/69132001f9271e832a5af33416acd5b645feaf47))
|
|
11
|
+
|
|
12
|
+
|
|
6
13
|
# 3.0.0-alpha.138 (2026-07-04)
|
|
7
14
|
|
|
8
15
|
### Features
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reventlessdev/reventless-core",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.139",
|
|
4
4
|
"description": "Core package for Reventless framework",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"jest": {
|
|
@@ -28,17 +28,17 @@
|
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"sury": "11.0.0-alpha.4",
|
|
30
30
|
"uuid": "^13.0.0",
|
|
31
|
+
"@reventlessdev/rescript-effect": "0.1.0-alpha.24",
|
|
31
32
|
"@reventlessdev/rescript-fast-csv": "1.2.0-alpha.11",
|
|
32
33
|
"@reventlessdev/rescript-jest": "1.0.0-alpha.5",
|
|
33
34
|
"@reventlessdev/rescript-node-streams": "1.1.0-alpha.10",
|
|
34
|
-
"@reventlessdev/rescript-effect": "0.1.0-alpha.24",
|
|
35
35
|
"@reventlessdev/rescript-hash-object": "1.2.0-alpha.10",
|
|
36
|
-
"@reventlessdev/rescript-ssh2": "1.1.0-alpha.11",
|
|
37
36
|
"@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.14",
|
|
38
37
|
"@reventlessdev/rescript-uuid": "1.1.0-alpha.13",
|
|
38
|
+
"@reventlessdev/rescript-ssh2": "1.1.0-alpha.11",
|
|
39
39
|
"@reventlessdev/reventless-interop": "3.0.0-alpha.23",
|
|
40
40
|
"@reventlessdev/reventless-spec": "3.0.0-alpha.64",
|
|
41
|
-
"@reventlessdev/reventless-infra": "3.0.0-alpha.
|
|
41
|
+
"@reventlessdev/reventless-infra": "3.0.0-alpha.85"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"rescript": "^12.3.0",
|
|
@@ -27,7 +27,7 @@ type operations = {
|
|
|
27
27
|
append: (
|
|
28
28
|
array<rawStoredEvent>,
|
|
29
29
|
~condition: Reventless.DcbTag.appendCondition=?,
|
|
30
|
-
) => promise<result<Reventless.DcbTag.sequencePosition,
|
|
30
|
+
) => promise<result<Reventless.DcbTag.sequencePosition, ReventlessInfra.DcbEventLog.appendError>>,
|
|
31
31
|
readStream: (
|
|
32
32
|
~query: Reventless.DcbTag.query,
|
|
33
33
|
~after: Reventless.DcbTag.sequencePosition=?,
|
|
@@ -358,6 +358,12 @@ module Make = (
|
|
|
358
358
|
_ => Ok("ok"),
|
|
359
359
|
)
|
|
360
360
|
| Error(err) =>
|
|
361
|
+
// Typed classification (was substring-matching the error string,
|
|
362
|
+
// which disagreed with the backends' casing).
|
|
363
|
+
let (errorCode, errDetail) = switch err {
|
|
364
|
+
| ReventlessInfra.DcbEventLog.Conflict => ("Conflict", "conflict: condition check failed")
|
|
365
|
+
| StorageFailure(msg) => ("AppendFailed", msg)
|
|
366
|
+
}
|
|
361
367
|
if retries > 0 {
|
|
362
368
|
// Re-seed from the just-read snapshot so the retry reads only
|
|
363
369
|
// the delta the conflicting writer added, not full history.
|
|
@@ -369,13 +375,12 @@ module Make = (
|
|
|
369
375
|
EffectLogger.logWarn(
|
|
370
376
|
~comp,
|
|
371
377
|
`append failed (retrying ${(maxRetries - retries + 1)
|
|
372
|
-
->Int.toString}/${maxRetries->Int.toString}): ${
|
|
378
|
+
->Int.toString}/${maxRetries->Int.toString}): ${errDetail}`,
|
|
373
379
|
)->Effect.flatMap(_ => attempt(~retries=retries - 1))
|
|
374
380
|
} else {
|
|
375
381
|
// Retries exhausted — drop any cached snapshot so the next
|
|
376
382
|
// command for this entity takes the cold full-read path.
|
|
377
383
|
cacheInvalidate()
|
|
378
|
-
let errorCode = err->String.startsWith("Conflict") ? "Conflict" : "AppendFailed"
|
|
379
384
|
// A surfaced Conflict means the 3-retry loop could not absorb
|
|
380
385
|
// the contention — the operator signal for "this slice is hot"
|
|
381
386
|
// (consider sharding / async — Issue 10, §4 of the analysis).
|
|
@@ -384,12 +389,12 @@ module Make = (
|
|
|
384
389
|
}
|
|
385
390
|
CommandTopic_Helpers.reportRejected(
|
|
386
391
|
cmdJson.meta.msgId,
|
|
387
|
-
{errorCode, errorDetail:
|
|
392
|
+
{errorCode, errorDetail: errDetail},
|
|
388
393
|
)
|
|
389
394
|
EffectLogger.logError(
|
|
390
395
|
~comp,
|
|
391
|
-
`append failed, retries exhausted: ${
|
|
392
|
-
)->Effect.map(_ => Error(
|
|
396
|
+
`append failed, retries exhausted: ${errDetail}`,
|
|
397
|
+
)->Effect.map(_ => Error(errDetail))
|
|
393
398
|
}
|
|
394
399
|
},
|
|
395
400
|
)
|
|
@@ -235,27 +235,37 @@ function Make(Spec) {
|
|
|
235
235
|
}));
|
|
236
236
|
}
|
|
237
237
|
let err = appendResult._0;
|
|
238
|
+
let match;
|
|
239
|
+
match = typeof err !== "object" ? [
|
|
240
|
+
"Conflict",
|
|
241
|
+
"conflict: condition check failed"
|
|
242
|
+
] : [
|
|
243
|
+
"AppendFailed",
|
|
244
|
+
err._0
|
|
245
|
+
];
|
|
246
|
+
let errDetail = match[1];
|
|
247
|
+
let errorCode = match[0];
|
|
238
248
|
if (retries > 0) {
|
|
239
249
|
seed.contents = [
|
|
240
250
|
state,
|
|
241
251
|
headPosition
|
|
242
252
|
];
|
|
243
253
|
Metrics$ReventlessCore.emitCount("AppendRetry", Spec.name, undefined);
|
|
244
|
-
return Effect.flatMap(EffectLogger$ReventlessCore.logWarn(comp, undefined, `append failed (retrying ` + ((3 - retries | 0) + 1 | 0).toString() + `/` + (3).toString() + `): ` +
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
254
|
+
return Effect.flatMap(EffectLogger$ReventlessCore.logWarn(comp, undefined, `append failed (retrying ` + ((3 - retries | 0) + 1 | 0).toString() + `/` + (3).toString() + `): ` + errDetail), () => attempt(retries - 1 | 0));
|
|
255
|
+
} else {
|
|
256
|
+
cacheInvalidate();
|
|
257
|
+
if (errorCode === "Conflict") {
|
|
258
|
+
Metrics$ReventlessCore.emitCount("AppendConflict", Spec.name, undefined);
|
|
259
|
+
}
|
|
260
|
+
CommandTopic_Helpers$ReventlessCore.reportRejected(cmdJson.meta.msgId, {
|
|
261
|
+
errorCode: errorCode,
|
|
262
|
+
errorDetail: errDetail
|
|
263
|
+
});
|
|
264
|
+
return Effect.map(EffectLogger$ReventlessCore.logError(comp, undefined, `append failed, retries exhausted: ` + errDetail), () => ({
|
|
265
|
+
TAG: "Error",
|
|
266
|
+
_0: errDetail
|
|
267
|
+
}));
|
|
250
268
|
}
|
|
251
|
-
CommandTopic_Helpers$ReventlessCore.reportRejected(cmdJson.meta.msgId, {
|
|
252
|
-
errorCode: errorCode,
|
|
253
|
-
errorDetail: err
|
|
254
|
-
});
|
|
255
|
-
return Effect.map(EffectLogger$ReventlessCore.logError(comp, undefined, `append failed, retries exhausted: ` + err), () => ({
|
|
256
|
-
TAG: "Error",
|
|
257
|
-
_0: err
|
|
258
|
-
}));
|
|
259
269
|
});
|
|
260
270
|
}
|
|
261
271
|
cachePut([
|
|
@@ -167,7 +167,7 @@ let makeMockStorage = (): mockStorage => {
|
|
|
167
167
|
let append = async (newEvents, ~condition=?) => {
|
|
168
168
|
if failNextAppendsRef.contents > 0 {
|
|
169
169
|
failNextAppendsRef := failNextAppendsRef.contents - 1
|
|
170
|
-
Error(
|
|
170
|
+
Error(ReventlessInfra.DcbEventLog.Conflict)
|
|
171
171
|
} else {
|
|
172
172
|
let conflictDetected = switch condition {
|
|
173
173
|
| Some(cond: Reventless.DcbTag.appendCondition) =>
|
|
@@ -181,7 +181,7 @@ let makeMockStorage = (): mockStorage => {
|
|
|
181
181
|
| None => false
|
|
182
182
|
}
|
|
183
183
|
if conflictDetected {
|
|
184
|
-
Error(
|
|
184
|
+
Error(ReventlessInfra.DcbEventLog.Conflict)
|
|
185
185
|
} else {
|
|
186
186
|
let storedEvents = newEvents->Array.map((event: DcbEventLog_Adapter.rawStoredEvent) => {
|
|
187
187
|
position := position.contents + 1
|
|
@@ -245,7 +245,7 @@ function makeMockStorage() {
|
|
|
245
245
|
failNextAppendsRef.contents = failNextAppendsRef.contents - 1 | 0;
|
|
246
246
|
return {
|
|
247
247
|
TAG: "Error",
|
|
248
|
-
_0: "
|
|
248
|
+
_0: "Conflict"
|
|
249
249
|
};
|
|
250
250
|
}
|
|
251
251
|
let conflictDetected = condition !== undefined ? events.contents.some(event => {
|
|
@@ -260,7 +260,7 @@ function makeMockStorage() {
|
|
|
260
260
|
if (conflictDetected) {
|
|
261
261
|
return {
|
|
262
262
|
TAG: "Error",
|
|
263
|
-
_0: "
|
|
263
|
+
_0: "Conflict"
|
|
264
264
|
};
|
|
265
265
|
}
|
|
266
266
|
let storedEvents = newEvents.map(event => {
|