V03 of the package DDL: the executions table's nullable outcome_blob
column, and a GIN index on metadata.
outcome_blob
An execution's own answer, kept where a reader that has never seen
the execution live can find it. A durable subchart child's completion used to exist
only on the step that produced it - a stored record carries no
donedata - which is fine while a completion is answered immediately
and not fine once N of them have to be collected and assembled in
index order. The column is written once, at the child's completion,
by StatifierPersistence.Storage.update_execution_status/4.
Nullable, because almost no execution has one: an ordinary execution answers nobody, and an execution that fails at creation never completes.
It is a blob column, not a jsonb one, and the difference is a
disclosure decision rather than a typing convenience. metadata is
restricted to host identities (ADR-0006 decision 2) precisely because
:blob_type encryption does not reach a queryable column. A donedata
payload carries whatever the chart's author put in it, so it belongs
on the side of that line the encryption reaches: outcome_blob joins
the three existing blob columns and takes the configured :blob_type
with them.
The metadata GIN index
V02 shipped no index and said why: which pairs a host queries by is
the host's call (ADR-0006 decision 4). Fan-out settlement changes the
arithmetic. Every child completion asks "are all N of my siblings
terminal?", which is the same jsonb containment query the cascade
already issues, so one fan-out of N children issues N of them; without
an index each is a sequential scan of the host's whole executions table.
That is not a cost a host can be left to discover in production, so
this package now ships the index the query it issues needs.
jsonb_path_ops rather than the default jsonb_ops: containment
(@>) is the only operator either query uses, and the path-ops
opclass serves exactly that, with a smaller index. A host that needs
the wider operator set, or an expression index on particular keys,
still adds its own - ADR-0006 decision 4's clause is narrowed by this
migration, not withdrawn, and the record carries a dated Note saying
so.
Adapters other than Postgres
GIN and jsonb_path_ops are Postgres spellings. up/1 therefore
creates the index only when the migration's repo runs on
Ecto.Adapters.Postgres, and down/1 drops it under the same
condition; on any other adapter both steps are skipped and the
migration otherwise runs to completion. Before that check existed the
whole of V03 rolled back on a SQLite repo - ecto_sqlite3 raises
ArgumentError from using: - which took the outcome_blob column
with it and left such a host unable to run this package's DDL at all
(sp-11w).
The column is created on every adapter, because it is a nullable
binary column and every adapter has one. That is what keeps
StatifierPersistence.Storage.Ecto.supports_execution_outcome?/1 true
everywhere.
Skipping the index is not the same as the index not mattering. What
the index serves - the jsonb containment queries
StatifierPersistence.Storage.Ecto.list_executions_by_metadata/2 and
list_execution_states_by_metadata/2 issue - is Postgres-only SQL in its
own right, so an adapter that cannot take the index cannot run the
queries either and says so: that adapter's metadata support is
declared false, and a durable subchart or fan-out over such a store is
refused at open rather than started and left unsettleable. sp-5lm
tracks the Ecto adapter's Postgres-only surface.
Summary
Functions
Drops the metadata GIN index and the executions table's
outcome_blob column.
Adds the executions table's nullable outcome_blob column and the
metadata GIN index per config.
Functions
@spec down(StatifierPersistence.Ecto.Config.t()) :: :ok
Drops the metadata GIN index and the executions table's
outcome_blob column.
@spec up(StatifierPersistence.Ecto.Config.t()) :: :ok
Adds the executions table's nullable outcome_blob column and the
metadata GIN index per config.