typed_eav 0.6.0 → 0.7.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +157 -0
- data/README.md +241 -60
- data/app/models/typed_eav/field/base.rb +77 -27
- data/app/models/typed_eav/field/currency.rb +43 -0
- data/app/models/typed_eav/field/file.rb +1 -1
- data/app/models/typed_eav/field/image.rb +1 -1
- data/app/models/typed_eav/field/reference.rb +11 -0
- data/app/models/typed_eav/section.rb +11 -5
- data/app/models/typed_eav/value.rb +95 -32
- data/db/migrate/20260430000000_add_parent_scope_to_typed_eav_partitions.rb +1 -1
- data/db/migrate/20260712000000_enforce_parent_scope_invariant.rb +4 -0
- data/db/migrate/20260816000000_use_partial_covering_scalar_indexes.rb +198 -0
- data/lib/typed_eav/bulk_read.rb +41 -9
- data/lib/typed_eav/bulk_upsert.rb +141 -0
- data/lib/typed_eav/bulk_write.rb +65 -39
- data/lib/typed_eav/config.rb +19 -21
- data/lib/typed_eav/csv_mapper.rb +1 -1
- data/lib/typed_eav/engine.rb +16 -27
- data/lib/typed_eav/entity_query.rb +32 -7
- data/lib/typed_eav/event_dispatcher.rb +21 -30
- data/lib/typed_eav/field/typed_storage.rb +48 -0
- data/lib/typed_eav/field_deletion.rb +75 -0
- data/lib/typed_eav/query_builder.rb +17 -27
- data/lib/typed_eav/registry.rb +7 -8
- data/lib/typed_eav/version.rb +1 -1
- data/lib/typed_eav/versioning/subscriber.rb +25 -29
- data/lib/typed_eav/versioning.rb +59 -41
- data/lib/typed_eav.rb +2 -0
- metadata +4 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 37b913418c998e4620c90efb72a0be8c93b838a2977db0347c9198b743ced2f3
|
|
4
|
+
data.tar.gz: 0a8a7f3a622712cc29b5738b8b0f4a54e3df3ffc56657277a69ebb956d3c46bf
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c5bdc06300d8bb92538c93cfe23ac3afb6bed9719a29f6f7590740e6d4f56ac14998e21b4230c58fa2eed10e89dfe053af86144f3cbcf9637049c3d5b811d90b
|
|
7
|
+
data.tar.gz: 4760251616a62f91b7ed931dafc8b92a25653047cab8fcbd21dd027fe6353fdd2e7209a0629233306c69f77ec8eec08459854e8cc81443595d0b68e0503f4e8b
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,148 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
No unreleased changes.
|
|
11
|
+
|
|
12
|
+
## [0.7.0] - 2026-08-18
|
|
13
|
+
|
|
14
|
+
This release hardens TypedEAV's correctness and operational behavior, replaces
|
|
15
|
+
the highest-impact read N+1 path, and adds explicitly bounded bulk-maintenance
|
|
16
|
+
APIs. It keeps the typed-column architecture and existing semantic write path;
|
|
17
|
+
it does not claim that one storage design or batch size wins every workload.
|
|
18
|
+
|
|
19
|
+
### Highlights
|
|
20
|
+
|
|
21
|
+
- Batch partition resolution in `typed_eav_hash_for(records)` so the accepted
|
|
22
|
+
1,000-scope BulkRead shape executes three SQL statements instead of 1,002.
|
|
23
|
+
- Replace the six scalar value indexes with smaller partial-covering indexes
|
|
24
|
+
that omit irrelevant NULL cells while preserving index-only entity reads.
|
|
25
|
+
- Add an explicit reduced-semantics PostgreSQL bulk-upsert API and opt-in
|
|
26
|
+
chunked transactions for applications that knowingly prefer throughput or
|
|
27
|
+
bounded commits over the full semantic write envelope.
|
|
28
|
+
- Write `ValueVersion` audit rows in the same source transaction as each
|
|
29
|
+
`Value`, so either both persist or both roll back.
|
|
30
|
+
- Add SQL-narrowed default backfills and resumable, callback-preserving,
|
|
31
|
+
keyset-batched field deletion.
|
|
32
|
+
|
|
33
|
+
### Changed
|
|
34
|
+
|
|
35
|
+
- Query operands are normalized and validated by the owning Field before SQL is
|
|
36
|
+
built. This keeps scalar, range, array, Currency, Reference, and text-search
|
|
37
|
+
operands aligned with write semantics; Active Record remains responsible for
|
|
38
|
+
SQL bind plumbing.
|
|
39
|
+
- `typed_eav_hash_for(records)` resolves all effective partition definitions in
|
|
40
|
+
one batched query, loads values once, and preloads field associations once.
|
|
41
|
+
Its public return shape, logical-missing behavior, partition precedence, and
|
|
42
|
+
orphan filtering are unchanged.
|
|
43
|
+
- Versioning now installs synchronous `Value` lifecycle callbacks instead of
|
|
44
|
+
writing audit rows from an internal after-commit subscriber. A failed audit
|
|
45
|
+
insert now rolls back the source mutation instead of leaving an unversioned
|
|
46
|
+
committed `Value`.
|
|
47
|
+
- The six scalar indexes now use `(field_id, value) INCLUDE (entity_id) WHERE
|
|
48
|
+
value IS NOT NULL`. The migration creates every replacement concurrently
|
|
49
|
+
before dropping its legacy index; rollback recreates legacy indexes before
|
|
50
|
+
removing replacements.
|
|
51
|
+
- Field and Section partition mutations validate their own pending tuple rather
|
|
52
|
+
than leaking scope changes through shared lookup state. Value pending state is
|
|
53
|
+
also preserved across validation and lifecycle boundaries.
|
|
54
|
+
- JSONB and TypedEAV are documented as workload-dependent storage choices.
|
|
55
|
+
Applications can own expression B-tree indexes for stable JSONB paths and GIN
|
|
56
|
+
indexes for containment; TypedEAV supplies stable typed columns and ordinary
|
|
57
|
+
per-type indexes. No final storage winner is claimed without representative
|
|
58
|
+
workload evidence.
|
|
59
|
+
|
|
60
|
+
### Added
|
|
61
|
+
|
|
62
|
+
- SQL-narrowed default backfills through an exact-host relation, while retaining
|
|
63
|
+
partition checks, batching, callbacks, validations, idempotence, versions,
|
|
64
|
+
error reporting, and Field-owned logical-missing detection across multi-cell
|
|
65
|
+
storage.
|
|
66
|
+
- `Field::Base#destroy_with_values_in_batches!`, a callback-preserving,
|
|
67
|
+
keyset-batched exact-field deletion path with locked bounded finalization. A
|
|
68
|
+
failed batch leaves the Field available for inspection and retry.
|
|
69
|
+
- `bulk_upsert_typed_eav_values`, an explicitly reduced-semantics PostgreSQL
|
|
70
|
+
upsert. Callers must pass `acknowledge_reduced_semantics: true`; values are
|
|
71
|
+
cast and validated before SQL, while host saves, persistence callbacks,
|
|
72
|
+
versioning, delete shorthand, and per-record savepoints are intentionally
|
|
73
|
+
skipped.
|
|
74
|
+
- `transaction: :chunks, chunk_size: N` for both semantic BulkWrite and the
|
|
75
|
+
reduced upsert. Completed chunks remain committed if a later chunk fails;
|
|
76
|
+
`transaction: :all` remains the default.
|
|
77
|
+
|
|
78
|
+
### Fixed
|
|
79
|
+
|
|
80
|
+
- Validate Field defaults through the same typed domain rules used by ordinary
|
|
81
|
+
writes, including range, option, reference, array, Currency, and multi-cell
|
|
82
|
+
logical-missing semantics.
|
|
83
|
+
- Fail closed when versioning callbacks are missing, duplicated, installed on
|
|
84
|
+
the wrong lifecycle kind, or configured across different connection pools.
|
|
85
|
+
- Lock and drain only the exact Field's remaining Values during deletion,
|
|
86
|
+
preserving callback/version ordering across retry and race boundaries.
|
|
87
|
+
- Keep Strong Migrations-compatible constraint validation outside a migration
|
|
88
|
+
transaction while preserving the generated-consumer migration path.
|
|
89
|
+
- Reject normalized duplicate bulk-upsert keys such as `:age` and `"age"`
|
|
90
|
+
before issuing SQL.
|
|
91
|
+
|
|
92
|
+
### Performance
|
|
93
|
+
|
|
94
|
+
- BulkRead characterization reduced the 1,002 statements observed across 1,000
|
|
95
|
+
scopes to three for the same shape. This is a statement-count result, not a
|
|
96
|
+
representative throughput claim.
|
|
97
|
+
- BulkWrite evidence remains bounded to the exercised 100- and 1,000-host
|
|
98
|
+
lanes; no 10k/100k throughput or universal batch-size claim is made.
|
|
99
|
+
- Representative PostgreSQL 15, 16, and 18 migration/catalog checks passed for
|
|
100
|
+
the partial-covering indexes. PostgreSQL 17 planner and benchmark timings are
|
|
101
|
+
retained as co-tenant diagnostic evidence rather than portable latency
|
|
102
|
+
promises.
|
|
103
|
+
|
|
104
|
+
### Reliability
|
|
105
|
+
|
|
106
|
+
- ValueVersion rows are written in the source transaction, preserving atomic
|
|
107
|
+
rollback with the Value mutation and avoiding a false after-commit rollback
|
|
108
|
+
assumption.
|
|
109
|
+
- The parent-scope check-constraint migration validates existing rows before
|
|
110
|
+
adding constraints with Strong Migrations-compatible nontransactional DDL;
|
|
111
|
+
the unchanged consumer migration canary passed.
|
|
112
|
+
|
|
113
|
+
### Upgrade notes
|
|
114
|
+
|
|
115
|
+
- Copy/install the latest engine migrations and run the application's normal
|
|
116
|
+
migration command. The parent-scope validation and scalar-index migrations
|
|
117
|
+
use `disable_ddl_transaction!`; do not wrap them in an application-level
|
|
118
|
+
transaction. Concurrent create-before-drop ordering avoids an index-coverage
|
|
119
|
+
gap during normal PostgreSQL deployment.
|
|
120
|
+
- Versioned applications should expect an audit-write failure to abort the
|
|
121
|
+
`Value` mutation. `Value` and `ValueVersion` must share one connection pool;
|
|
122
|
+
versioning fails closed when they do not.
|
|
123
|
+
- Semantic BulkWrite still runs host validations/callbacks, Value persistence
|
|
124
|
+
callbacks, and versioning. Under the default `transaction: :all`, individual
|
|
125
|
+
validation/save failures are isolated by savepoints and successful records
|
|
126
|
+
may commit, while an uncaught exception rolls back the outer transaction.
|
|
127
|
+
`transaction: :chunks` deliberately permits earlier chunks to remain
|
|
128
|
+
committed after a later failure.
|
|
129
|
+
- `bulk_set_typed_eav_values_per_record` uses records as Hash keys; duplicate
|
|
130
|
+
Active Record instances for the same persisted row collapse to one entry.
|
|
131
|
+
Use ordered separate calls when two updates to the same row must both occur.
|
|
132
|
+
- The reduced bulk upsert accepts persisted, unique records and one uniform
|
|
133
|
+
values Hash, returns the number of upserted value rows, and intentionally does
|
|
134
|
+
not provide semantic successes/errors or callback/version guarantees.
|
|
135
|
+
- The supported runtime contract remains Ruby 3.3–4.0, Rails 7.2–8.1, and
|
|
136
|
+
PostgreSQL 15–18.
|
|
137
|
+
|
|
138
|
+
### Evidence boundaries
|
|
139
|
+
|
|
140
|
+
- The BulkRead result is an exact SQL-statement-count improvement for the
|
|
141
|
+
characterized public path, not a universal throughput or latency guarantee.
|
|
142
|
+
- BulkWrite measurements cover 100- and 1,000-host lanes only. Applications
|
|
143
|
+
should choose chunk sizes from their own callback cost, transaction duration,
|
|
144
|
+
lock contention, and failure-recovery needs.
|
|
145
|
+
- Public chained-IN multi-filter SQL remains the supported strategy. Optional
|
|
146
|
+
`pg_trgm` and dependency statistics stay application-owned and measured;
|
|
147
|
+
TypedEAV does not automatically install extensions, specialized indexes, or
|
|
148
|
+
extended statistics.
|
|
149
|
+
- No representative storage tournament selected TypedEAV, JSONB, per-type EAV,
|
|
150
|
+
or conventional columns as a universal winner.
|
|
151
|
+
|
|
10
152
|
## [0.6.0] - 2026-07-13
|
|
11
153
|
|
|
12
154
|
Hardens correctness, query efficiency, installation confidence, and release
|
|
@@ -307,6 +449,17 @@ internal helper relocation (see "Changed" below). Anchored by ADRs
|
|
|
307
449
|
|
|
308
450
|
### Internal
|
|
309
451
|
|
|
452
|
+
- Added the opt-in `Field#destroy_with_values_in_batches!` API for exact-field,
|
|
453
|
+
callback-preserving keyset deletion. It commits bounded Value destroy batches,
|
|
454
|
+
retains the Field across failure, and performs locked bounded finalization;
|
|
455
|
+
ordinary Field destruction and dependency policies remain unchanged.
|
|
456
|
+
|
|
457
|
+
- Atomic ValueVersion writes are installed from the Value callback chains at
|
|
458
|
+
boot, with an identical-pool guard and idempotent recovery if a callback is
|
|
459
|
+
removed. BulkWrite keeps caller context unchanged while correlating version
|
|
460
|
+
groups through its pending marker; EventDispatcher remains a public and
|
|
461
|
+
generic observer broker.
|
|
462
|
+
|
|
310
463
|
- `TypedEAV::EventDispatcher` is retained as the synchronous broker
|
|
311
464
|
between `TypedEAV::Hooks` and `ActiveSupport::Notifications`. The
|
|
312
465
|
cleanup arc explicitly considered collapsing it and rejected that:
|
|
@@ -427,7 +580,11 @@ worked examples.
|
|
|
427
580
|
|
|
428
581
|
Initial release.
|
|
429
582
|
|
|
583
|
+
[Unreleased]: https://github.com/dchuk/typed_eav/compare/v0.7.0...HEAD
|
|
584
|
+
[0.7.0]: https://github.com/dchuk/typed_eav/releases/tag/v0.7.0
|
|
430
585
|
[0.6.0]: https://github.com/dchuk/typed_eav/releases/tag/v0.6.0
|
|
586
|
+
[0.5.0]: https://github.com/dchuk/typed_eav/releases/tag/v0.5.0
|
|
587
|
+
[0.4.0]: https://github.com/dchuk/typed_eav/releases/tag/v0.4.0
|
|
431
588
|
[0.3.2]: https://github.com/dchuk/typed_eav/releases/tag/v0.3.2
|
|
432
589
|
[0.3.1]: https://github.com/dchuk/typed_eav/releases/tag/v0.3.1
|
|
433
590
|
[0.3.0]: https://github.com/dchuk/typed_eav/releases/tag/v0.3.0
|