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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 255a937ce445032caf1880728a75a7363dda1323f8d20650bb2c73380cdb0c82
4
- data.tar.gz: 3ff1c84f019c73902ae18812b19e8ab254d30813128f80fc7878adf0b43e21d1
3
+ metadata.gz: 37b913418c998e4620c90efb72a0be8c93b838a2977db0347c9198b743ced2f3
4
+ data.tar.gz: 0a8a7f3a622712cc29b5738b8b0f4a54e3df3ffc56657277a69ebb956d3c46bf
5
5
  SHA512:
6
- metadata.gz: f0dc18a3c981530aba9c5be15acf07a1ce1a766845464aaa656876856f113d2b874e70173e9feb51bc2f88596c6bb9deea8a4b473d058e266c8ebc4e4584689c
7
- data.tar.gz: 1d76a14b8ad44f7f17aee8e7973a6a468536711ddfd299cf376cd9d4d41e643c0e84e4b648df1b75bb961cb93764789c9ff9e3c15baffea8f7c323ff64a8f566
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