@smthrs/journal 1.0.0-rc.4 → 1.0.0-rc.6
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/README.md +4 -4
- package/dist/cjs/JournalGeneration.d.ts +2 -2
- package/dist/cjs/JournalGeneration.d.ts.map +1 -1
- package/dist/cjs/JournalGeneration.js +3 -2
- package/dist/cjs/JournalGeneration.js.map +2 -2
- package/dist/cjs/Migrations.d.ts +2 -2
- package/dist/cjs/Migrations.d.ts.map +1 -1
- package/dist/cjs/SqlConsensus.d.ts +1 -1
- package/dist/cjs/SqlConsensus.d.ts.map +1 -1
- package/dist/cjs/SqlConsensus.js +1 -1
- package/dist/cjs/SqlConsensus.js.map +1 -1
- package/dist/cjs/SqlJournal.d.ts +1 -1
- package/dist/cjs/SqlJournal.d.ts.map +1 -1
- package/dist/cjs/SqlJournal.js +2 -2
- package/dist/cjs/SqlJournal.js.map +2 -2
- package/dist/cjs/migrations/0001_initial.d.ts +1 -1
- package/dist/cjs/migrations/0001_initial.d.ts.map +1 -1
- package/dist/cjs/migrations/0001_initial.js +1 -1
- package/dist/cjs/migrations/0001_initial.js.map +1 -1
- package/dist/cjs/migrations/0002_checkpoints.d.ts +1 -1
- package/dist/cjs/migrations/0002_checkpoints.d.ts.map +1 -1
- package/dist/cjs/migrations/0002_checkpoints.js +1 -1
- package/dist/cjs/migrations/0002_checkpoints.js.map +1 -1
- package/dist/cjs/migrations/0003_startup_index.d.ts +2 -2
- package/dist/cjs/migrations/0003_startup_index.d.ts.map +1 -1
- package/dist/cjs/migrations/0003_startup_index.js +1 -1
- package/dist/cjs/migrations/0003_startup_index.js.map +1 -1
- package/dist/cjs/migrations/0004_dedup.d.ts +1 -1
- package/dist/cjs/migrations/0004_dedup.d.ts.map +1 -1
- package/dist/cjs/migrations/0004_dedup.js +1 -1
- package/dist/cjs/migrations/0004_dedup.js.map +1 -1
- package/dist/cjs/migrations/0005_run_event_type.d.ts +1 -1
- package/dist/cjs/migrations/0005_run_event_type.d.ts.map +1 -1
- package/dist/cjs/migrations/0005_run_event_type.js +1 -1
- package/dist/cjs/migrations/0005_run_event_type.js.map +1 -1
- package/dist/cjs/migrations/0006_consensus.d.ts +1 -1
- package/dist/cjs/migrations/0006_consensus.d.ts.map +1 -1
- package/dist/cjs/migrations/0006_consensus.js +1 -1
- package/dist/cjs/migrations/0006_consensus.js.map +1 -1
- package/dist/cjs/test/TestJournal.d.ts +1 -1
- package/dist/cjs/test/TestJournal.d.ts.map +1 -1
- package/dist/esm/JournalGeneration.d.ts +2 -2
- package/dist/esm/JournalGeneration.d.ts.map +1 -1
- package/dist/esm/JournalGeneration.js +8 -2
- package/dist/esm/JournalGeneration.js.map +1 -1
- package/dist/esm/Migrations.d.ts +2 -2
- package/dist/esm/Migrations.d.ts.map +1 -1
- package/dist/esm/SqlConsensus.d.ts +1 -1
- package/dist/esm/SqlConsensus.d.ts.map +1 -1
- package/dist/esm/SqlConsensus.js +1 -1
- package/dist/esm/SqlConsensus.js.map +1 -1
- package/dist/esm/SqlJournal.d.ts +1 -1
- package/dist/esm/SqlJournal.d.ts.map +1 -1
- package/dist/esm/SqlJournal.js +2 -2
- package/dist/esm/SqlJournal.js.map +1 -1
- package/dist/esm/migrations/0001_initial.d.ts +1 -1
- package/dist/esm/migrations/0001_initial.d.ts.map +1 -1
- package/dist/esm/migrations/0001_initial.js +1 -1
- package/dist/esm/migrations/0001_initial.js.map +1 -1
- package/dist/esm/migrations/0002_checkpoints.d.ts +1 -1
- package/dist/esm/migrations/0002_checkpoints.d.ts.map +1 -1
- package/dist/esm/migrations/0002_checkpoints.js +1 -1
- package/dist/esm/migrations/0002_checkpoints.js.map +1 -1
- package/dist/esm/migrations/0003_startup_index.d.ts +2 -2
- package/dist/esm/migrations/0003_startup_index.d.ts.map +1 -1
- package/dist/esm/migrations/0003_startup_index.js +1 -1
- package/dist/esm/migrations/0003_startup_index.js.map +1 -1
- package/dist/esm/migrations/0004_dedup.d.ts +1 -1
- package/dist/esm/migrations/0004_dedup.d.ts.map +1 -1
- package/dist/esm/migrations/0004_dedup.js +1 -1
- package/dist/esm/migrations/0004_dedup.js.map +1 -1
- package/dist/esm/migrations/0005_run_event_type.d.ts +1 -1
- package/dist/esm/migrations/0005_run_event_type.d.ts.map +1 -1
- package/dist/esm/migrations/0005_run_event_type.js +1 -1
- package/dist/esm/migrations/0005_run_event_type.js.map +1 -1
- package/dist/esm/migrations/0006_consensus.d.ts +1 -1
- package/dist/esm/migrations/0006_consensus.d.ts.map +1 -1
- package/dist/esm/migrations/0006_consensus.js +1 -1
- package/dist/esm/migrations/0006_consensus.js.map +1 -1
- package/dist/esm/test/TestJournal.d.ts +1 -1
- package/dist/esm/test/TestJournal.d.ts.map +1 -1
- package/docs/README.md +1 -1
- package/docs/installation.md +3 -3
- package/docs/quickstart.md +1 -1
- package/package.json +7 -7
- package/src/JournalGeneration.ts +8 -2
- package/src/SqlConsensus.ts +1 -1
- package/src/SqlJournal.ts +3 -3
- package/src/migrations/0001_initial.ts +1 -1
- package/src/migrations/0002_checkpoints.ts +1 -1
- package/src/migrations/0003_startup_index.ts +1 -1
- package/src/migrations/0004_dedup.ts +1 -1
- package/src/migrations/0005_run_event_type.ts +1 -1
- package/src/migrations/0006_consensus.ts +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"0001_initial.d.ts","sourceRoot":"","sources":["../../../src/migrations/0001_initial.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,SAAS,MAAM
|
|
1
|
+
{"version":3,"file":"0001_initial.d.ts","sourceRoot":"","sources":["../../../src/migrations/0001_initial.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,SAAS,MAAM,sBAAsB,CAAA;AAEjD;;;;;GAKG;AACH,eAAO,MAAM,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,SAAS,CAAC,SAAS,CAwBpE,CAAA"}
|
|
@@ -33,7 +33,7 @@ __export(initial_exports, {
|
|
|
33
33
|
module.exports = __toCommonJS(initial_exports);
|
|
34
34
|
var Dialect = __toESM(require("@smthrs/database/Dialect"), 1);
|
|
35
35
|
var Effect = __toESM(require("effect/Effect"), 1);
|
|
36
|
-
var SqlClient = __toESM(require("effect/
|
|
36
|
+
var SqlClient = __toESM(require("effect/sql/SqlClient"), 1);
|
|
37
37
|
const initial = Effect.gen(function* () {
|
|
38
38
|
const sql = yield* SqlClient.SqlClient;
|
|
39
39
|
yield* sql`CREATE TABLE flows_journal_events (
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/migrations/0001_initial.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Initial durable journal event schema.\n *\n * @since 0.1.0\n */\n\nimport * as Dialect from \"@smthrs/database/Dialect\"\nimport * as Effect from \"effect/Effect\"\nimport * as SqlClient from \"effect/
|
|
4
|
+
"sourcesContent": ["/**\n * Initial durable journal event schema.\n *\n * @since 0.1.0\n */\n\nimport * as Dialect from \"@smthrs/database/Dialect\"\nimport * as Effect from \"effect/Effect\"\nimport * as SqlClient from \"effect/sql/SqlClient\"\n\n/**\n * Creates the `flows_journal_events` table and its event-type index.\n *\n * @category migrations\n * @since 0.1.0\n */\nexport const initial: Effect.Effect<void, unknown, SqlClient.SqlClient> = Effect.gen(function*() {\n const sql = yield* SqlClient.SqlClient\n\n yield* sql`CREATE TABLE flows_journal_events (\n run_id TEXT NOT NULL CHECK (length(run_id) > 0),\n seq ${Dialect.integer(sql)} NOT NULL CHECK (${\n Dialect.isInteger(sql, sql`seq`)\n } AND seq >= 0 AND seq <= 9007199254740991),\n event_id TEXT NOT NULL UNIQUE CHECK (length(event_id) > 0),\n source_id TEXT NOT NULL CHECK (length(source_id) > 0),\n source_seq ${Dialect.integer(sql)} NOT NULL CHECK (${\n Dialect.isInteger(sql, sql`source_seq`)\n } AND source_seq >= 0 AND source_seq <= 9007199254740991),\n emitted_at_ms ${Dialect.integer(sql)} NOT NULL CHECK (${\n Dialect.isInteger(sql, sql`emitted_at_ms`)\n } AND emitted_at_ms >= 0 AND emitted_at_ms <= 9007199254740991),\n event_type TEXT NOT NULL CHECK (length(event_type) > 0),\n payload_json TEXT NOT NULL CHECK (${Dialect.jsonValid(sql, sql`payload_json`)}),\n meta_json TEXT NOT NULL CHECK (${Dialect.jsonValid(sql, sql`meta_json`)}),\n PRIMARY KEY (run_id, seq),\n UNIQUE (run_id, source_id, source_seq)\n )`\n\n yield* sql`CREATE INDEX flows_journal_events_event_type_idx ON flows_journal_events (event_type)`\n})\n"],
|
|
5
5
|
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAMA,cAAyB;AACzB,aAAwB;AACxB,gBAA2B;AAQpB,MAAM,UAA6D,OAAO,IAAI,aAAY;AAC/F,QAAM,MAAM,OAAO,UAAU;AAE7B,SAAO;AAAA;AAAA,UAEC,QAAQ,QAAQ,GAAG,CAAC,oBAC1B,QAAQ,UAAU,KAAK,QAAQ,CACjC;AAAA;AAAA;AAAA,iBAGe,QAAQ,QAAQ,GAAG,CAAC,oBACjC,QAAQ,UAAU,KAAK,eAAe,CACxC;AAAA,oBACkB,QAAQ,QAAQ,GAAG,CAAC,oBACpC,QAAQ,UAAU,KAAK,kBAAkB,CAC3C;AAAA;AAAA,wCAEsC,QAAQ,UAAU,KAAK,iBAAiB,CAAC;AAAA,qCAC5C,QAAQ,UAAU,KAAK,cAAc,CAAC;AAAA;AAAA;AAAA;AAKzE,SAAO;AACT,CAAC;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"0002_checkpoints.d.ts","sourceRoot":"","sources":["../../../src/migrations/0002_checkpoints.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,SAAS,MAAM
|
|
1
|
+
{"version":3,"file":"0002_checkpoints.d.ts","sourceRoot":"","sources":["../../../src/migrations/0002_checkpoints.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,SAAS,MAAM,sBAAsB,CAAA;AAEjD;;;;;GAKG;AACH,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,SAAS,CAAC,SAAS,CAiBxE,CAAA"}
|
|
@@ -33,7 +33,7 @@ __export(checkpoints_exports, {
|
|
|
33
33
|
module.exports = __toCommonJS(checkpoints_exports);
|
|
34
34
|
var Dialect = __toESM(require("@smthrs/database/Dialect"), 1);
|
|
35
35
|
var Effect = __toESM(require("effect/Effect"), 1);
|
|
36
|
-
var SqlClient = __toESM(require("effect/
|
|
36
|
+
var SqlClient = __toESM(require("effect/sql/SqlClient"), 1);
|
|
37
37
|
const checkpoints = Effect.gen(function* () {
|
|
38
38
|
const sql = yield* SqlClient.SqlClient;
|
|
39
39
|
yield* sql`CREATE TABLE flows_journal_checkpoints (
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/migrations/0002_checkpoints.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Durable journal checkpoints and the compaction floor.\n *\n * One row per `(run_id, seq)`: the caller-captured state that replays the run\n * from `seq` without the entries before it. `compacted_at_ms` is NULL until a\n * compaction has truncated the entries strictly below `seq`; the largest\n * compacted `seq` per run is the run's compaction floor, and reads whose\n * cursor starts below it fail with `compacted` instead of returning a\n * silently shortened history.\n *\n * Prior art: Temporal's mutable state is exactly this shape, a durable\n * snapshot pinned to a history offset (`last_first_event_id` in\n * `reference/temporal/proto/internal/temporal/server/api/persistence/v1/executions.proto`),\n * with history below it never replayed.\n *\n * @since 0.1.0\n */\n\nimport * as Dialect from \"@smthrs/database/Dialect\"\nimport * as Effect from \"effect/Effect\"\nimport * as SqlClient from \"effect/
|
|
4
|
+
"sourcesContent": ["/**\n * Durable journal checkpoints and the compaction floor.\n *\n * One row per `(run_id, seq)`: the caller-captured state that replays the run\n * from `seq` without the entries before it. `compacted_at_ms` is NULL until a\n * compaction has truncated the entries strictly below `seq`; the largest\n * compacted `seq` per run is the run's compaction floor, and reads whose\n * cursor starts below it fail with `compacted` instead of returning a\n * silently shortened history.\n *\n * Prior art: Temporal's mutable state is exactly this shape, a durable\n * snapshot pinned to a history offset (`last_first_event_id` in\n * `reference/temporal/proto/internal/temporal/server/api/persistence/v1/executions.proto`),\n * with history below it never replayed.\n *\n * @since 0.1.0\n */\n\nimport * as Dialect from \"@smthrs/database/Dialect\"\nimport * as Effect from \"effect/Effect\"\nimport * as SqlClient from \"effect/sql/SqlClient\"\n\n/**\n * Creates the `flows_journal_checkpoints` table.\n *\n * @category migrations\n * @since 0.1.0\n */\nexport const checkpoints: Effect.Effect<void, unknown, SqlClient.SqlClient> = Effect.gen(function*() {\n const sql = yield* SqlClient.SqlClient\n\n yield* sql`CREATE TABLE flows_journal_checkpoints (\n run_id TEXT NOT NULL CHECK (length(run_id) > 0),\n seq ${Dialect.integer(sql)} NOT NULL CHECK (${\n Dialect.isInteger(sql, sql`seq`)\n } AND seq >= 0 AND seq <= 9007199254740991),\n state_json TEXT NOT NULL CHECK (${Dialect.jsonValid(sql, sql`state_json`)}),\n created_at_ms ${Dialect.integer(sql)} NOT NULL CHECK (${\n Dialect.isInteger(sql, sql`created_at_ms`)\n } AND created_at_ms >= 0 AND created_at_ms <= 9007199254740991),\n compacted_at_ms ${Dialect.integer(sql)} CHECK (compacted_at_ms IS NULL OR (${\n Dialect.isInteger(sql, sql`compacted_at_ms`)\n } AND compacted_at_ms >= 0 AND compacted_at_ms <= 9007199254740991)),\n PRIMARY KEY (run_id, seq)\n )`\n})\n"],
|
|
5
5
|
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAkBA,cAAyB;AACzB,aAAwB;AACxB,gBAA2B;AAQpB,MAAM,cAAiE,OAAO,IAAI,aAAY;AACnG,QAAM,MAAM,OAAO,UAAU;AAE7B,SAAO;AAAA;AAAA,UAEC,QAAQ,QAAQ,GAAG,CAAC,oBAC1B,QAAQ,UAAU,KAAK,QAAQ,CACjC;AAAA,sCACoC,QAAQ,UAAU,KAAK,eAAe,CAAC;AAAA,oBACzD,QAAQ,QAAQ,GAAG,CAAC,oBACpC,QAAQ,UAAU,KAAK,kBAAkB,CAC3C;AAAA,sBACoB,QAAQ,QAAQ,GAAG,CAAC,uCACtC,QAAQ,UAAU,KAAK,oBAAoB,CAC7C;AAAA;AAAA;AAGF,CAAC;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -4,12 +4,12 @@
|
|
|
4
4
|
* @since 0.1.0
|
|
5
5
|
*/
|
|
6
6
|
import * as Effect from "effect/Effect";
|
|
7
|
-
import * as SqlClient from "effect/
|
|
7
|
+
import * as SqlClient from "effect/sql/SqlClient";
|
|
8
8
|
/**
|
|
9
9
|
* Preserves deterministic timestamp/run/sequence ordering without sorting the retained history.
|
|
10
10
|
*
|
|
11
11
|
* @category migrations
|
|
12
12
|
* @since 0.1.0
|
|
13
13
|
*/
|
|
14
|
-
export declare const startupIndex: Effect.Effect<void, import("effect/
|
|
14
|
+
export declare const startupIndex: Effect.Effect<void, import("effect/sql/SqlError").SqlError, SqlClient.SqlClient>;
|
|
15
15
|
//# sourceMappingURL=0003_startup_index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"0003_startup_index.d.ts","sourceRoot":"","sources":["../../../src/migrations/0003_startup_index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,SAAS,MAAM
|
|
1
|
+
{"version":3,"file":"0003_startup_index.d.ts","sourceRoot":"","sources":["../../../src/migrations/0003_startup_index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,SAAS,MAAM,sBAAsB,CAAA;AAEjD;;;;;GAKG;AACH,eAAO,MAAM,YAAY,kFAGvB,CAAA"}
|
|
@@ -32,7 +32,7 @@ __export(startup_index_exports, {
|
|
|
32
32
|
});
|
|
33
33
|
module.exports = __toCommonJS(startup_index_exports);
|
|
34
34
|
var Effect = __toESM(require("effect/Effect"), 1);
|
|
35
|
-
var SqlClient = __toESM(require("effect/
|
|
35
|
+
var SqlClient = __toESM(require("effect/sql/SqlClient"), 1);
|
|
36
36
|
const startupIndex = Effect.gen(function* () {
|
|
37
37
|
const sql = yield* SqlClient.SqlClient;
|
|
38
38
|
yield* sql`CREATE INDEX flows_journal_events_startup_idx ON flows_journal_events (emitted_at_ms DESC, run_id DESC, seq DESC)`;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/migrations/0003_startup_index.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Index for the bounded journal source-event startup window.\n *\n * @since 0.1.0\n */\n\nimport * as Effect from \"effect/Effect\"\nimport * as SqlClient from \"effect/
|
|
4
|
+
"sourcesContent": ["/**\n * Index for the bounded journal source-event startup window.\n *\n * @since 0.1.0\n */\n\nimport * as Effect from \"effect/Effect\"\nimport * as SqlClient from \"effect/sql/SqlClient\"\n\n/**\n * Preserves deterministic timestamp/run/sequence ordering without sorting the retained history.\n *\n * @category migrations\n * @since 0.1.0\n */\nexport const startupIndex = Effect.gen(function*() {\n const sql = yield* SqlClient.SqlClient\n yield* sql`CREATE INDEX flows_journal_events_startup_idx ON flows_journal_events (emitted_at_ms DESC, run_id DESC, seq DESC)`\n})\n"],
|
|
5
5
|
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAMA,aAAwB;AACxB,gBAA2B;AAQpB,MAAM,eAAe,OAAO,IAAI,aAAY;AACjD,QAAM,MAAM,OAAO,UAAU;AAC7B,SAAO;AACT,CAAC;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"0004_dedup.d.ts","sourceRoot":"","sources":["../../../src/migrations/0004_dedup.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,SAAS,MAAM
|
|
1
|
+
{"version":3,"file":"0004_dedup.d.ts","sourceRoot":"","sources":["../../../src/migrations/0004_dedup.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,SAAS,MAAM,sBAAsB,CAAA;AAEjD;;;;;GAKG;AACH,eAAO,MAAM,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,SAAS,CAAC,SAAS,CA0BlE,CAAA"}
|
|
@@ -33,7 +33,7 @@ __export(dedup_exports, {
|
|
|
33
33
|
module.exports = __toCommonJS(dedup_exports);
|
|
34
34
|
var Dialect = __toESM(require("@smthrs/database/Dialect"), 1);
|
|
35
35
|
var Effect = __toESM(require("effect/Effect"), 1);
|
|
36
|
-
var SqlClient = __toESM(require("effect/
|
|
36
|
+
var SqlClient = __toESM(require("effect/sql/SqlClient"), 1);
|
|
37
37
|
const dedup = Effect.gen(function* () {
|
|
38
38
|
const sql = yield* SqlClient.SqlClient;
|
|
39
39
|
yield* sql`CREATE TABLE flows_journal_dedup (
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/migrations/0004_dedup.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Producer identities retained when compaction deletes event payloads.\n *\n * @since 1.0.0-rc.0\n */\n\nimport * as Dialect from \"@smthrs/database/Dialect\"\nimport * as Effect from \"effect/Effect\"\nimport * as SqlClient from \"effect/
|
|
4
|
+
"sourcesContent": ["/**\n * Producer identities retained when compaction deletes event payloads.\n *\n * @since 1.0.0-rc.0\n */\n\nimport * as Dialect from \"@smthrs/database/Dialect\"\nimport * as Effect from \"effect/Effect\"\nimport * as SqlClient from \"effect/sql/SqlClient\"\n\n/**\n * Creates the durable compacted-identity index.\n *\n * @category migrations\n * @since 1.0.0-rc.0\n */\nexport const dedup: Effect.Effect<void, unknown, SqlClient.SqlClient> = Effect.gen(function*() {\n const sql = yield* SqlClient.SqlClient\n yield* sql`CREATE TABLE flows_journal_dedup (\n run_id TEXT NOT NULL CHECK (length(run_id) > 0),\n source_id TEXT NOT NULL CHECK (length(source_id) > 0),\n source_seq ${Dialect.integer(sql)} NOT NULL CHECK (${\n Dialect.isInteger(sql, sql`source_seq`)\n } AND source_seq >= 0 AND source_seq <= 9007199254740991),\n event_id TEXT NOT NULL UNIQUE CHECK (length(event_id) > 0),\n seq ${Dialect.integer(sql)} NOT NULL CHECK (${\n Dialect.isInteger(sql, sql`seq`)\n } AND seq >= 0 AND seq <= 9007199254740991),\n content_hash TEXT NOT NULL CHECK (length(content_hash) = 64),\n PRIMARY KEY (run_id, source_id, source_seq)\n )`\n // Enforce the same identity constraints after their event rows are removed.\n // RAISE(IGNORE) leaves INSERT ... RETURNING empty, so the existing duplicate\n // path checks the owner fence and then resolves the original receipt.\n yield* Dialect.trigger(sql, {\n name: \"flows_journal_dedup_insert_guard\",\n table: \"flows_journal_events\",\n event: \"BEFORE INSERT\",\n when: `EXISTS (SELECT 1 FROM flows_journal_dedup WHERE event_id = NEW.event_id\n OR (run_id = NEW.run_id AND source_id = NEW.source_id AND source_seq = NEW.source_seq))`,\n ignore: true\n })\n})\n"],
|
|
5
5
|
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAMA,cAAyB;AACzB,aAAwB;AACxB,gBAA2B;AAQpB,MAAM,QAA2D,OAAO,IAAI,aAAY;AAC7F,QAAM,MAAM,OAAO,UAAU;AAC7B,SAAO;AAAA;AAAA;AAAA,iBAGQ,QAAQ,QAAQ,GAAG,CAAC,oBACjC,QAAQ,UAAU,KAAK,eAAe,CACxC;AAAA;AAAA,UAEQ,QAAQ,QAAQ,GAAG,CAAC,oBAC1B,QAAQ,UAAU,KAAK,QAAQ,CACjC;AAAA;AAAA;AAAA;AAOA,SAAO,QAAQ,QAAQ,KAAK;AAAA,IAC1B,MAAM;AAAA,IACN,OAAO;AAAA,IACP,OAAO;AAAA,IACP,MAAM;AAAA;AAAA,IAEN,QAAQ;AAAA,EACV,CAAC;AACH,CAAC;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"0005_run_event_type.d.ts","sourceRoot":"","sources":["../../../src/migrations/0005_run_event_type.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,SAAS,MAAM
|
|
1
|
+
{"version":3,"file":"0005_run_event_type.d.ts","sourceRoot":"","sources":["../../../src/migrations/0005_run_event_type.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,SAAS,MAAM,sBAAsB,CAAA;AAEjD;;;;;GAKG;AACH,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,SAAS,CAAC,SAAS,CAGzE,CAAA"}
|
|
@@ -32,7 +32,7 @@ __export(run_event_type_exports, {
|
|
|
32
32
|
});
|
|
33
33
|
module.exports = __toCommonJS(run_event_type_exports);
|
|
34
34
|
var Effect = __toESM(require("effect/Effect"), 1);
|
|
35
|
-
var SqlClient = __toESM(require("effect/
|
|
35
|
+
var SqlClient = __toESM(require("effect/sql/SqlClient"), 1);
|
|
36
36
|
const runEventType = Effect.gen(function* () {
|
|
37
37
|
const sql = yield* SqlClient.SqlClient;
|
|
38
38
|
yield* sql`CREATE INDEX flows_journal_events_run_event_type_idx ON flows_journal_events (run_id, event_type, seq)`;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/migrations/0005_run_event_type.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Indexed exact event-type reads within a run.\n *\n * @since 1.0.0\n */\n\nimport * as Effect from \"effect/Effect\"\nimport * as SqlClient from \"effect/
|
|
4
|
+
"sourcesContent": ["/**\n * Indexed exact event-type reads within a run.\n *\n * @since 1.0.0\n */\n\nimport * as Effect from \"effect/Effect\"\nimport * as SqlClient from \"effect/sql/SqlClient\"\n\n/**\n * Keeps filtered cursor reads proportional to the matching entries.\n *\n * @category migrations\n * @since 1.0.0\n */\nexport const runEventType: Effect.Effect<void, unknown, SqlClient.SqlClient> = Effect.gen(function*() {\n const sql = yield* SqlClient.SqlClient\n yield* sql`CREATE INDEX flows_journal_events_run_event_type_idx ON flows_journal_events (run_id, event_type, seq)`\n})\n"],
|
|
5
5
|
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAMA,aAAwB;AACxB,gBAA2B;AAQpB,MAAM,eAAkE,OAAO,IAAI,aAAY;AACpG,QAAM,MAAM,OAAO,UAAU;AAC7B,SAAO;AACT,CAAC;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* @since 1.0.0
|
|
10
10
|
*/
|
|
11
11
|
import * as Effect from "effect/Effect";
|
|
12
|
-
import * as SqlClient from "effect/
|
|
12
|
+
import * as SqlClient from "effect/sql/SqlClient";
|
|
13
13
|
/**
|
|
14
14
|
* Creates the `flows_consensus_leases` table and backfills a lease for every
|
|
15
15
|
* running or claimed run when `flows_runs` is already present, so an owner
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"0006_consensus.d.ts","sourceRoot":"","sources":["../../../src/migrations/0006_consensus.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,SAAS,MAAM
|
|
1
|
+
{"version":3,"file":"0006_consensus.d.ts","sourceRoot":"","sources":["../../../src/migrations/0006_consensus.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,SAAS,MAAM,sBAAsB,CAAA;AAEjD;;;;;;;GAOG;AACH,eAAO,MAAM,SAAS,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,SAAS,CAAC,SAAS,CA2DtE,CAAA"}
|
|
@@ -33,7 +33,7 @@ __export(consensus_exports, {
|
|
|
33
33
|
module.exports = __toCommonJS(consensus_exports);
|
|
34
34
|
var Dialect = __toESM(require("@smthrs/database/Dialect"), 1);
|
|
35
35
|
var Effect = __toESM(require("effect/Effect"), 1);
|
|
36
|
-
var SqlClient = __toESM(require("effect/
|
|
36
|
+
var SqlClient = __toESM(require("effect/sql/SqlClient"), 1);
|
|
37
37
|
const consensus = Effect.gen(function* () {
|
|
38
38
|
const sql = yield* SqlClient.SqlClient;
|
|
39
39
|
yield* sql`CREATE TABLE flows_consensus_leases (
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/migrations/0006_consensus.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * The `SqlConsensus` lease table.\n *\n * This is the private schema of the default consensus strategy: the owner\n * tuple, the two-phase claim columns, and the grant timestamp relocated from\n * `flows_runs`. Lease rows are strategy evidence, never history: heartbeats\n * and lease state never enter the journal.\n *\n * @since 1.0.0\n */\n\nimport * as Dialect from \"@smthrs/database/Dialect\"\nimport * as Effect from \"effect/Effect\"\nimport * as SqlClient from \"effect/
|
|
4
|
+
"sourcesContent": ["/**\n * The `SqlConsensus` lease table.\n *\n * This is the private schema of the default consensus strategy: the owner\n * tuple, the two-phase claim columns, and the grant timestamp relocated from\n * `flows_runs`. Lease rows are strategy evidence, never history: heartbeats\n * and lease state never enter the journal.\n *\n * @since 1.0.0\n */\n\nimport * as Dialect from \"@smthrs/database/Dialect\"\nimport * as Effect from \"effect/Effect\"\nimport * as SqlClient from \"effect/sql/SqlClient\"\n\n/**\n * Creates the `flows_consensus_leases` table and backfills a lease for every\n * running or claimed run when `flows_runs` is already present, so an owner\n * that was driving a run before this migration keeps its fence afterwards.\n *\n * @category migrations\n * @since 1.0.0\n */\nexport const consensus: Effect.Effect<void, unknown, SqlClient.SqlClient> = Effect.gen(function*() {\n const sql = yield* SqlClient.SqlClient\n\n yield* sql`CREATE TABLE flows_consensus_leases (\n run_id TEXT PRIMARY KEY CHECK (length(run_id) > 0),\n owner_host_id TEXT,\n owner_pid ${Dialect.integer(sql)} CHECK (owner_pid IS NULL OR (${\n Dialect.isInteger(sql, sql`owner_pid`)\n } AND owner_pid >= 0)),\n owner_nonce TEXT,\n granted_at_ms ${Dialect.integer(sql)} CHECK (granted_at_ms IS NULL OR (${\n Dialect.isInteger(sql, sql`granted_at_ms`)\n } AND granted_at_ms >= 0)),\n heartbeat_at_ms ${Dialect.integer(sql)} CHECK (heartbeat_at_ms IS NULL OR (${\n Dialect.isInteger(sql, sql`heartbeat_at_ms`)\n } AND heartbeat_at_ms >= 0)),\n claim_host_id TEXT,\n claim_pid ${Dialect.integer(sql)} CHECK (claim_pid IS NULL OR (${\n Dialect.isInteger(sql, sql`claim_pid`)\n } AND claim_pid >= 0)),\n claim_nonce TEXT,\n claimed_at_ms ${Dialect.integer(sql)} CHECK (claimed_at_ms IS NULL OR (${\n Dialect.isInteger(sql, sql`claimed_at_ms`)\n } AND claimed_at_ms >= 0))\n )`\n\n const tables = yield* Dialect.tables(sql)\n if (!tables.some((table) => table.name === \"flows_runs\")) {\n return\n }\n\n yield* sql`\n INSERT INTO flows_consensus_leases (\n run_id,\n owner_host_id,\n owner_pid,\n owner_nonce,\n granted_at_ms,\n heartbeat_at_ms,\n claim_host_id,\n claim_pid,\n claim_nonce,\n claimed_at_ms\n )\n SELECT\n run_id,\n owner_host_id,\n owner_pid,\n owner_nonce,\n CASE WHEN status = 'running' THEN heartbeat_at_ms ELSE NULL END,\n CASE WHEN status = 'running' THEN heartbeat_at_ms ELSE NULL END,\n claim_host_id,\n claim_pid,\n claim_nonce,\n claimed_at_ms\n FROM flows_runs\n WHERE status = 'running' OR claim_host_id IS NOT NULL\n ON CONFLICT (run_id) DO NOTHING\n `\n})\n"],
|
|
5
5
|
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAWA,cAAyB;AACzB,aAAwB;AACxB,gBAA2B;AAUpB,MAAM,YAA+D,OAAO,IAAI,aAAY;AACjG,QAAM,MAAM,OAAO,UAAU;AAE7B,SAAO;AAAA;AAAA;AAAA,gBAGO,QAAQ,QAAQ,GAAG,CAAC,iCAChC,QAAQ,UAAU,KAAK,cAAc,CACvC;AAAA;AAAA,oBAEkB,QAAQ,QAAQ,GAAG,CAAC,qCACpC,QAAQ,UAAU,KAAK,kBAAkB,CAC3C;AAAA,sBACoB,QAAQ,QAAQ,GAAG,CAAC,uCACtC,QAAQ,UAAU,KAAK,oBAAoB,CAC7C;AAAA;AAAA,gBAEc,QAAQ,QAAQ,GAAG,CAAC,iCAChC,QAAQ,UAAU,KAAK,cAAc,CACvC;AAAA;AAAA,oBAEkB,QAAQ,QAAQ,GAAG,CAAC,qCACpC,QAAQ,UAAU,KAAK,kBAAkB,CAC3C;AAAA;AAGA,QAAM,SAAS,OAAO,QAAQ,OAAO,GAAG;AACxC,MAAI,CAAC,OAAO,KAAK,CAAC,UAAU,MAAM,SAAS,YAAY,GAAG;AACxD;AAAA,EACF;AAEA,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA4BT,CAAC;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -39,5 +39,5 @@ export interface TestJournalOptions extends Omit<SqlJournal.SqlJournalOptions, "
|
|
|
39
39
|
* @category layers
|
|
40
40
|
* @since 0.1.0
|
|
41
41
|
*/
|
|
42
|
-
export declare const layer: (options?: TestJournalOptions) => Layer.Layer<import("../Journal.ts").Journal, import("effect/
|
|
42
|
+
export declare const layer: (options?: TestJournalOptions) => Layer.Layer<import("../Journal.ts").Journal, import("effect/sql/SqlError").SqlError | import("../Journal.ts").JournalError | import("effect/sql/Migrator").MigrationError, never>;
|
|
43
43
|
//# sourceMappingURL=TestJournal.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TestJournal.d.ts","sourceRoot":"","sources":["../../../src/test/TestJournal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;AAErC,OAAO,KAAK,UAAU,MAAM,kBAAkB,CAAA;AAE9C;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,kBAAmB,SAAQ,IAAI,CAAC,UAAU,CAAC,iBAAiB,EAAE,UAAU,GAAG,UAAU,CAAC;IACrG,yEAAyE;IACzE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACtC,6EAA6E;IAC7E,QAAQ,CAAC,QAAQ,CAAC,EAAE,UAAU,CAAC,iBAAiB,CAAC,UAAU,CAAC,GAAG,SAAS,CAAA;CACzE;AAED;;;;;;GAMG;AACH,eAAO,MAAM,KAAK,GAAI,UAAU,kBAAkB,
|
|
1
|
+
{"version":3,"file":"TestJournal.d.ts","sourceRoot":"","sources":["../../../src/test/TestJournal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;AAErC,OAAO,KAAK,UAAU,MAAM,kBAAkB,CAAA;AAE9C;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,kBAAmB,SAAQ,IAAI,CAAC,UAAU,CAAC,iBAAiB,EAAE,UAAU,GAAG,UAAU,CAAC;IACrG,yEAAyE;IACzE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACtC,6EAA6E;IAC7E,QAAQ,CAAC,QAAQ,CAAC,EAAE,UAAU,CAAC,iBAAiB,CAAC,UAAU,CAAC,GAAG,SAAS,CAAA;CACzE;AAED;;;;;;GAMG;AACH,eAAO,MAAM,KAAK,GAAI,UAAU,kBAAkB,sLAKgC,CAAA"}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* @since 1.0.0-rc.0
|
|
5
5
|
*/
|
|
6
6
|
import * as Effect from "effect/Effect";
|
|
7
|
-
import * as SqlClient from "effect/
|
|
7
|
+
import * as SqlClient from "effect/sql/SqlClient";
|
|
8
8
|
/**
|
|
9
9
|
* Installs the generation table idempotently. Both the SQL journal and time
|
|
10
10
|
* travel initialize it, including databases whose migration ladder already
|
|
@@ -13,7 +13,7 @@ import * as SqlClient from "effect/unstable/sql/SqlClient";
|
|
|
13
13
|
* @category migrations
|
|
14
14
|
* @since 1.0.0-rc.0
|
|
15
15
|
*/
|
|
16
|
-
export declare const initialize: Effect.Effect<void, import("effect/
|
|
16
|
+
export declare const initialize: Effect.Effect<void, import("effect/sql/SqlError").SqlError, SqlClient.SqlClient>;
|
|
17
17
|
/**
|
|
18
18
|
* Registers a live journal's cache invalidation until its scope closes.
|
|
19
19
|
* Multiple journals over the same SQL client must observe the same rewind.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"JournalGeneration.d.ts","sourceRoot":"","sources":["../../src/JournalGeneration.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,SAAS,MAAM
|
|
1
|
+
{"version":3,"file":"JournalGeneration.d.ts","sourceRoot":"","sources":["../../src/JournalGeneration.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,SAAS,MAAM,sBAAsB,CAAA;AAEjD;;;;;;;GAOG;AACH,eAAO,MAAM,UAAU,kFAqBrB,CAAA;AAIF;;;;;;GAMG;AACH,eAAO,MAAM,UAAU,GAAI,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC,KAAK,IAAI,mFAgBtE,CAAA;AAEJ;;;;;;;GAOG;AACH,eAAO,MAAM,MAAM,GAAI,QAAQ,aAAa,CAAC,MAAM,CAAC,oDAMhD,CAAA"}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import * as Dialect from "@smthrs/database/Dialect";
|
|
7
7
|
import * as Effect from "effect/Effect";
|
|
8
|
-
import * as SqlClient from "effect/
|
|
8
|
+
import * as SqlClient from "effect/sql/SqlClient";
|
|
9
9
|
/**
|
|
10
10
|
* Installs the generation table idempotently. Both the SQL journal and time
|
|
11
11
|
* travel initialize it, including databases whose migration ladder already
|
|
@@ -19,13 +19,19 @@ export const initialize = Effect.gen(function* () {
|
|
|
19
19
|
// A read-only observer reads the schema it finds and installs nothing.
|
|
20
20
|
if (Dialect.isReadOnly(sql))
|
|
21
21
|
return;
|
|
22
|
-
|
|
22
|
+
const create = sql `
|
|
23
23
|
CREATE TABLE IF NOT EXISTS flows_journal_generations (
|
|
24
24
|
run_id TEXT PRIMARY KEY NOT NULL CHECK (length(run_id) > 0),
|
|
25
25
|
generation ${Dialect.integer(sql)} NOT NULL CHECK (${Dialect.isInteger(sql, sql `generation`)} AND generation > 0 AND generation <= 9007199254740991),
|
|
26
26
|
after_seq ${Dialect.integer(sql)} NOT NULL CHECK (${Dialect.isInteger(sql, sql `after_seq`)} AND after_seq >= -1 AND after_seq <= 9007199254740991)
|
|
27
27
|
)
|
|
28
28
|
`;
|
|
29
|
+
// The journal layer and time travel's migration both run this, side by side
|
|
30
|
+
// on a host that builds them together. PostgreSQL serializes only what opens
|
|
31
|
+
// a transaction, which takes the schema's writer lock first: two bare
|
|
32
|
+
// CREATE TABLE IF NOT EXISTS statements race in the catalog, and the loser
|
|
33
|
+
// fails on `pg_type_typname_nsp_index`.
|
|
34
|
+
yield* sql.onDialectOrElse({ pg: () => sql.withTransaction(create), orElse: () => create });
|
|
29
35
|
});
|
|
30
36
|
const journals = new WeakMap();
|
|
31
37
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"JournalGeneration.js","sourceRoot":"","sources":["../../src/JournalGeneration.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,OAAO,MAAM,0BAA0B,CAAA;AACnD,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,SAAS,MAAM
|
|
1
|
+
{"version":3,"file":"JournalGeneration.js","sourceRoot":"","sources":["../../src/JournalGeneration.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,OAAO,MAAM,0BAA0B,CAAA;AACnD,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,SAAS,MAAM,sBAAsB,CAAA;AAEjD;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAC5C,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,SAAS,CAAC,SAAS,CAAA;IACtC,uEAAuE;IACvE,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAM;IACnC,MAAM,MAAM,GAAG,GAAG,CAAA;;;mBAGD,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,oBACnC,OAAO,CAAC,SAAS,CAAC,GAAG,EAAE,GAAG,CAAA,YAAY,CACxC;kBACgB,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,oBAClC,OAAO,CAAC,SAAS,CAAC,GAAG,EAAE,GAAG,CAAA,WAAW,CACvC;;GAEC,CAAA;IACD,4EAA4E;IAC5E,6EAA6E;IAC7E,sEAAsE;IACtE,2EAA2E;IAC3E,wCAAwC;IACxC,KAAK,CAAC,CAAC,GAAG,CAAC,eAAe,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,eAAe,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,MAAM,EAAE,CAAC,CAAA;AAC7F,CAAC,CAAC,CAAA;AAEF,MAAM,QAAQ,GAAG,IAAI,OAAO,EAAqE,CAAA;AAEjG;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,MAA+C,EAAE,EAAE,CAC5E,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,SAAS,CAAC,SAAS,CAAA;IACtC,IAAI,SAAS,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;IACjC,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;QAC5B,SAAS,GAAG,IAAI,GAAG,EAAE,CAAA;QACrB,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,SAAS,CAAC,CAAA;IAC9B,CAAC;IACD,MAAM,UAAU,GAAG,SAAS,CAAA;IAC5B,KAAK,CAAC,CAAC,MAAM,CAAC,cAAc,CAC1B,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,EACzC,GAAG,EAAE,CACH,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE;QACf,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;IAC3B,CAAC,CAAC,CACL,CAAA;AACH,CAAC,CAAC,CAAA;AAEJ;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,MAA6B,EAAE,EAAE,CACtD,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,SAAS,CAAC,SAAS,CAAA;IACtC,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE;QACtB,KAAK,MAAM,UAAU,IAAI,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE;YAAE,UAAU,CAAC,MAAM,CAAC,CAAA;IACtE,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA","sourcesContent":["/**\n * Durable generations distinguish histories that reuse journal sequences.\n *\n * @since 1.0.0-rc.0\n */\n\nimport * as Dialect from \"@smthrs/database/Dialect\"\nimport * as Effect from \"effect/Effect\"\nimport * as SqlClient from \"effect/sql/SqlClient\"\n\n/**\n * Installs the generation table idempotently. Both the SQL journal and time\n * travel initialize it, including databases whose migration ladder already\n * passed the journal's reserved block. A read-only client installs nothing.\n *\n * @category migrations\n * @since 1.0.0-rc.0\n */\nexport const initialize = Effect.gen(function*() {\n const sql = yield* SqlClient.SqlClient\n // A read-only observer reads the schema it finds and installs nothing.\n if (Dialect.isReadOnly(sql)) return\n const create = sql`\n CREATE TABLE IF NOT EXISTS flows_journal_generations (\n run_id TEXT PRIMARY KEY NOT NULL CHECK (length(run_id) > 0),\n generation ${Dialect.integer(sql)} NOT NULL CHECK (${\n Dialect.isInteger(sql, sql`generation`)\n } AND generation > 0 AND generation <= 9007199254740991),\n after_seq ${Dialect.integer(sql)} NOT NULL CHECK (${\n Dialect.isInteger(sql, sql`after_seq`)\n } AND after_seq >= -1 AND after_seq <= 9007199254740991)\n )\n `\n // The journal layer and time travel's migration both run this, side by side\n // on a host that builds them together. PostgreSQL serializes only what opens\n // a transaction, which takes the schema's writer lock first: two bare\n // CREATE TABLE IF NOT EXISTS statements race in the catalog, and the loser\n // fails on `pg_type_typname_nsp_index`.\n yield* sql.onDialectOrElse({ pg: () => sql.withTransaction(create), orElse: () => create })\n})\n\nconst journals = new WeakMap<SqlClient.SqlClient, Set<(runIds: ReadonlyArray<string>) => void>>()\n\n/**\n * Registers a live journal's cache invalidation until its scope closes.\n * Multiple journals over the same SQL client must observe the same rewind.\n *\n * @category lifecycle\n * @since 1.0.0-rc.0\n */\nexport const onTruncate = (forget: (runIds: ReadonlyArray<string>) => void) =>\n Effect.gen(function*() {\n const sql = yield* SqlClient.SqlClient\n let listeners = journals.get(sql)\n if (listeners === undefined) {\n listeners = new Set()\n journals.set(sql, listeners)\n }\n const registered = listeners\n yield* Effect.acquireRelease(\n Effect.sync(() => registered.add(forget)),\n () =>\n Effect.sync(() => {\n registered.delete(forget)\n })\n )\n })\n\n/**\n * Forgets cached identities and allocation floors after a committed truncation.\n * The rewinder must quiesce producers and flush pending admissions first.\n * Database rows remain authoritative; this operation changes no durable data.\n *\n * @category lifecycle\n * @since 1.0.0-rc.0\n */\nexport const forget = (runIds: ReadonlyArray<string>) =>\n Effect.gen(function*() {\n const sql = yield* SqlClient.SqlClient\n yield* Effect.sync(() => {\n for (const invalidate of journals.get(sql) ?? []) invalidate(runIds)\n })\n })\n"]}
|
package/dist/esm/Migrations.d.ts
CHANGED
|
@@ -30,7 +30,7 @@ export declare const set: DatabaseMigrations.MigrationSet;
|
|
|
30
30
|
* @category migrations
|
|
31
31
|
* @since 0.1.0
|
|
32
32
|
*/
|
|
33
|
-
export declare const run: import("effect/Effect").Effect<readonly (readonly [id: number, name: string])[], import("effect/
|
|
33
|
+
export declare const run: import("effect/Effect").Effect<readonly (readonly [id: number, name: string])[], import("effect/sql/SqlError").SqlError | import("effect/sql/Migrator").MigrationError, import("effect/sql/SqlClient").SqlClient>;
|
|
34
34
|
/**
|
|
35
35
|
* Layer that runs journal migrations before exposing the database to journal
|
|
36
36
|
* services.
|
|
@@ -38,5 +38,5 @@ export declare const run: import("effect/Effect").Effect<readonly (readonly [id:
|
|
|
38
38
|
* @category layers
|
|
39
39
|
* @since 0.1.0
|
|
40
40
|
*/
|
|
41
|
-
export declare const layer: Layer.Layer<never, import("effect/
|
|
41
|
+
export declare const layer: Layer.Layer<never, import("effect/sql/SqlError").SqlError | import("effect/sql/Migrator").MigrationError, import("effect/sql/SqlClient").SqlClient>;
|
|
42
42
|
//# sourceMappingURL=Migrations.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Migrations.d.ts","sourceRoot":"","sources":["../../src/Migrations.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,kBAAkB,MAAM,6BAA6B,CAAA;AACjE,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;AAQrC;;;;;;GAMG;AACH,eAAO,MAAM,GAAG,EAAE,kBAAkB,CAAC,YAWpC,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,GAAG,
|
|
1
|
+
{"version":3,"file":"Migrations.d.ts","sourceRoot":"","sources":["../../src/Migrations.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,kBAAkB,MAAM,6BAA6B,CAAA;AACjE,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;AAQrC;;;;;;GAMG;AACH,eAAO,MAAM,GAAG,EAAE,kBAAkB,CAAC,YAWpC,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,GAAG,mNAAgC,CAAA;AAEhD;;;;;;GAMG;AACH,eAAO,MAAM,KAAK,qJAA2B,CAAA"}
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
import { DurableWriter } from "@smthrs/database/DurableWriter";
|
|
25
25
|
import * as Effect from "effect/Effect";
|
|
26
26
|
import * as Layer from "effect/Layer";
|
|
27
|
-
import * as SqlClient from "effect/
|
|
27
|
+
import * as SqlClient from "effect/sql/SqlClient";
|
|
28
28
|
import { Consensus, type Service } from "./Consensus.ts";
|
|
29
29
|
/**
|
|
30
30
|
* Constructs the database-backed strategy over the context's SQL client and
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SqlConsensus.d.ts","sourceRoot":"","sources":["../../src/SqlConsensus.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAGH,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAA;AAE9D,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;AACrC,OAAO,KAAK,SAAS,MAAM
|
|
1
|
+
{"version":3,"file":"SqlConsensus.d.ts","sourceRoot":"","sources":["../../src/SqlConsensus.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAGH,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAA;AAE9D,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;AACrC,OAAO,KAAK,SAAS,MAAM,sBAAsB,CAAA;AACjD,OAAO,EAIL,SAAS,EAUT,KAAK,OAAO,EACb,MAAM,gBAAgB,CAAA;AAoBvB;;;;;;GAMG;AACH,eAAO,MAAM,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,KAAK,EAAE,aAAa,GAAG,SAAS,CAAC,SAAS,CAuQlF,CAAA;AAEF;;;;;GAKG;AACH,eAAO,MAAM,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,SAAS,EAAE,KAAK,EAAE,aAAa,GAAG,SAAS,CAAC,SAAS,CAAiC,CAAA"}
|
package/dist/esm/SqlConsensus.js
CHANGED
|
@@ -26,7 +26,7 @@ import { DurableWriter } from "@smthrs/database/DurableWriter";
|
|
|
26
26
|
import * as Duration from "effect/Duration";
|
|
27
27
|
import * as Effect from "effect/Effect";
|
|
28
28
|
import * as Layer from "effect/Layer";
|
|
29
|
-
import * as SqlClient from "effect/
|
|
29
|
+
import * as SqlClient from "effect/sql/SqlClient";
|
|
30
30
|
import { Consensus, ConsensusError, fenceLost, heartbeatStaleAfter, matchesEvidence } from "./Consensus.js";
|
|
31
31
|
const staleAfterMs = Duration.toMillis(heartbeatStaleAfter);
|
|
32
32
|
const rejected = (reason) => ({ _tag: "Rejected", reason });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SqlConsensus.js","sourceRoot":"","sources":["../../src/SqlConsensus.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The default database-backed consensus strategy.\n *\n * This is the run store's former `flows_runs` ownership compare-and-swap\n * relocated: the owner tuple, the two-phase claim columns, claim recovery, and\n * the grant timestamp are the private schema of one strategy — the\n * `flows_consensus_leases` table — instead of a public contract on the run\n * row. The run row still mirrors the outcome, but the lease is what\n * arbitrates.\n *\n * Commit-time admission (R3) is exact: `guard` is a plain SELECT that joins\n * the caller's open write transaction — `DurableWriter` serializes write\n * transactions, so no reclaim can commit between the guard read and the\n * statements beside it — and every mutating operation runs through\n * `DurableWriter.write`, joining an enclosing transaction as a savepoint.\n * Prior art: Temporal's shard `rangeID` check\n * (`reference/temporal/service/history/shard/context_impl.go`,\n * `renewRangeLocked`).\n *\n * Governing design: `packages/smithers/flows/journal/docs/concepts/owner-fence.md`.\n *\n * @since 1.0.0\n */\n\nimport * as Dialect from \"@smthrs/database/Dialect\"\nimport { DurableWriter } from \"@smthrs/database/DurableWriter\"\nimport * as Duration from \"effect/Duration\"\nimport * as Effect from \"effect/Effect\"\nimport * as Layer from \"effect/Layer\"\nimport * as SqlClient from \"effect/unstable/sql/SqlClient\"\nimport {\n type ActivateOutcome,\n type Claimed,\n type ClaimOutcome,\n Consensus,\n ConsensusError,\n fenceLost,\n type HeartbeatOutcome,\n heartbeatStaleAfter,\n type LivenessEvidence,\n matchesEvidence,\n type RecoverOutcome,\n type Rejected,\n type RejectionReason,\n type Service\n} from \"./Consensus.ts\"\nimport type { OwnerId } from \"./OwnerId.ts\"\n\nconst staleAfterMs = Duration.toMillis(heartbeatStaleAfter)\n\nconst rejected = (reason: RejectionReason): Rejected => ({ _tag: \"Rejected\", reason })\nconst claimed = (grantedAtMs: number): Claimed => ({ _tag: \"Claimed\", grantedAtMs })\nconst lost = { _tag: \"Lost\" } as const\nconst activated = { _tag: \"Activated\" } as const\nconst recovered = { _tag: \"Recovered\" } as const\n\ninterface LeaseRow {\n readonly owner_host_id: string | null\n readonly owner_pid: number | null\n readonly owner_nonce: string | null\n readonly claim_host_id: string | null\n readonly claim_pid: number | null\n readonly claim_nonce: string | null\n}\n\n/**\n * Constructs the database-backed strategy over the context's SQL client and\n * durable writer.\n *\n * @since 1.0.0\n * @category constructors\n */\nexport const make: Effect.Effect<Service, never, DurableWriter | SqlClient.SqlClient> = Effect.gen(function*() {\n const sql = yield* Effect.service(SqlClient.SqlClient)\n const writer = yield* DurableWriter\n\n const strategyError = (cause: unknown): ConsensusError =>\n new ConsensusError({\n code: \"persistence_failed\",\n message: \"consensus lease operation failed\",\n cause\n })\n\n // The original cause is preserved through the wrapper so an enclosing\n // `DurableWriter.write`'s retry classification still sees a transient\n // conflict raised by a savepoint-nested lease statement.\n const write = <A>(\n body: Effect.Effect<A, unknown>\n ): Effect.Effect<A, ConsensusError> =>\n writer.write(body).pipe(Effect.mapError((cause) => cause instanceof ConsensusError ? cause : strategyError(cause)))\n\n const leaseOf = (runId: string) =>\n sql<LeaseRow>`\n SELECT owner_host_id, owner_pid, owner_nonce, claim_host_id, claim_pid, claim_nonce\n FROM flows_consensus_leases\n WHERE run_id = ${runId}\n `\n\n const prune = (runId: string) =>\n sql`\n DELETE FROM flows_consensus_leases\n WHERE run_id = ${runId} AND owner_host_id IS NULL AND claim_host_id IS NULL\n `\n\n const claim: Service[\"claim\"] = Effect.fn(\"Consensus.claim\")((\n runId: string,\n claimant: OwnerId,\n nowMs: number\n ) =>\n write(Effect.gen(function*() {\n const rows = yield* sql<{ readonly run_id: string }>`\n INSERT INTO flows_consensus_leases (run_id, claim_host_id, claim_pid, claim_nonce, claimed_at_ms)\n VALUES (${runId}, ${claimant.hostId}, ${claimant.pid}, ${claimant.nonce}, ${nowMs})\n ON CONFLICT (run_id) DO UPDATE SET\n claim_host_id = excluded.claim_host_id,\n claim_pid = excluded.claim_pid,\n claim_nonce = excluded.claim_nonce,\n claimed_at_ms = excluded.claimed_at_ms\n WHERE flows_consensus_leases.claim_host_id IS NULL\n AND flows_consensus_leases.owner_host_id IS NULL\n RETURNING run_id\n `\n if (rows.length > 0) return claimed(nowMs)\n // The upsert only misses on a conflicting row, so the lease exists and\n // holds a claim, an owner, or both.\n const lease = (yield* leaseOf(runId))[0]!\n return lease.claim_host_id !== null ? rejected(\"already_claimed\") : rejected(\"owner_held\")\n }))\n )\n\n const activate: Service[\"activate\"] = Effect.fn(\"Consensus.activate\")((\n runId: string,\n owner: OwnerId,\n grantedAtMs: number,\n nowMs: number\n ) =>\n write(Effect.gen(function*() {\n const rows = yield* sql<{ readonly run_id: string }>`\n UPDATE flows_consensus_leases\n SET\n owner_host_id = ${owner.hostId},\n owner_pid = ${owner.pid},\n owner_nonce = ${owner.nonce},\n granted_at_ms = ${grantedAtMs},\n heartbeat_at_ms = ${nowMs},\n claim_host_id = NULL,\n claim_pid = NULL,\n claim_nonce = NULL,\n claimed_at_ms = NULL\n WHERE run_id = ${runId}\n AND claim_host_id = ${owner.hostId}\n AND claim_pid = ${owner.pid}\n AND claim_nonce = ${owner.nonce}\n AND claimed_at_ms = ${grantedAtMs}\n RETURNING run_id\n `\n return (rows.length > 0 ? activated : lost) satisfies ActivateOutcome\n }))\n )\n\n const heartbeat: Service[\"heartbeat\"] = Effect.fn(\"Consensus.heartbeat\")((\n runId: string,\n owner: OwnerId,\n nowMs: number\n ) =>\n write(Effect.gen(function*() {\n // Monotonic: a pulse delayed past a newer one from the same owner never\n // moves the lease backwards and never makes a live run look stale.\n const rows = yield* sql<{ readonly heartbeat_at_ms: number }>`\n UPDATE flows_consensus_leases\n SET heartbeat_at_ms = ${Dialect.greatest(sql)}(heartbeat_at_ms, ${nowMs})\n WHERE run_id = ${runId}\n AND owner_host_id = ${owner.hostId}\n AND owner_pid = ${owner.pid}\n AND owner_nonce = ${owner.nonce}\n RETURNING heartbeat_at_ms\n `\n const row = rows[0]\n return (row === undefined\n ? lost\n : { _tag: \"Renewed\", heartbeatAtMs: Number(row.heartbeat_at_ms) }) satisfies HeartbeatOutcome\n }))\n )\n\n const reconfirm: Service[\"reconfirm\"] = Effect.fn(\"Consensus.reconfirm\")((\n runId: string,\n owner: OwnerId,\n nowMs: number\n ) =>\n write(Effect.gen(function*() {\n // A pending steal has already reserved the lease, even before activation.\n // Compare both owner and claim in the same update as the renewal.\n const rows = yield* sql<{ readonly heartbeat_at_ms: number }>`\n UPDATE flows_consensus_leases\n SET heartbeat_at_ms = ${Dialect.greatest(sql)}(heartbeat_at_ms, ${nowMs})\n WHERE run_id = ${runId}\n AND owner_host_id = ${owner.hostId}\n AND owner_pid = ${owner.pid}\n AND owner_nonce = ${owner.nonce}\n AND claim_host_id IS NULL\n RETURNING heartbeat_at_ms\n `\n const row = rows[0]\n return (row === undefined\n ? lost\n : { _tag: \"Renewed\", heartbeatAtMs: Number(row.heartbeat_at_ms) }) satisfies HeartbeatOutcome\n }))\n )\n\n const release: Service[\"release\"] = Effect.fn(\"Consensus.release\")((runId: string, owner: OwnerId) =>\n write(Effect.gen(function*() {\n yield* sql`\n UPDATE flows_consensus_leases\n SET claim_host_id = NULL, claim_pid = NULL, claim_nonce = NULL, claimed_at_ms = NULL\n WHERE run_id = ${runId}\n AND claim_host_id = ${owner.hostId}\n AND claim_pid = ${owner.pid}\n AND claim_nonce = ${owner.nonce}\n `\n yield* sql`\n UPDATE flows_consensus_leases\n SET owner_host_id = NULL, owner_pid = NULL, owner_nonce = NULL,\n granted_at_ms = NULL, heartbeat_at_ms = NULL\n WHERE run_id = ${runId}\n AND owner_host_id = ${owner.hostId}\n AND owner_pid = ${owner.pid}\n AND owner_nonce = ${owner.nonce}\n `\n yield* prune(runId)\n }))\n )\n\n const steal: Service[\"steal\"] = Effect.fn(\"Consensus.steal\")((\n runId: string,\n claimant: OwnerId,\n nowMs: number,\n evidence: LivenessEvidence\n ) =>\n Effect.suspend((): Effect.Effect<ClaimOutcome, ConsensusError> => {\n if (!matchesEvidence(evidence.expectedOwner, claimant, nowMs, evidence)) {\n return Effect.succeed(rejected(\"evidence_invalid\"))\n }\n return write(Effect.gen(function*() {\n const rows = yield* sql<{ readonly run_id: string }>`\n UPDATE flows_consensus_leases\n SET\n claim_host_id = ${claimant.hostId},\n claim_pid = ${claimant.pid},\n claim_nonce = ${claimant.nonce},\n claimed_at_ms = ${nowMs}\n WHERE run_id = ${runId}\n AND owner_host_id = ${evidence.expectedOwner.hostId}\n AND owner_pid = ${evidence.expectedOwner.pid}\n AND owner_nonce = ${evidence.expectedOwner.nonce}\n AND heartbeat_at_ms < ${nowMs - staleAfterMs}\n AND claim_host_id IS NULL\n RETURNING run_id\n `\n if (rows.length > 0) return claimed(nowMs)\n const lease = (yield* leaseOf(runId))[0]\n if (lease !== undefined && lease.claim_host_id !== null) return rejected(\"already_claimed\")\n if (\n lease === undefined ||\n lease.owner_host_id !== evidence.expectedOwner.hostId ||\n Number(lease.owner_pid) !== evidence.expectedOwner.pid ||\n lease.owner_nonce !== evidence.expectedOwner.nonce\n ) {\n // A run nobody owns, or one owned by somebody else, has no owner\n // this evidence could disprove.\n return rejected(\"evidence_invalid\")\n }\n return rejected(\"owner_live\")\n }))\n })\n )\n\n const recover: Service[\"recover\"] = Effect.fn(\"Consensus.recover\")((\n runId: string,\n staleClaimant: OwnerId,\n grantedAtMs: number,\n observer: OwnerId,\n nowMs: number,\n evidence: LivenessEvidence\n ) =>\n Effect.suspend((): Effect.Effect<RecoverOutcome, ConsensusError> => {\n if (!matchesEvidence(staleClaimant, observer, nowMs, evidence)) {\n return Effect.succeed(rejected(\"evidence_invalid\"))\n }\n return write(Effect.gen(function*() {\n const rows = yield* sql<{ readonly run_id: string }>`\n UPDATE flows_consensus_leases\n SET claim_host_id = NULL, claim_pid = NULL, claim_nonce = NULL, claimed_at_ms = NULL\n WHERE run_id = ${runId}\n AND claim_host_id = ${staleClaimant.hostId}\n AND claim_pid = ${staleClaimant.pid}\n AND claim_nonce = ${staleClaimant.nonce}\n AND claimed_at_ms = ${grantedAtMs}\n AND claimed_at_ms < ${nowMs - staleAfterMs}\n RETURNING run_id\n `\n if (rows.length > 0) {\n yield* prune(runId)\n return recovered\n }\n const matches = yield* sql<{ readonly run_id: string }>`\n SELECT run_id FROM flows_consensus_leases\n WHERE run_id = ${runId}\n AND claim_host_id = ${staleClaimant.hostId}\n AND claim_pid = ${staleClaimant.pid}\n AND claim_nonce = ${staleClaimant.nonce}\n AND claimed_at_ms = ${grantedAtMs}\n `\n // The exact claim survived the compare-and-swap only because it is\n // not yet stale; anything else means the claim moved on.\n return matches.length > 0 ? rejected(\"claim_fresh\") : rejected(\"claim_changed\")\n }))\n })\n )\n\n const guard: Service[\"guard\"] = Effect.fn(\"Consensus.guard\")((runId: string, owner: OwnerId) =>\n write(Effect.gen(function*() {\n const held = yield* sql<{ readonly ok: number }>`\n SELECT 1 AS ok FROM flows_consensus_leases\n WHERE run_id = ${runId}\n AND owner_host_id = ${owner.hostId}\n AND owner_pid = ${owner.pid}\n AND owner_nonce = ${owner.nonce}\n `\n if (held.length === 0) {\n return yield* Effect.fail(fenceLost(runId, owner))\n }\n }))\n )\n\n return Consensus.of({ claim, activate, heartbeat, reconfirm, release, steal, recover, guard })\n})\n\n/**\n * Provides the database-backed strategy.\n *\n * @since 1.0.0\n * @category layers\n */\nexport const layer: Layer.Layer<Consensus, never, DurableWriter | SqlClient.SqlClient> = Layer.effect(Consensus, make)\n"]}
|
|
1
|
+
{"version":3,"file":"SqlConsensus.js","sourceRoot":"","sources":["../../src/SqlConsensus.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The default database-backed consensus strategy.\n *\n * This is the run store's former `flows_runs` ownership compare-and-swap\n * relocated: the owner tuple, the two-phase claim columns, claim recovery, and\n * the grant timestamp are the private schema of one strategy — the\n * `flows_consensus_leases` table — instead of a public contract on the run\n * row. The run row still mirrors the outcome, but the lease is what\n * arbitrates.\n *\n * Commit-time admission (R3) is exact: `guard` is a plain SELECT that joins\n * the caller's open write transaction — `DurableWriter` serializes write\n * transactions, so no reclaim can commit between the guard read and the\n * statements beside it — and every mutating operation runs through\n * `DurableWriter.write`, joining an enclosing transaction as a savepoint.\n * Prior art: Temporal's shard `rangeID` check\n * (`reference/temporal/service/history/shard/context_impl.go`,\n * `renewRangeLocked`).\n *\n * Governing design: `packages/smithers/flows/journal/docs/concepts/owner-fence.md`.\n *\n * @since 1.0.0\n */\n\nimport * as Dialect from \"@smthrs/database/Dialect\"\nimport { DurableWriter } from \"@smthrs/database/DurableWriter\"\nimport * as Duration from \"effect/Duration\"\nimport * as Effect from \"effect/Effect\"\nimport * as Layer from \"effect/Layer\"\nimport * as SqlClient from \"effect/sql/SqlClient\"\nimport {\n type ActivateOutcome,\n type Claimed,\n type ClaimOutcome,\n Consensus,\n ConsensusError,\n fenceLost,\n type HeartbeatOutcome,\n heartbeatStaleAfter,\n type LivenessEvidence,\n matchesEvidence,\n type RecoverOutcome,\n type Rejected,\n type RejectionReason,\n type Service\n} from \"./Consensus.ts\"\nimport type { OwnerId } from \"./OwnerId.ts\"\n\nconst staleAfterMs = Duration.toMillis(heartbeatStaleAfter)\n\nconst rejected = (reason: RejectionReason): Rejected => ({ _tag: \"Rejected\", reason })\nconst claimed = (grantedAtMs: number): Claimed => ({ _tag: \"Claimed\", grantedAtMs })\nconst lost = { _tag: \"Lost\" } as const\nconst activated = { _tag: \"Activated\" } as const\nconst recovered = { _tag: \"Recovered\" } as const\n\ninterface LeaseRow {\n readonly owner_host_id: string | null\n readonly owner_pid: number | null\n readonly owner_nonce: string | null\n readonly claim_host_id: string | null\n readonly claim_pid: number | null\n readonly claim_nonce: string | null\n}\n\n/**\n * Constructs the database-backed strategy over the context's SQL client and\n * durable writer.\n *\n * @since 1.0.0\n * @category constructors\n */\nexport const make: Effect.Effect<Service, never, DurableWriter | SqlClient.SqlClient> = Effect.gen(function*() {\n const sql = yield* Effect.service(SqlClient.SqlClient)\n const writer = yield* DurableWriter\n\n const strategyError = (cause: unknown): ConsensusError =>\n new ConsensusError({\n code: \"persistence_failed\",\n message: \"consensus lease operation failed\",\n cause\n })\n\n // The original cause is preserved through the wrapper so an enclosing\n // `DurableWriter.write`'s retry classification still sees a transient\n // conflict raised by a savepoint-nested lease statement.\n const write = <A>(\n body: Effect.Effect<A, unknown>\n ): Effect.Effect<A, ConsensusError> =>\n writer.write(body).pipe(Effect.mapError((cause) => cause instanceof ConsensusError ? cause : strategyError(cause)))\n\n const leaseOf = (runId: string) =>\n sql<LeaseRow>`\n SELECT owner_host_id, owner_pid, owner_nonce, claim_host_id, claim_pid, claim_nonce\n FROM flows_consensus_leases\n WHERE run_id = ${runId}\n `\n\n const prune = (runId: string) =>\n sql`\n DELETE FROM flows_consensus_leases\n WHERE run_id = ${runId} AND owner_host_id IS NULL AND claim_host_id IS NULL\n `\n\n const claim: Service[\"claim\"] = Effect.fn(\"Consensus.claim\")((\n runId: string,\n claimant: OwnerId,\n nowMs: number\n ) =>\n write(Effect.gen(function*() {\n const rows = yield* sql<{ readonly run_id: string }>`\n INSERT INTO flows_consensus_leases (run_id, claim_host_id, claim_pid, claim_nonce, claimed_at_ms)\n VALUES (${runId}, ${claimant.hostId}, ${claimant.pid}, ${claimant.nonce}, ${nowMs})\n ON CONFLICT (run_id) DO UPDATE SET\n claim_host_id = excluded.claim_host_id,\n claim_pid = excluded.claim_pid,\n claim_nonce = excluded.claim_nonce,\n claimed_at_ms = excluded.claimed_at_ms\n WHERE flows_consensus_leases.claim_host_id IS NULL\n AND flows_consensus_leases.owner_host_id IS NULL\n RETURNING run_id\n `\n if (rows.length > 0) return claimed(nowMs)\n // The upsert only misses on a conflicting row, so the lease exists and\n // holds a claim, an owner, or both.\n const lease = (yield* leaseOf(runId))[0]!\n return lease.claim_host_id !== null ? rejected(\"already_claimed\") : rejected(\"owner_held\")\n }))\n )\n\n const activate: Service[\"activate\"] = Effect.fn(\"Consensus.activate\")((\n runId: string,\n owner: OwnerId,\n grantedAtMs: number,\n nowMs: number\n ) =>\n write(Effect.gen(function*() {\n const rows = yield* sql<{ readonly run_id: string }>`\n UPDATE flows_consensus_leases\n SET\n owner_host_id = ${owner.hostId},\n owner_pid = ${owner.pid},\n owner_nonce = ${owner.nonce},\n granted_at_ms = ${grantedAtMs},\n heartbeat_at_ms = ${nowMs},\n claim_host_id = NULL,\n claim_pid = NULL,\n claim_nonce = NULL,\n claimed_at_ms = NULL\n WHERE run_id = ${runId}\n AND claim_host_id = ${owner.hostId}\n AND claim_pid = ${owner.pid}\n AND claim_nonce = ${owner.nonce}\n AND claimed_at_ms = ${grantedAtMs}\n RETURNING run_id\n `\n return (rows.length > 0 ? activated : lost) satisfies ActivateOutcome\n }))\n )\n\n const heartbeat: Service[\"heartbeat\"] = Effect.fn(\"Consensus.heartbeat\")((\n runId: string,\n owner: OwnerId,\n nowMs: number\n ) =>\n write(Effect.gen(function*() {\n // Monotonic: a pulse delayed past a newer one from the same owner never\n // moves the lease backwards and never makes a live run look stale.\n const rows = yield* sql<{ readonly heartbeat_at_ms: number }>`\n UPDATE flows_consensus_leases\n SET heartbeat_at_ms = ${Dialect.greatest(sql)}(heartbeat_at_ms, ${nowMs})\n WHERE run_id = ${runId}\n AND owner_host_id = ${owner.hostId}\n AND owner_pid = ${owner.pid}\n AND owner_nonce = ${owner.nonce}\n RETURNING heartbeat_at_ms\n `\n const row = rows[0]\n return (row === undefined\n ? lost\n : { _tag: \"Renewed\", heartbeatAtMs: Number(row.heartbeat_at_ms) }) satisfies HeartbeatOutcome\n }))\n )\n\n const reconfirm: Service[\"reconfirm\"] = Effect.fn(\"Consensus.reconfirm\")((\n runId: string,\n owner: OwnerId,\n nowMs: number\n ) =>\n write(Effect.gen(function*() {\n // A pending steal has already reserved the lease, even before activation.\n // Compare both owner and claim in the same update as the renewal.\n const rows = yield* sql<{ readonly heartbeat_at_ms: number }>`\n UPDATE flows_consensus_leases\n SET heartbeat_at_ms = ${Dialect.greatest(sql)}(heartbeat_at_ms, ${nowMs})\n WHERE run_id = ${runId}\n AND owner_host_id = ${owner.hostId}\n AND owner_pid = ${owner.pid}\n AND owner_nonce = ${owner.nonce}\n AND claim_host_id IS NULL\n RETURNING heartbeat_at_ms\n `\n const row = rows[0]\n return (row === undefined\n ? lost\n : { _tag: \"Renewed\", heartbeatAtMs: Number(row.heartbeat_at_ms) }) satisfies HeartbeatOutcome\n }))\n )\n\n const release: Service[\"release\"] = Effect.fn(\"Consensus.release\")((runId: string, owner: OwnerId) =>\n write(Effect.gen(function*() {\n yield* sql`\n UPDATE flows_consensus_leases\n SET claim_host_id = NULL, claim_pid = NULL, claim_nonce = NULL, claimed_at_ms = NULL\n WHERE run_id = ${runId}\n AND claim_host_id = ${owner.hostId}\n AND claim_pid = ${owner.pid}\n AND claim_nonce = ${owner.nonce}\n `\n yield* sql`\n UPDATE flows_consensus_leases\n SET owner_host_id = NULL, owner_pid = NULL, owner_nonce = NULL,\n granted_at_ms = NULL, heartbeat_at_ms = NULL\n WHERE run_id = ${runId}\n AND owner_host_id = ${owner.hostId}\n AND owner_pid = ${owner.pid}\n AND owner_nonce = ${owner.nonce}\n `\n yield* prune(runId)\n }))\n )\n\n const steal: Service[\"steal\"] = Effect.fn(\"Consensus.steal\")((\n runId: string,\n claimant: OwnerId,\n nowMs: number,\n evidence: LivenessEvidence\n ) =>\n Effect.suspend((): Effect.Effect<ClaimOutcome, ConsensusError> => {\n if (!matchesEvidence(evidence.expectedOwner, claimant, nowMs, evidence)) {\n return Effect.succeed(rejected(\"evidence_invalid\"))\n }\n return write(Effect.gen(function*() {\n const rows = yield* sql<{ readonly run_id: string }>`\n UPDATE flows_consensus_leases\n SET\n claim_host_id = ${claimant.hostId},\n claim_pid = ${claimant.pid},\n claim_nonce = ${claimant.nonce},\n claimed_at_ms = ${nowMs}\n WHERE run_id = ${runId}\n AND owner_host_id = ${evidence.expectedOwner.hostId}\n AND owner_pid = ${evidence.expectedOwner.pid}\n AND owner_nonce = ${evidence.expectedOwner.nonce}\n AND heartbeat_at_ms < ${nowMs - staleAfterMs}\n AND claim_host_id IS NULL\n RETURNING run_id\n `\n if (rows.length > 0) return claimed(nowMs)\n const lease = (yield* leaseOf(runId))[0]\n if (lease !== undefined && lease.claim_host_id !== null) return rejected(\"already_claimed\")\n if (\n lease === undefined ||\n lease.owner_host_id !== evidence.expectedOwner.hostId ||\n Number(lease.owner_pid) !== evidence.expectedOwner.pid ||\n lease.owner_nonce !== evidence.expectedOwner.nonce\n ) {\n // A run nobody owns, or one owned by somebody else, has no owner\n // this evidence could disprove.\n return rejected(\"evidence_invalid\")\n }\n return rejected(\"owner_live\")\n }))\n })\n )\n\n const recover: Service[\"recover\"] = Effect.fn(\"Consensus.recover\")((\n runId: string,\n staleClaimant: OwnerId,\n grantedAtMs: number,\n observer: OwnerId,\n nowMs: number,\n evidence: LivenessEvidence\n ) =>\n Effect.suspend((): Effect.Effect<RecoverOutcome, ConsensusError> => {\n if (!matchesEvidence(staleClaimant, observer, nowMs, evidence)) {\n return Effect.succeed(rejected(\"evidence_invalid\"))\n }\n return write(Effect.gen(function*() {\n const rows = yield* sql<{ readonly run_id: string }>`\n UPDATE flows_consensus_leases\n SET claim_host_id = NULL, claim_pid = NULL, claim_nonce = NULL, claimed_at_ms = NULL\n WHERE run_id = ${runId}\n AND claim_host_id = ${staleClaimant.hostId}\n AND claim_pid = ${staleClaimant.pid}\n AND claim_nonce = ${staleClaimant.nonce}\n AND claimed_at_ms = ${grantedAtMs}\n AND claimed_at_ms < ${nowMs - staleAfterMs}\n RETURNING run_id\n `\n if (rows.length > 0) {\n yield* prune(runId)\n return recovered\n }\n const matches = yield* sql<{ readonly run_id: string }>`\n SELECT run_id FROM flows_consensus_leases\n WHERE run_id = ${runId}\n AND claim_host_id = ${staleClaimant.hostId}\n AND claim_pid = ${staleClaimant.pid}\n AND claim_nonce = ${staleClaimant.nonce}\n AND claimed_at_ms = ${grantedAtMs}\n `\n // The exact claim survived the compare-and-swap only because it is\n // not yet stale; anything else means the claim moved on.\n return matches.length > 0 ? rejected(\"claim_fresh\") : rejected(\"claim_changed\")\n }))\n })\n )\n\n const guard: Service[\"guard\"] = Effect.fn(\"Consensus.guard\")((runId: string, owner: OwnerId) =>\n write(Effect.gen(function*() {\n const held = yield* sql<{ readonly ok: number }>`\n SELECT 1 AS ok FROM flows_consensus_leases\n WHERE run_id = ${runId}\n AND owner_host_id = ${owner.hostId}\n AND owner_pid = ${owner.pid}\n AND owner_nonce = ${owner.nonce}\n `\n if (held.length === 0) {\n return yield* Effect.fail(fenceLost(runId, owner))\n }\n }))\n )\n\n return Consensus.of({ claim, activate, heartbeat, reconfirm, release, steal, recover, guard })\n})\n\n/**\n * Provides the database-backed strategy.\n *\n * @since 1.0.0\n * @category layers\n */\nexport const layer: Layer.Layer<Consensus, never, DurableWriter | SqlClient.SqlClient> = Layer.effect(Consensus, make)\n"]}
|
package/dist/esm/SqlJournal.d.ts
CHANGED
|
@@ -21,7 +21,7 @@ import { DurableWriter } from "@smthrs/database/DurableWriter";
|
|
|
21
21
|
import * as Effect from "effect/Effect";
|
|
22
22
|
import * as Layer from "effect/Layer";
|
|
23
23
|
import * as Scope from "effect/Scope";
|
|
24
|
-
import * as SqlClient from "effect/
|
|
24
|
+
import * as SqlClient from "effect/sql/SqlClient";
|
|
25
25
|
import { Consensus } from "./Consensus.ts";
|
|
26
26
|
import { Journal, JournalError, type OverflowPolicy, type Service } from "./Journal.ts";
|
|
27
27
|
import { RunId, type Seq } from "./JournalEvent.ts";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SqlJournal.d.ts","sourceRoot":"","sources":["../../src/SqlJournal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,EAA8B,aAAa,EAAE,MAAM,gCAAgC,CAAA;AAI1F,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAEvC,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;AAOrC,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;
|
|
1
|
+
{"version":3,"file":"SqlJournal.d.ts","sourceRoot":"","sources":["../../src/SqlJournal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,EAA8B,aAAa,EAAE,MAAM,gCAAgC,CAAA;AAI1F,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAEvC,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;AAOrC,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;AAErC,OAAO,KAAK,SAAS,MAAM,sBAAsB,CAAA;AAGjD,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAE1C,OAAO,EASL,OAAO,EACP,YAAY,EAGZ,KAAK,cAAc,EACnB,KAAK,OAAO,EAEb,MAAM,cAAc,CAAA;AACrB,OAAO,EAKL,KAAK,EACL,KAAK,GAAG,EAGT,MAAM,mBAAmB,CAAA;AAK1B,OAAO,KAAK,SAAS,MAAM,gBAAgB,CAAA;AAM3C;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAA;IAC/B,QAAQ,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,KAAK,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;CAC/E;AAED;;;;;GAKG;AACH,MAAM,WAAW,iBAAiB;IAChC;;;;;;;;;OASG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,6DAA6D;IAC7D,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAA;IACjC,yDAAyD;IACzD,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACvC;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACH,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC9C;;;;;;;;;;;;;;OAcG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC3C;;;;;;;;;OASG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,CAAC,QAAQ,GAAG,SAAS,CAAA;IAChD;;;;OAIG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAAA;CACnD;AA+WD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,IAAI,GACf,SAAS,iBAAiB,KACzB,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,YAAY,EAAE,SAAS,GAAG,aAAa,GAAG,SAAS,CAAC,SAAS,GAAG,KAAK,CAAC,KAAK,CAqlEjG,CAAA;AAEJ;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,SAAS,GACpB,SAAS,iBAAiB,KACzB,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE,YAAY,EAAE,SAAS,GAAG,aAAa,GAAG,SAAS,CAAC,SAAS,CAC/C,CAAA;AAEtC;;;;;;;GAOG;AACH,eAAO,MAAM,KAAK,GAChB,SAAS,iBAAiB,KACzB,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE,YAAY,EAAE,aAAa,GAAG,SAAS,CAAC,SAAS,CACb,CAAA"}
|
package/dist/esm/SqlJournal.js
CHANGED
|
@@ -33,8 +33,8 @@ import * as Result from "effect/Result";
|
|
|
33
33
|
import * as Schema from "effect/Schema";
|
|
34
34
|
import * as Scope from "effect/Scope";
|
|
35
35
|
import * as Semaphore from "effect/Semaphore";
|
|
36
|
+
import * as SqlClient from "effect/sql/SqlClient";
|
|
36
37
|
import * as Stream from "effect/Stream";
|
|
37
|
-
import * as SqlClient from "effect/unstable/sql/SqlClient";
|
|
38
38
|
import { Consensus } from "./Consensus.js";
|
|
39
39
|
import { JournalInternalFault } from "./internal/JournalInternalFault.js";
|
|
40
40
|
import { Checkpoint, CheckpointOptions, CompactOptions, EntriesOptions, Journal, JournalError, make as makeJournal, maxEntriesLimit, StreamOptions } from "./Journal.js";
|
|
@@ -1797,7 +1797,7 @@ export const make = (options) => Effect.gen(function* () {
|
|
|
1797
1797
|
projection: activeProjection.name,
|
|
1798
1798
|
runId: activeOptions.runId,
|
|
1799
1799
|
...(activeOptions.afterSequence === undefined ? {} : { afterSequence: activeOptions.afterSequence })
|
|
1800
|
-
}).pipe(Effect.andThen(Effect.succeed(stream(activeOptions).pipe(Stream.scanEffect(activeProjection.initial, (state, entry) => Effect.suspend(() => activeProjection.reduce(state, entry)).pipe(Effect.catchCause((cause) => Cause.hasInterruptsOnly(cause)
|
|
1800
|
+
}).pipe(Effect.andThen(Effect.succeed(stream(activeOptions).pipe(Stream.scanEffect(() => activeProjection.initial, (state, entry) => Effect.suspend(() => activeProjection.reduce(state, entry)).pipe(Effect.catchCause((cause) => Cause.hasInterruptsOnly(cause)
|
|
1801
1801
|
? Effect.failCause(cause)
|
|
1802
1802
|
: Effect.fail(error("projection_failed", `projection ${activeProjection.name} failed`, cause))))))))))(projection, streamOptions));
|
|
1803
1803
|
yield* JournalGeneration.onTruncate((runIds) => {
|