@zio.dev/zio-blocks 0.0.51 → 0.0.56
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/adr/2026-07-18-data-migration.md +123 -0
- package/guides/async-getting-started.md +687 -0
- package/guides/compile-time-resource-safety-with-scope.md +6 -0
- package/guides/getting-started-with-mux.md +0 -112
- package/guides/query-dsl-extending.md +1 -1
- package/guides/query-dsl-fluent-builder.md +1 -1
- package/guides/query-dsl-reified-optics.md +1 -1
- package/guides/query-dsl-sql.md +395 -1
- package/guides/sql-checked-interpolation.md +173 -0
- package/guides/sql-transactions.md +286 -0
- package/guides/telemetry-guide.md +131 -70
- package/guides/zio-schema-migration.md +6 -6
- package/index.md +200 -559
- package/package.json +1 -1
- package/reference/async.md +1379 -531
- package/reference/chunk.md +3 -3
- package/reference/codegen/index.md +1 -1
- package/reference/combinators.md +4 -4
- package/reference/config/config-decoder.md +460 -0
- package/reference/config/config-source.md +489 -0
- package/reference/config/errors.md +278 -0
- package/reference/config/flags.md +369 -0
- package/reference/config/formats.md +314 -0
- package/reference/config/index.md +304 -0
- package/reference/config/rollout.md +336 -0
- package/reference/context.md +6 -49
- package/reference/data-migration.md +269 -0
- package/reference/datastar/attributes.md +302 -0
- package/reference/datastar/events.md +234 -0
- package/reference/datastar/index.md +256 -0
- package/reference/datastar/signals.md +230 -0
- package/reference/datastar/sse.md +295 -0
- package/reference/datastar.md +2 -2
- package/reference/docs.md +2 -2
- package/reference/endpoint/bulk-creation.md +96 -0
- package/reference/endpoint/endpoint.md +1 -0
- package/reference/endpoint/index.md +9 -89
- package/reference/endpoint/path-codec.md +12 -24
- package/reference/endpoint/route-pattern.md +4 -6
- package/reference/endpoint/segment-codec.md +19 -32
- package/reference/html.md +313 -9
- package/reference/htmx/index.md +4 -52
- package/reference/htmx/response-headers.md +240 -0
- package/reference/http-model/headers.md +735 -0
- package/reference/http-model/index.md +3 -1
- package/reference/http-model/model.md +107 -71
- package/reference/http-model/schema-codecs.md +522 -0
- package/reference/http-model/schema.md +6 -3
- package/reference/http-model/server-sent-event.md +341 -0
- package/reference/jwt.md +195 -0
- package/reference/maybe.md +128 -11
- package/reference/media-type.md +2 -2
- package/reference/mux.mdx +7 -2
- package/reference/openapi.md +3 -3
- package/reference/projection.md +654 -0
- package/reference/resource-management/index.md +1 -1
- package/reference/resource-management/resource.md +2 -98
- package/reference/resource-management/scope.md +1 -209
- package/reference/resource-management/wire.md +4 -50
- package/reference/ringbuffer/advanced.mdx +1 -1
- package/reference/ringbuffer/index.mdx +3 -3
- package/reference/ringbuffer/mpmc.mdx +38 -4
- package/reference/ringbuffer/mpsc.mdx +36 -4
- package/reference/ringbuffer/spmc.mdx +1 -1
- package/reference/ringbuffer/spsc.mdx +87 -15
- package/reference/schema/allows.md +0 -96
- package/reference/schema/binding.md +2 -2
- package/reference/schema/built-in-codecs/avro.md +2 -2
- package/reference/schema/built-in-codecs/bson.md +50 -20
- package/reference/schema/built-in-codecs/csv.md +2 -2
- package/reference/schema/built-in-codecs/index.md +3 -3
- package/reference/schema/built-in-codecs/json/index.md +2 -2
- package/reference/schema/built-in-codecs/json/json.md +1 -0
- package/reference/schema/built-in-codecs/messagepack.md +3 -3
- package/reference/schema/built-in-codecs/thrift.md +2 -2
- package/reference/schema/built-in-codecs/toon.md +3 -3
- package/reference/schema/built-in-codecs/yaml.md +2 -2
- package/reference/schema/codec.md +11 -11
- package/reference/schema/dynamic-optic.md +48 -3
- package/reference/schema/dynamic-schema.md +3 -3
- package/reference/schema/index.md +2 -0
- package/reference/schema/path-interpolator.md +2 -0
- package/reference/schema/reflect-transformer.md +140 -0
- package/reference/schema/schema-evolution/as.md +4 -4
- package/reference/schema/schema-evolution/into.md +2 -2
- package/reference/schema/schema-expr.md +2 -2
- package/reference/schema/schema-search.md +263 -0
- package/reference/schema/schema.md +10 -2
- package/reference/schema/type-class-derivation.md +1 -1
- package/reference/smithy.md +502 -3
- package/reference/sql/db-codec-deriver.md +3 -3
- package/reference/sql/db-codec.md +22 -22
- package/reference/sql/db-con.md +4 -4
- package/reference/sql/db-connection.md +1 -1
- package/reference/sql/db-param.md +1 -1
- package/reference/sql/db-result-reader.md +4 -2
- package/reference/sql/db-tx.md +46 -14
- package/reference/sql/ddl.md +1 -1
- package/reference/sql/frag.md +44 -10
- package/reference/sql/index.md +7 -7
- package/reference/sql/repo.md +15 -15
- package/reference/sql/sql-dialect.md +1 -1
- package/reference/sql/sql-logger.md +1 -1
- package/reference/sql/sql-name-mapper.md +3 -3
- package/reference/sql/table-metadata.md +3 -3
- package/reference/sql/table.md +10 -10
- package/reference/sql/transactor-zio.md +1 -1
- package/reference/sql/transactor.md +21 -11
- package/reference/sql-zio.md +2 -2
- package/reference/streams/core/index.md +32 -0
- package/reference/streams/{pipeline.md → core/pipeline.md} +210 -74
- package/reference/streams/{sink.md → core/sink.md} +331 -353
- package/reference/streams/{stream.md → core/stream.md} +919 -209
- package/reference/streams/execution-and-compatibility/async-execution.md +822 -0
- package/reference/streams/execution-and-compatibility/index.md +35 -0
- package/reference/streams/execution-and-compatibility/platform-differences.md +297 -0
- package/reference/streams/execution-and-compatibility/scala-2-compatibility.md +88 -0
- package/reference/streams/execution-and-compatibility/zero-boxing.md +393 -0
- package/reference/streams/index.md +140 -67
- package/reference/streams/primitives/index.md +30 -0
- package/reference/streams/primitives/reader.md +1992 -0
- package/reference/streams/{writer.md → primitives/writer.md} +254 -98
- package/reference/telemetry/common/any-value.md +90 -0
- package/reference/telemetry/common/attribute-key.md +87 -0
- package/reference/telemetry/common/attributes.md +118 -0
- package/reference/telemetry/common/index.md +39 -0
- package/reference/telemetry/common/instrumentation-scope.md +24 -0
- package/reference/telemetry/common/resource.md +34 -0
- package/reference/telemetry/index.md +311 -0
- package/reference/telemetry/logging/index.md +197 -0
- package/reference/telemetry/logging/log-enrichment.md +72 -0
- package/reference/telemetry/logging/log-formatter.md +100 -0
- package/reference/telemetry/logging/log-record-processor.md +56 -0
- package/reference/telemetry/logging/log-record.md +44 -0
- package/reference/telemetry/logging/log-writer.md +64 -0
- package/reference/telemetry/logging/logger-provider.md +142 -0
- package/reference/telemetry/logging/logger.md +83 -0
- package/reference/telemetry/logging/severity.md +62 -0
- package/reference/telemetry/metrics/index.md +150 -0
- package/reference/telemetry/metrics/instruments.md +183 -0
- package/reference/telemetry/metrics/labeled-instruments.md +74 -0
- package/reference/telemetry/metrics/meter-provider.md +76 -0
- package/reference/telemetry/metrics/meter.md +98 -0
- package/reference/telemetry/metrics/metric-data.md +57 -0
- package/reference/telemetry/otel/custom-exporter.md +216 -0
- package/reference/telemetry/otel/index.md +212 -0
- package/reference/telemetry/tracing/index.md +155 -0
- package/reference/telemetry/tracing/sampler.md +89 -0
- package/reference/telemetry/tracing/span-builder.md +57 -0
- package/reference/telemetry/tracing/span-context.md +39 -0
- package/reference/telemetry/tracing/span-data.md +32 -0
- package/reference/telemetry/tracing/span-kind.md +55 -0
- package/reference/telemetry/tracing/span-processor.md +53 -0
- package/reference/telemetry/tracing/span-status.md +47 -0
- package/reference/telemetry/tracing/span.md +117 -0
- package/reference/telemetry/tracing/tracer-provider.md +91 -0
- package/reference/telemetry/tracing/tracer.md +52 -0
- package/reference/typeid.md +0 -64
- package/sidebars.js +365 -185
- package/undocumented-report.md +528 -270
- package/reference/config.md +0 -158
- package/reference/streams/concurrent-operators.md +0 -106
- package/reference/streams/reader.md +0 -1284
- package/reference/streams/scala-2-compatibility.md +0 -55
- package/reference/streams/zero-boxing.md +0 -275
- package/reference/telemetry.md +0 -693
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: adr-001-data-migration
|
|
3
|
+
title: "ADR-001: SQL Data Migration Architecture"
|
|
4
|
+
status: accepted
|
|
5
|
+
date: 2026-07-18
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# ADR-001: SQL Data Migration Architecture
|
|
9
|
+
|
|
10
|
+
## Status
|
|
11
|
+
|
|
12
|
+
Accepted
|
|
13
|
+
|
|
14
|
+
## Context
|
|
15
|
+
|
|
16
|
+
The `zio-blocks` library needed an online data migration system that allows evolving database schemas from application code. The system builds on two existing foundations:
|
|
17
|
+
|
|
18
|
+
- `zio.blocks.schema.migration.Migration[A, B]` for typed schema evolution
|
|
19
|
+
- `zio.blocks.sql.Repo[E, ID]` for database access
|
|
20
|
+
|
|
21
|
+
Key requirements:
|
|
22
|
+
|
|
23
|
+
- No hand-written SQL in migrations
|
|
24
|
+
- No XML or Liquibase-style migration files
|
|
25
|
+
- Support for both PostgreSQL and SQLite
|
|
26
|
+
- Safe cutover for large tables with zero data loss
|
|
27
|
+
|
|
28
|
+
The module is Scala 3 only.
|
|
29
|
+
|
|
30
|
+
## Decisions
|
|
31
|
+
|
|
32
|
+
### Decision 1: Three Execution Models
|
|
33
|
+
|
|
34
|
+
**Decision:** Provide three execution tiers, each suited to a different scale of change:
|
|
35
|
+
|
|
36
|
+
- **A-Tiny** (`TinyMigrator`): Simple startup DDL via `Transactor.transact`. For schema-only changes that are fast and non-blocking.
|
|
37
|
+
- **A-Small** (`SmallMigrator`): Queue-based batch worker for moderate tables. Lifecycle: `init()` then loop `processBatch()` then `complete()`.
|
|
38
|
+
- **B-Large** (`LargeMigrator`): Incremental worker with safe completion protocol. Lifecycle: `init()` then `fence()` then `drain()` then `complete()`.
|
|
39
|
+
|
|
40
|
+
**Rationale:** Different migration scales need different guarantees. Tiny is lightweight and runs at startup. Small is transactional and batch-oriented. Large has distributed coordination and a safe cutover protocol. A single execution model would either be too heavy for simple DDL changes or too weak for large-table migrations.
|
|
41
|
+
|
|
42
|
+
### Decision 2: Coalesced Dirty-Key Queue
|
|
43
|
+
|
|
44
|
+
**Decision:** The queue table is a single shared table with columns `(id, op, payload)` — `id` is the source primary key (TEXT, PRIMARY KEY so duplicate keys coalesce via upsert), `op` records the capturing operation (`I`/`U`/`D`), and `payload` optionally stores the row JSON on deletes. There is no per-migration queue table or `migration_id` column; the source table remains authoritative. Keys enter the queue either manually (`QueueTable.enqueue`) or automatically via opt-in capture triggers (`captureTriggers = true` installs them in `init()`); trigger installation is idempotent and requires PG 14+ on PostgreSQL.
|
|
45
|
+
|
|
46
|
+
**Rationale:** Bounded queue storage. A single keyed table keeps the worker protocol simple and lets manual enqueue and trigger capture share one mechanism. The source row is always reread at processing time for `I`/`U` entries, avoiding stale data; `D` entries carry the payload needed to resolve the target key. Coalescing on the primary key means multiple updates to the same row between processing cycles collapse into a single queue entry. Manual enqueue keeps the default behavior dependency-free; triggers let producers write normally without application-code changes, but must not be enabled when the migrator writes to the same physical table it captures from (self-requeue loop).
|
|
47
|
+
|
|
48
|
+
### Decision 3: Source-Authoritative Replay
|
|
49
|
+
|
|
50
|
+
**Decision:** Workers claim a dirty key and, in one transaction: reread the source row (without taking a source-row lock), apply the migration, write the target, and remove the key. A missing source row at processing time means delete the target row.
|
|
51
|
+
|
|
52
|
+
**Rationale:** Prevents deadlock with concurrent source writers. The worker holds only the dirty-key claim, not the source row lock. A source writer can proceed without waiting for the worker to finish reading. The source table is the single source of truth, so rereading at processing time guarantees the worker acts on the latest state.
|
|
53
|
+
|
|
54
|
+
### Decision 4: Target Strategy Orthogonality
|
|
55
|
+
|
|
56
|
+
**Decision:** `TargetStrategy` is a thin enum (`InPlace` | `ShadowTable(suffix)`) orthogonal to the execution model. ShadowTable uses `CREATE TABLE IF NOT EXISTS ... LIKE ... INCLUDING ALL` and a DDL swap at completion.
|
|
57
|
+
|
|
58
|
+
**Rationale:** Decouples "how to write" from "when to write." The same worker code works for both strategies. InPlace updates rows directly in the source table. ShadowTable writes to a separate table and swaps it in at cutover. Callers choose the strategy based on whether the schema change is backward compatible.
|
|
59
|
+
|
|
60
|
+
### Decision 5: Safe Completion Protocol (B-Large)
|
|
61
|
+
|
|
62
|
+
**Decision:** State machine: `Initialized -> Fenced -> Drained -> Completed`. Fenced state prevents new source writes. Drain processes remaining queue items. Complete swaps the shadow table.
|
|
63
|
+
|
|
64
|
+
**Rationale:** Guarantees zero data loss during cutover. Queue persistence across restarts enables crash recovery. State is checked at each transition with `require()` guards. The protocol ensures every row modified between `init()` and `complete()` is migrated, even if the application crashes and restarts mid-migration.
|
|
65
|
+
|
|
66
|
+
### Decision 6: PostgreSQL-Specific Features
|
|
67
|
+
|
|
68
|
+
**Decision:** `QueueTable.dequeue` uses `SELECT ... FOR UPDATE SKIP LOCKED` for concurrent workers on PostgreSQL. Shadow table uses `CREATE TABLE ... LIKE ... INCLUDING ALL`. DDL swap is atomic.
|
|
69
|
+
|
|
70
|
+
**Rationale:** PostgreSQL's row-level locking and DDL transactionality enable safe multi-worker migrations. SQLite uses `dequeueSQLite` without `SKIP LOCKED` since SQLite serializes writes via `BEGIN IMMEDIATE` (single-writer model). The dialect split is necessary because the two databases have fundamentally different concurrency models.
|
|
71
|
+
|
|
72
|
+
### Decision 7: ID Type Parameters with Equality Constraint
|
|
73
|
+
|
|
74
|
+
**Decision:** `LargeMigrator` and `SmallMigrator` accept separate `ID1` and `ID2` type parameters, but construction requires evidence that `ID1 =:= ID2`. The queue stores V1 IDs.
|
|
75
|
+
|
|
76
|
+
**Rationale:** The two type parameters document the roles (source-repo ID vs target-repo ID) and keep repo signatures precise, while the `ID1 =:= ID2` constraint makes delete propagation type-safe: when a source row disappears between dequeue and read, the worker deletes the corresponding target row using the same key, reinterpreted through the evidence (`ev.substituteCo`) rather than a cast. Genuinely different key types (for example, `Int` to `Long`) are not supported by the built-in migrators; callers needing that must provide a custom conversion path outside this module.
|
|
77
|
+
|
|
78
|
+
### Decision 8: Pre-Commit State Enforcement
|
|
79
|
+
|
|
80
|
+
**Decision:** `complete()` requires `Drained` state. `drain()` requires `Fenced` state. `fence()` requires `Initialized` state. `drain()` only transitions to `Drained` if not paused.
|
|
81
|
+
|
|
82
|
+
**Rationale:** Prevents misordered lifecycle calls. The pause guard prevents `complete()` from being called when the queue is not empty (paused mid-drain). The stall valve turns an unbounded hot loop (empty batches with pending items, e.g. a stuck concurrent worker holding row locks) into a clear failure after 100 consecutive rounds; `SmallMigrator.complete()` likewise refuses while items are pending. State transitions are enforced at runtime with `require()` checks, making invalid sequences fail fast rather than producing subtle data inconsistencies.
|
|
83
|
+
|
|
84
|
+
### Decision 9: Failure Policy
|
|
85
|
+
|
|
86
|
+
**Decision:** On worker transaction failure: rollback, retain the dirty key, return failure to caller. No automatic retry, no dead-letter queue.
|
|
87
|
+
|
|
88
|
+
**Rationale:** Simple, predictable semantics. The caller decides retry policy. No silent data loss. No configuration surface for retry counts, backoff strategies, or dead-letter routing. The dirty key remains in the queue, so the row can be retried by calling `processBatch()` or `drain()` again.
|
|
89
|
+
|
|
90
|
+
### Decision 10: Scope Exclusions
|
|
91
|
+
|
|
92
|
+
**Decision:** The following are explicitly out of scope:
|
|
93
|
+
|
|
94
|
+
- No schema-diff or automatic migration derivation (Derivation.scala removed per review)
|
|
95
|
+
- No CLI (MigrationCLI removed per review)
|
|
96
|
+
- No JSON or full-row outbox payloads
|
|
97
|
+
- No external brokers or LISTEN/NOTIFY
|
|
98
|
+
- No composite keys, configurable retry, configuration files, third-party dialects, or automatic target-schema DDL generation
|
|
99
|
+
|
|
100
|
+
**Rationale:** Keeps scope narrow. `Migration[A, B]` provides the typed transformation. `Repo` provides the database surface. The data migration module is a thin orchestration layer. Callers supply the migration logic; the module handles queueing, worker coordination, and cutover.
|
|
101
|
+
|
|
102
|
+
## Consequences
|
|
103
|
+
|
|
104
|
+
### Positive
|
|
105
|
+
|
|
106
|
+
- Clean separation of concerns: `Migration[A, B]` (what) vs execution model (how) vs target strategy (where)
|
|
107
|
+
- Queue-based dirty-key tracking is storage-bounded and crash-recoverable
|
|
108
|
+
- Safe completion protocol prevents data loss during cutover
|
|
109
|
+
- ID type flexibility supports realistic migration scenarios
|
|
110
|
+
- No hand-written SQL, no XML, no external dependencies beyond the database
|
|
111
|
+
|
|
112
|
+
### Tradeoffs
|
|
113
|
+
|
|
114
|
+
- Missing-source-row delete relies on ID type compatibility (`ID1 =:= ID2`). For genuinely different ID types, a conversion function would be needed.
|
|
115
|
+
- No automatic retry requires caller-side error handling. Callers must implement their own retry logic if desired.
|
|
116
|
+
- PostgreSQL-only `SKIP LOCKED` limits full multi-worker support. SQLite is single-worker due to its write serialization model.
|
|
117
|
+
- State is in-memory (not persisted). Restart resets the state machine, though the queue table persists across restarts for crash recovery.
|
|
118
|
+
- Per-key writer blocking during worker processing is accepted. A source writer waits while the worker holds the dirty-key claim. No tuning configuration is provided for this.
|
|
119
|
+
|
|
120
|
+
## References
|
|
121
|
+
|
|
122
|
+
- [Data Migration Reference Documentation](../reference/data-migration.md)
|
|
123
|
+
- PR #1534 (implementation)
|