v3.0.0 - WAL-Logged Storage and Durable Pattern Matching #20
CrystallineCore
announced in
Announcements
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Version 3.0.0
First release integrated with WAL logging. This is a breaking on-disk
format change: indexes built under 2.x must be
REINDEXed.The focus of this release is durability: index state now lives in the index
relation's own WAL-logged pages, participating in PostgreSQL's ordinary
recovery machinery.
New Features
WAL-logged, crash-safe on-disk storage. Replaces the external-file
snapshot mechanism from 2.5.0 with in-relation,
GenericXLog-protected pagestorage covering per-character and length bitmaps, the TID array,
tombstones, the free-slot list, and per-record string caches. Index state
now survives a crash and replicates correctly.
Pending-list write path with opportunistic draining. Steady-state
INSERT,UPDATEandDELETEappend a small delta record to a structure'sown pending chain instead of rewriting a whole snapshot. Once a structure's
pending chain passes a threshold (
biscuit.delta_compaction_slots, a newGUC), it is re-serialized into a fresh compacted blob.
VACUUMalsoperforms a full drain pass and tracks lifetime drain counters.
Read-time pending-list reconciliation. Queries transparently merge
not-yet-drained pending records into the results they read, so a backend
sees a consistent view regardless of whether another backend's writes have
been drained yet.
Cross-backend cache coherency. Each session's cached copy of the index
carries the generation it was loaded at; every scan compares that
generation against the metapage and reloads on mismatch, so a backend
always sees other backends' committed writes.
Candidate-mask threading across scan keys. Conjunctive queries evaluate
their most selective key first and restrict later keys to the surviving
rows, instead of computing each key independently and intersecting.
Queries combining an anchored predicate with an unanchored one benefit
substantially.
Rewritten cost model. Costs are now derived from pattern shape, column
statistics and relation size, letting the planner weigh Biscuit against
gin_trgm_opspg_trgm and atext_pattern_opsB-tree. Costing for unanchoredpatterns continues to be refined; verify with
EXPLAINwhere a specific planmatters.
Length-predicate support. Patterns consisting only of
_wildcards(
'______','______%') are recognised as length predicates and answeredfrom the length bitmaps in a single lookup, rather than being treated as
unusable.
New
biscuit_like_ops/biscuit_ilike_opsoperator classes. In additionto the default
biscuit_ops, which builds both case-sensitive andcase-insensitive structures, a column may be indexed with
biscuit_like_ops(LIKE and NOT LIKE only) or
biscuit_ilike_ops(ILIKE and NOT ILIKE only),skipping the build and maintenance cost of the structure set it will never be
queried with. The mode is derived from the column's opfamily at build and load
time and is never persisted, so it cannot go stale across a
REINDEXunder adifferent opclass.
Internal Changes
blob/pending-chain storage; the 2.5.0 flat-file snapshot format is no
longer read.
replacing the previous bulk in-memory sweep.
Known Limitations
These follow from the design and should be planned for. Figures observed during
testing will vary with hardware, data and workload.
Write amplification. Because one indexed string touches many
per-character structures,
INSERTandUPDATEagainst a live index generateconsiderably more WAL than the corresponding heap writes alone. Substantial
WAL is characteristic of maintaining any secondary text-search structure, and
in testing Biscuit's WAL volume per row was comparable to a
pg_trgmGINindex on the same data. WAL per row also grows as the index grows, so
measurements taken on a small index will understate a large one.
DELETEismuch cheaper, recording a tombstone rather than rewriting structures.
Size
pg_walaccordingly, monitor free space, and where replication slotsare in use consider setting
max_slot_wal_keep_size. Allow for thecorresponding effect on crash-recovery duration when planning restart
windows.
Bulk-load before indexing. Creating the index after a load is
substantially faster, and generates far less WAL, than inserting the same
rows into an already-indexed table.
Per-connection memory. Each backend holds its own copy of the index in
session-local memory for the life of the connection, loaded lazily as
patterns are queried. Memory therefore scales with concurrent connections;
biscuit_index_memory_size()reports the current session's copy.Cache reload on invalidation. A committed write by any backend
invalidates cached copies, which are then reloaded in full rather than
refreshed incrementally. Read latency rises for a period after each write, and
the effect is more pronounced with many concurrent readers. Incremental
refresh is planned.
Build cost and index size. Biscuit indexes are larger and slower to build
than comparable
pg_trgmor B-tree indexes on the same column.VACUUMdoesnot reduce index size; use
REINDEX. Build memory scales with row count.Unanchored query cost grows with the square of string length, so a small
number of unusually long values can affect query cost across the table.
No ordered, backward, index-only or unique scans, and Biscuit indexes are
not clusterable.
Upgrade Notes
This is a breaking on-disk format change. Indexes built under 2.x must be
REINDEXed after upgrading; there is no automatic migration and no dual-formatreader. Until an index is rebuilt, its first cold load under the new version
fails with an error referring to this note.
Plan a maintenance window sized for the rebuild: index build is slower than for
pg_trgmGIN on the same data.This discussion was created from the release v3.0.0 - WAL-Logged Storage and Durable Pattern Matching.
All reactions