Repository navigation
v0.4.7
[0.4.7] — 2026-10-12
No version was cut between 0.4.6 (2026-07-23) and this release — everything below merged to main
across that gap in 66 PRs (GH #5–#151) with no corresponding gradle.properties bump, so this entry
consolidates all of it. The headline work: three adversarial review rounds (2026-09-08 pre-multi-DC
review, 2026-09-09 post-fix critical audit, 2026-10-11 docs reality-check follow-up) each
specifically re-checked the fixes from the round before, which is how several of the "Fixed" items
below are themselves fixes to regressions introduced by an earlier fix in this same release.
Breaking / behavior changes
findActive()'sALLOW FILTERINGis now an explicit opt-in (GH #12) — without
@SecondaryIndexon the marker column, it throwsKandraQueryExceptionunless called with
allowFullScan = true, instead of silently runningALLOW FILTERINGwith a WARN.KandraBatchScopesplit into suspend-only and blocking (KandraBlockingBatchScope) types
(GH #99) — mixing a blocking repository'ssaveInBatchinto a suspendbatch { }block (which
would block the coroutine dispatcher) is now a compile error instead of a runtime footgun.@VersionLWT updates no longer blindly retry on transient errors (GH #15) — retrying the
identical conditional statement after a timeout could raise a spurious
KandraOptimisticLockExceptionfor a write that had already succeeded; see the retry recipe in
docs/USER_GUIDE.md.EACH_QUORUM(as a read consistency) andSERIAL/LOCAL_SERIAL(as a write consistency) now
throwKandraQueryExceptioninstead of reaching the driver unvalidated (GH #140/#141) — a caller
relying on the previous unvalidated pass-through will now get a clear exception at query time.AUTO_MIGRATErefuses toALTER TABLE ADDa missing partition/clustering key column (GH #92)
— throwsKandraSchemaExceptioninstead of silently adding it as a plain column and corrupting row
identity.
Added
saveAndGet()/saveIfNotExistsAndGet()/updateAndGet()onKandraRepository/
KandraSuspendRepository(GH #132/#138/#136) — same writes assave()/saveIfNotExists()/
update(), but return the entity actually persisted (generated id,@CreatedAt/@UpdatedAt,
post-write@Version) instead of discarding it.update()/updateAndGet()gained aserialConsistencyparameter (GH #134) — lets a versioned
update requestSERIAL(cross-DC Paxos) instead of the previous hardcodedLOCAL_SERIALfor its
IF version = ?check.throttle { }config block — client-side admission control wiring the driver's own
ConcurrencyLimitingRequestThrottler(maxConcurrentRequests/maxQueueSize), off by default
(GH #81/ISS-073).PoolConfiggained local/remote connection-pool size fields — previously no
Kandra-level configuration at all (GH #80).KandraThrottledException(GH #103) — wraps a backpressure/admission-control rejection instead
of letting the raw driverRequestThrottlingExceptionleak through unwrapped.- Consistency Strict Mode (GH #5) — opt-in, WARN-only warning when a query resolves to
LOCAL_ONE/ONEin a deploymentloadBalancing.allowedRemoteDcsmarks as multi-DC. kandra-codegengenerates typed Koin/Kodein DI accessors (GH #17) — a compile-checked
alternative to hand-typednamed("...")qualifier lookups.DebugConfig.rawQueryStrictMode— makes theraw()/rawQuery()injection heuristic throw
KandraQueryExceptioninstead of only warning.
Fixed — correctness / data integrity
@Transientcolumns broke every full-row decode (GH #130).save()discarded the entity's generated id/timestamps (GH #132);update()discarded the
actually-persisted post-update version (GH #136);saveIfNotExists()discarded the actually-
persisted entity (GH #138) — all three addressed by the new...AndGet()methods above.- Predicate values bypassed codec encoding in
buildWhere()— brokeeq/gt/lt/inon any
non-primitive column type (GH #144). append()/remove()bound a rawCollection, not the column's actual CQL collection type —
broke everySet<T>column (GH #145).INon a@SecondaryIndexcolumn silently queried the partition key instead of the indexed column
(GH #146).- Counter columns threw on decode whenever any counter cell in a row was untouched (NULL) (GH #93).
- Repositories silently discarded the plugin's own configured
StatementBuilder/codec. BatchEngine-bypassing write paths now route throughexecuteWithRetry(GH #29).- Eager
SchemaRegistryvalidation for@PartitionKey/@ClusteringKeyconflicts, blank@Column
names, and identifier shape (GH #30); duplicatecqlNames now rejected eagerly, withfrozen<>
support for collection keys/nesting (GH #31). - DI qualifier collisions are now detected before binding instead of resolving the wrong repository
at runtime (GH #35). - The
AUTO_CREATE/AUTO_MIGRATEDDL bootstrap claim is now keyed by schema fingerprint (GH #90)
and fenced by holder with a clock-skew-proof staleness fallback (GH #91), after an LWT claim guard
against concurrent-instance races was added first (GH #79). - Migration claim resolution now distinguishes losing a race to an
APPLIEDrow from losing to a
CLAIMEDone (GH #101); checksum now hashes every sibling compiled class fileup()produces
(object expressions, anonymous classes, SAM lambdas), not just the migration's own (GH #102), and
normalizes compiled bytecode before hashing (GH #63); migration claim staleness is now measured by
the DB cluster's own clock, not the app instance's (GH #64); first crash-safety pass
(CLAIMED/APPLIED distinction + kandra-migrate test suite) (GH #26/#37). - Codegen now gives clear diagnostics for file-name collisions on same-simple-name nested entity
classes and for@ScyllaTableon a non-data-class, instead of a late, confusing failure (GH #104). FakePreparedStatement.bind()now simulates real binding instead of unconditionally throwing
(GH #28) — the fix thekandra-testskill was still describing the pre-fix state of as of the
2026-09-09 docs pass.
Fixed — consistency & multi-DC
LOGGED BATCH/versioned-update consistency anddecodeEntitycolumn-map caching (GH #54/#68);
generic reads (find/findAll/findPage/exists) ignored configured read consistency, and
unpaged reads had no row cap (GH #55/#67 — the latter is thefindAll()10,000-row truncation
documented indocs/USER_GUIDE.md/docs/features/repositories.md).- Multi-DC failover, credential rotation re-applying to the live session, keyspace/DC identifier
validation, and a dead pool-config field removed (GH #58/#61/#65/#69). - Retry idempotency,
saveIfNotExistsLWT retry, versioned-update TTL preservation, and soft-delete
consistency (GH #56/#57/#59/#62). - Suspend-safe batch collection, failure-path metrics, and RF-aware Strict Mode (GH #60/#82/#83).
- Lookup-index reads and versioned-update lookup-table writes both bypassed configured consistency
(GH #96);findById()cache hits silently ignored the caller's consistency override (GH #95); a
cache invalidate-after-write race could pin a stale value indefinitely, and@CacheResult's
per-process-only scope is now documented (GH #100). - Strict Mode's RF-vs-consistency math was wrong for multi-DC
NetworkTopologyStrategy, causing
false-positive warnings (GH #98).
Fixed — security / hardening
raw()/rawQuery()'s injection guard is no longer suppressed by an unrelated bound parameter
elsewhere in the same string (GH #32), and now also catches a bare;statement terminator or a
--//* */comment marker — closing a gap where a payload against an unquoted numeric/UUID column
reached the driver undetected (GH #142).KandraColumnRef'scqlNameis now validated against CQL identifier shape (GH #33).KandraCredentials.toString()no longer prints the plaintext password (GH #66).- SSL config (
requireEncryption/minimumTlsVersion/cipherSuites) is now actually enforced,
not dead fields (GH #78); TLS floor now fails closed, the reverse-DNS hostname-verification bug is
fixed, and pool size is validated (GH #105). - A missing-auth fallback is now logged,
existsQuery's raw CQL path is guarded, and predicate
columns are validated (GH #107).
Fixed — performance / scalability
- Entity reflection (copy fn, properties, constructor) is now cached per
KClassinstead of
recomputed every call (GH #13). findActive()/findActiveSuspend()had no row cap — an unboundedALLOW FILTERINGscan could
OOM the process (GH #94).- Suspend read/write paths no longer block the coroutine dispatcher on a prepared-statement cache
miss (GH #27); graceful shutdown's drain loop replaced aThread.sleepbusy-wait with coroutine
polling (GH #34). kandra-runtimeread-path/metrics polish: implicitLIMIT 1where applicable, atomic
cachegetOrPut, cache-stampede protection, and more specific metrics labels (GH #106).
Testing & CI
kandra-ktortests now run in CI, with newkandra-runtimeunit test coverage (GH #16).- Codegen tests now assert on generated
*Table.ktcontent, not just file existence (GH #37, part). - New SSL round-trip, multi-DC failover, and migration-claim concurrency test coverage (GH #84);
kandra-multidc/SSL integration suites are now wired into CI instead of tagged "manual" with zero
scheduled execution (GH #97); multi-DC/SSL test fixtures hardened — fixed compose ports, pause-vs-
real-partition realism, a missing hostname-mismatch SSL test (GH #108). - Jakarta validator factory reuse, codegen nullability, and health-check debounce (GH #36, items
1/2/4). - Assorted low-severity findings from the 2026-09-09 post-fix critical audit (GH #109).
Documentation
@LookupIndex+@SoftDeletecombined storage-growth behavior documented (GH #6); LWT documented
as akandra-migrateprerequisite (GH #7).- Two full reality-check passes against current source (2026-09-08, 2026-09-09) corrected drift
acrossREADME.md/docs/USER_GUIDE.md/docs/features//.claude/skills/— see
docs/reviews/2026-09-09-docs-reality-check.md. kandra-multidcdocumented as experimental — not yet validated against a genuine multi-region
topology (every round so far has been an approximation: Testcontainers on one host, a two-
physical-host self-hosted round, and a ScyllaDB Cloud attempt that turned out to provision
multi-AZ, not multi-DC) — GH #149, and extended todocs/USER_GUIDE.md's Multi-DC Support section.- A further reality-check pass (2026-10-11) found and fixed 9 gaps left by a month of the above work
shipping with no doc updates — thefindAll()row cap, the three...AndGet()methods,
KandraThrottledException/throttle { }, theEACH_QUORUM/SERIALconsistency-validation
caveat,rawQueryStrictMode, theAUTO_MIGRATEkey-column guard, the migration-checksum
sibling-class coverage, and a stray(0.4.7)version tag that predated this actual release — see
docs/reviews/2026-10-11-docs-reality-check-followup.md.