Skip to content

2.4.3

Choose a tag to compare

@lakhansamani lakhansamani released this 28 Oct 18:59
0f4032a

Patch release. Corrects the CassandraDB provider, which did not work on Apache Cassandra at all on three queries, and closes the timestamp-domain exposure #818 left behind. No schema or data migration, no breaking change. One operational note: sanity-check NTP on application hosts before upgrading.

Fixed

  • The CassandraDB provider did not work on Apache Cassandra at all, on three queries (#824). One provider backs both cassandradb and scylladb, but cassandradb was missing from the storage suite's backend list, so it had only ever been exercised through ScyllaDB. Apache Cassandra accepts CQL LIKE only against a SASI index and rejects it against the plain secondary index this provider creates, so GetWebhookByEventName (webhook dispatch by event name never fired), DeleteAllSessionTokensByUserID (session revocation failed) and DeleteSessionTokensByNamespace errored outright on every Apache Cassandra deployment. ScyllaDB accepts the same statements, which is why it went unnoticed. All three already carried ALLOW FILTERING, so the index was doing no work; the comparison moves into Go. cassandradb is now in the suite on its own port, with make test-cassandradb.
  • DeleteAllSessionTokensByUserID matched an unanchored substring on five backends (#824). A revocation for user 42 also deleted tokens stored for 142 or provider:427. The SQL provider was anchored long ago; CassandraDB, MongoDB, ArangoDB, DynamoDB and Couchbase kept the defect because the assertion covering it lived inside a SQL-only subtest. The match is now the exact value or a :<userId> suffix — strictly tighter, never looser — on every backend, asserted by a test that runs on all eight. MongoDB additionally interpolated the id straight into $regex, so an id containing regex metacharacters was evaluated as a pattern; it is now escaped and anchored with \z rather than $, which also matches before a trailing newline. Harmless in practice, since every user id is a UUIDv4, but it was a live over-delete primitive.
  • Single-use session tokens and OAuth states could be consumed twice on Cassandra/ScyllaDB (#824). Completing #818: DeleteSessionTokenByUserIDAndKey treats a DELETE ... IF EXISTS applied flag as the exactly-once arbiter behind refresh-token rotation, and that lightweight transaction is stamped from the Paxos ballot — the server clock — while AddSessionToken's plain insert carried the driver's client timestamp. With the client clock ahead the tombstone lands below the insert: the row survives, yet Paxos saw a live row and returned applied=true, so the next claim of the same token also returned applied=true. Measured on scylladb/scylla with the client clock 10 ms ahead: 20 of 20 tokens claimed twice, 0 of 20 after the fix. session_tokens, oauth_states and authenticators now take the coordinator's clock on every write, including the one inside DeleteUser's cascade. Applied per query, never cluster-wide, so the ~20 tables that never touch a lightweight transaction stay on one client clock rather than several node clocks. No Paxos round-trip is added to the login path. Sanity-check NTP on application hosts before upgrading — a host minutes off NTP was already losing lightweight-transaction deletes on the old binary, which is the bug being fixed.
  • Cassandra/ScyllaDB: a delete could be silently discarded after a lightweight-transaction insert (#818). A lightweight transaction takes its mutation timestamp from the Paxos ballot, i.e. the server clock, while a plain DELETE writes a tombstone carrying the driver's client-side timestamp. A tombstone only shadows a row whose timestamp it exceeds, so with the server clock running ahead the delete was dropped with no error and the row survived. Measured on scylladb/scylla at 46 of 50 tight insert-delete pairs. AddUser loses its IF NOT EXISTS — it only ever constrained a freshly generated UUID partition key, enforced no uniqueness, and its applied flag was never read — and the two org_domain deletes gain IF EXISTS, because there the lightweight transaction is load-bearing: the domain is the partition key and first-writer-wins is the atomicity guarantee. A dropped delete there left a verified domain claimed permanently. Apache Cassandra was not observed to drop the write in the runs behind this entry, and the reason given here originally — that its ballot timestamp is truncated to whole milliseconds, flooring the row marker behind the later client timestamp — was measured false while preparing #824: with the client stamp forced back 10 ms, Apache Cassandra 4.1.12 drops 50 of 50, and an ambient run with the engine clock 15 ms ahead still dropped 0 of 50, which truncation cannot explain either. The exposure is not engine-specific. Whether it fires without forcing depends only on which way the client and coordinator clocks happen to sit, which is what made it look flaky. No schema or data migration.