Address globally aggregated immutable key-value stores(s).
Address globally aggregated immutable key-value stores(s).
(bulk-delete-statement db-type table store-keys)(bulk-insert-statement db-type table store-key-values)(bulk-select-statement db-type table store-keys)Generate SELECT statement for multiple keys. Returns a vector with [sql-string & parameters].
Generate SELECT statement for multiple keys. Returns a vector with [sql-string & parameters].
(change-row-id connection table from to)(conditional-write-domain {:keys [dbtype dbname]})How far this store's fence reaches: :global, :machine, :process, or nil.
For the servers it follows the dbtype. For the two embedded databases it cannot, because one dbtype spells three different deployments and the difference is the whole answer:
h2 with a mem: name, and sqlite with :memory:, live in one JVM heap.
A second process on the same host opens an entirely DIFFERENT database, so
the honest domain is :process. Reporting :machine here would be an
OVER-claim — a caller asking (conditional-write? store :machine) would be
told yes about writers that cannot even see this data.h2 reached over tcp:// or ssl:// is a server like any other, and
orders writers anywhere on the network: :global.A database this does not recognise gets NO domain and :expected-revision is
refused. The statement would work on any SQL database; what cannot be guessed
is how far its answer reaches, and guessing generously is how a deployment
comes to believe it is fenced across hosts when it is not.
How far this store's fence reaches: `:global`, `:machine`, `:process`, or nil.
For the servers it follows the dbtype. For the two embedded databases it
cannot, because one dbtype spells three different deployments and the
difference is the whole answer:
- `h2` with a `mem:` name, and sqlite with `:memory:`, live in one JVM heap.
A second process on the same host opens an entirely DIFFERENT database, so
the honest domain is `:process`. Reporting `:machine` here would be an
OVER-claim — a caller asking `(conditional-write? store :machine)` would be
told yes about writers that cannot even see this data.
- `h2` reached over `tcp://` or `ssl://` is a server like any other, and
orders writers anywhere on the network: `:global`.
- otherwise both are a file, ordering processes on the host that holds it and
no further — and not even that on a network filesystem, where SQLite's own
documentation calls locking unreliable.
A database this does not recognise gets NO domain and `:expected-revision` is
refused. The statement would work on any SQL database; what cannot be guessed
is how far its answer reaches, and guessing generously is how a deployment
comes to believe it is fenced across hosts when it is not.(connect-store db-spec & {:keys [table opts] :as params})(copy-row-statement db-type table to from)(create-statement db-type table)(delete-statement db-type table)(delete-store db-spec & {:keys [table opts]})(extract-bytes obj dbtype)(fenced-insert-statement db-type table id header meta value)Insert the row for id, relying on the PRIMARY KEY to refuse it if the row
already exists — which is create-if-absent, evaluated by the database.
A plain INSERT rather than a dialect-specific upsert precisely because it must NOT overwrite. The refusal arrives as an integrity-constraint violation, which is SQLSTATE class 23 in the standard and in every driver here.
Insert the row for `id`, relying on the PRIMARY KEY to refuse it if the row already exists — which is create-if-absent, evaluated by the database. A plain INSERT rather than a dialect-specific upsert precisely because it must NOT overwrite. The refusal arrives as an integrity-constraint violation, which is SQLSTATE class 23 in the standard and in every driver here.
(fenced-read? env)(fenced-update-statement db-type table id header meta value expected-meta)Replace the row for id, but only while its meta column still holds
expected-meta. Row count 1 means the write happened, 0 that it was refused.
ONE statement, so the comparison and the write are the same step — the database evaluates it, which is what makes this backing's guarantee reach as far as the database does. No transaction is needed for that; an UPDATE is atomic on its own.
The comparison is on the META column rather than a separate version column. konserve's revision lives inside the serialized metadata, so for this row the meta bytes ARE the revision, and comparing them needs no schema change and no migration for existing tables.
SQL Server needs the length too. Its varbinary comparison treats trailing zero bytes as insignificant, so two values of different lengths can compare EQUAL — which for a fence means a stale write passing. Microsoft's own guidance is to test the length alongside the data, and DATALENGTH is how. The other dialects compare binary exactly: bytea, longblob and SQLite BLOBs are all memcmp.
Replace the row for `id`, but only while its `meta` column still holds `expected-meta`. Row count 1 means the write happened, 0 that it was refused. ONE statement, so the comparison and the write are the same step — the database evaluates it, which is what makes this backing's guarantee reach as far as the database does. No transaction is needed for that; an UPDATE is atomic on its own. The comparison is on the META column rather than a separate version column. konserve's revision lives inside the serialized metadata, so for this row the meta bytes ARE the revision, and comparing them needs no schema change and no migration for existing tables. SQL Server needs the length too. Its varbinary comparison treats trailing zero bytes as insignificant, so two values of different lengths can compare EQUAL — which for a fence means a stale write passing. Microsoft's own guidance is to test the length alongside the data, and DATALENGTH is how. The other dialects compare binary exactly: bytea, longblob and SQLite BLOBs are all memcmp.
The :operation values konserve puts in the env of a CONDITIONAL write. The
read it takes under the lock to evaluate that write carries the same one, which
is what lets the read path tell itself apart from every other read.
The `:operation` values konserve puts in the env of a CONDITIONAL write. The read it takes under the lock to evaluate that write carries the same one, which is what lets the read path tell itself apart from every other read.
(get-connection db-spec)(integrity-violation? e)Is this the database refusing a duplicate primary key?
SQLSTATE class 23 is the standard's integrity-constraint violation. Measured
against every database this backend supports: postgres 23505, mysql 23000,
h2 23505, sqlserver 23000 — and sqlite, which reports NO SQLSTATE at all
(nil) and carries the refusal in the vendor code instead, 19 for
SQLITE_CONSTRAINT. Missing that case would turn create-if-absent on sqlite
from a clean rejection into a raw driver exception the caller cannot classify,
so the vendor code is matched too — narrowly, by driver class name, because
the number 19 means nothing in particular anywhere else.
Is this the database refusing a duplicate primary key? SQLSTATE class 23 is the standard's integrity-constraint violation. Measured against every database this backend supports: postgres 23505, mysql 23000, h2 23505, sqlserver 23000 — and sqlite, which reports NO SQLSTATE at all (`nil`) and carries the refusal in the vendor code instead, 19 for SQLITE_CONSTRAINT. Missing that case would turn create-if-absent on sqlite from a clean rejection into a raw driver exception the caller cannot classify, so the vendor code is matched too — narrowly, by driver class name, because the number 19 means nothing in particular anywhere else.
(offset-query db-type table offset)(read-all db-type connection table id)Maximum keys per SELECT IN clause, by database type. Based on SQL parameter limits (1 param per key) and practical result set sizes.
Maximum keys per SELECT IN clause, by database type. Based on SQL parameter limits (1 param per key) and practical result set sizes.
(read-field db-type
connection
table
id
column
&
{:keys [binary? locked-cb] :or {binary? false}})(read-meta db-type connection table id)(read-operation env db-type connection table id)(release store env)Must be called after work on database has finished in order to close connection
Must be called after work on database has finished in order to close connection
(remove-from-pool db-spec)How far a fenced write reaches, for the databases that are SERVERS. The
mechanism is the same everywhere — one UPDATE ... WHERE meta = ?, evaluated
by the database — but the reach is a property of where that database runs, and
a server on the network is reachable from any host, so its comparison orders
every writer anywhere.
How far a fenced write reaches, for the databases that are SERVERS. The mechanism is the same everywhere — one `UPDATE ... WHERE meta = ?`, evaluated by the database — but the reach is a property of where that database runs, and a server on the network is reachable from any host, so its comparison orders every writer anywhere.
(table-exists-query db-type table)(update-statement db-type table id header meta value)cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |