Skip to content

CNDB-18860: Rebase main-5.0 onto cassandra-5.0.9 - #2565

Open
JeremiahDJordan wants to merge 1862 commits into
main-5.0from
cndb-18860-main-5.0.9-rebase
Open

CNDB-18860: Rebase main-5.0 onto cassandra-5.0.9#2565
JeremiahDJordan wants to merge 1862 commits into
main-5.0from
cndb-18860-main-5.0.9-rebase

Conversation

@JeremiahDJordan

Copy link
Copy Markdown
Member

Rebases main-5.0 onto the cassandra-5.0.9 upstream tag.

Summary

  • 1,739 CC commits replayed on top of cassandra-5.0.9
  • All fixup commits squashed into their target commits
  • Full per-commit change report: agent_log/rebase-changes-report.md

Key API changes in cassandra-5.0.9 that required fixes

  • Gossiper.instance.getHostId(InetAddressAndPort) (1-arg) removed → replaced with StorageService.instance.getTokenMetadata().getHostId() + null-guards (4 sites in AutoRepairUtils, AutoRepair)
  • RepairOption constructor gained 2 new boolean params (forceRepair, offlineService) → updated all 3 call sites in AutoRepairState
  • CompactionParams.classFromName() return type changed; error message updated; CompactionMetrics byte counters changed from Counter to Meter (.inc().mark())
  • ReverseValueIterator 5-arg constructor removed; 6-arg form required
  • IndexInputReader constructors made package-private; FilterIndexInput added as base class for TrackingIndexInput
  • SystemDistributedKeyspace.TABLE_NAMES made private → getTableNames()
  • TableOperation.Progress.TOTAL_COMPRESSED removed → local constant restored in CompactionStats
  • ErrorMessage.fromException()fromExceptionNoStreamId() in transport layer

@JeremiahDJordan
JeremiahDJordan force-pushed the cndb-18860-main-5.0.9-rebase branch 10 times, most recently from 5693297 to 4faa67a Compare August 19, 2026 15:52
driftx and others added 20 commits August 19, 2026 12:07
… dupes in result set (#2024)

(cherry picked from commit ada025c)

Copy of #2023, but targeting
`main`

riptano/cndb#15485

This PR fixes a bug introduced to this branch via
#1884. The bug only impacts
SAI file format `aa` when the index file was produced via compaction,
which is why the modified test simply adds coverage to compact the table
and hit the bug.

The bug happens when an iterator produces the same partition across two
different batch fetches from storage. These keys were not collapsed in
the `key.equals(lastKey)` logic because compacted indexes use a row id
per row instead of per partition, and the logic in
`PrimaryKeyWithSource` considers rows with different row ids to be
distinct. However, when we went to materialize a batch from storage, we
hit this code:

```java
        ClusteringIndexFilter clusteringIndexFilter = command.clusteringIndexFilter(firstKey.partitionKey());
        if (cfs.metadata().comparator.size() == 0 || firstKey.hasEmptyClustering())
        {
            return clusteringIndexFilter;
        }
        else
        {
            nextClusterings.clear();
            for (PrimaryKey key : keys)
                nextClusterings.add(key.clustering());
            return new ClusteringIndexNamesFilter(nextClusterings, clusteringIndexFilter.isReversed());
        }
```

which returned `clusteringIndexFilter` for `aa` because those indexes do
not have the clustering information. Therefore, each batch fetched the
whole partition (which was subsequently filtered to the proper results),
and produced a multiplier effect where we saw `batch` many duplicates.

This fix works by comparing partition keys and clustering keys directly,
which is a return to the old comparison logic from before
#1884. There was actually a
discussion about this in the PR to `main`, but unfortunately, we missed
this case
#1883 (comment).

A more proper long term fix might be to remove the logic of creating a
`PrimaryKeyWithSource` for AA indexes. However, I preferred this
approach because it is essentially a `revert` instead of fixing forward
solution.

 (Rebase of commit fcc246f)

 (Rebase of commit 3f1b4bb)
)

Add defensive checks in SSTableWriter and SSTableReader to handle closed
channels and null file handles when creating shallow readers during
flush error recovery.

SSTableWriter: Catch IllegalStateException on file pointer access
SSTableReader: Handle null dfile in bytesOnDisk()

 (Rebase of commit c688745)

 (Rebase of commit 8c5b8fb)
### What is the issue

Paxos v2 doesn't have sensors

### What does this PR fix and why was it fixed

Register sensors in paxos v2

 (Rebase of commit ef045eb)

 (Rebase of commit 73cbefd)
…riesTest (#2073)

### What is the issue

AggregationQueriesTest is flaky

### What does this PR fix and why was it fixed

Sets the CQL timeout equal to server timeout so the client doesn't
timeout before the server is done

 (Rebase of commit 9b3b3a3)

 (Rebase of commit b61d14b)
…#2004)

Port some of the improvements for index hints done by
[CASSANDRA-20888](https://issues.apache.org/jira/browse/CASSANDRA-20888),
especially the ones in messaging. Also clean up unused methods in index hints.

 (Rebase of commit 6f77404)

 (Rebase of commit 7409a4a)
Fixes: riptano/cndb#15448

Bumps jvector version. Commits:
datastax/jvector@4.0.0-rc.2...4.0.0-rc.3

diff:

```
jvector % git log 4.0.0-rc.2...4.0.0-rc.3 --oneline
17169513 (tag: 4.0.0-rc.3) chore: update changelog for 4.0.0-rc.3 (#528)
67b2f88d Regression enhancements (#526)
baf87e80 chore: update changelog for 4.0.0-rc.3 (#527)
f3d235cc Release 4.0.0-rc.3
cfb3004f streamline PR checklist (#525)
df4a0688 add checklist template and initial CONTRIBUTIONS.md guide (#523)
63db005a GraphIndexBuilder::addGraphNode must iterate all graph levels to estimate used bytes (#521)
817a25c4 GitHub actions regression test (#499)
8364012f Remove unused construction batch member from OnHeapGraphIndex (#510)
1823b9be Switch from syncronized to concurrent map for pq codebook (#518)
6d590ad7 Enable specifying the benchmarks in the yaml file (#515)
1c298218 Create partial sums for PQ codebook for use during diversity checks (#511)
a916a07c PQ ranging bugfix and refactoring (#508)
66399923 Reducing the number of allocations in GraphSearcher (#501)
51d4f0bb SimdOps and NativeSimd ops refactored, VectorUtilSupport simplified (#498)
c5c3ff97 Add specific BuildScoreProvider for diversity to avoid extra encoding… (#503)
631515df Start development on 4.0.0-rc.3-SNAPSHOT
```

 (Rebase of commit 39b3669)

 (Rebase of commit df8934a)
Compaction might write SAI differently than flush, thus it's good to
have tests running after compaction.

Changes to use runThenFlushThenCompact instead of beforeAndAfterFlush in
BM25 test. Another test already tests after compaction in addition to
memtable and after flush.

 (Rebase of commit bda4e88)

 (Rebase of commit 142a233)
…shards and min sstable size are configured (#2031)

riptano/cndb#15253 large shard count jump
was involved in HCD-130

Replace power-of-two shard progression with factorization-based smooth
growth when base shard count is not power of 2. This prevents
problematic large jumps. For example, with num_shards 1000 which is 5^3* 2^3:

- Before: 1 → 2 → 8 → 1000 (125x jump causes data loss due to hcd-130)
- After:  1 → 5 → 25 → 125 → 250 → 500 → 1000 (max 5x jump)

* new behavior can be disabled via
`-Dunified_compaction.use_factorization_shard_count_growth`

 (Rebase of commit 9a21657)

 (Rebase of commit 4274127)
There's a problem impacting all main-5.0 jvm-dtest-upgrade, as the run-tests.sh script it builds dtest-5.0.4.0.jar and then from the apache/cassandra cassandra-5.0 branch builds dtest-5.0.7.jar. Many of the jvm-dtest upgrade tests only perform upgrades to the CURRENT version which is the latest found, and that's the dtest-5.0.7.jar so the dtest-5.0.4.0.jar (and all working code changes) gets ignored.

I don't know where/how CC jenkins runs the jvm-dtest-upgrade testsuite, but this happens when doing it like
```
.build/run-tests.sh -a jvm-dtest-upgrade
```
The fix is .build/run-tests.sh:202 needs to remove "cassandra-5.0"

 (Rebase of commit 38ae506)

 (Rebase of commit 7eb1ee9)
…ExecutorLocals within Message.execute and Dispatcher.processInit. This ensures that Future callbacks, which may execute on different threads, can correctly access the expected thread-local state.

 (Rebase of commit c9bd53f)

 (Rebase of commit 3c60c2e)
### What is the issue

PaxosCommit lacks sensors

### What does this PR fix and why was it fixed

Instrument PaxosCommit, PaxosPrepare, and PaxosPropose to track read,
write, and internode bytes. Reuse existing RequestSensors in
StorageProxy to prevent sensor data loss during CAS operations.

 (Rebase of commit 548bfc6)

 (Rebase of commit f760542)
…ring rebase if STAR-993 onto Apache Cassandra 5.0.4

 (Rebase of commit e6c9530)

 (Rebase of commit cabd285)
riptano/cndb#8641

PR in CNDB: riptano/cndb#15306

This pull request introduces new metrics for tracking invalid and other
error requests for CQL statements, and integrates these metrics into the
query failure notification logic. The main changes are the addition of
the `AllRequestsMetrics` class, updates to the `ClientRequestsMetrics`
class to include these new metrics, and modifications to the query event
notification methods to record errors using the new metrics.

Metrics added:
* org.apache.cassandra.metrics.ClientRequest.Timeouts.All
* org.apache.cassandra.metrics.ClientRequest.Unavailables.All
* org.apache.cassandra.metrics.ClientRequest.Failures.All
* org.apache.cassandra.metrics.ClientRequest.Invalid.All
* org.apache.cassandra.metrics.ClientRequest.OtherErrors.All

 Note: only requests for which a tenant can be identified are counted.

 (Rebase of commit 3517b24)

 (Rebase of commit 909d213)
Very long log lines can be produced during commitlog replay

Limits the amount of information printed at debug, but retains full
information at trace

 (Rebase of commit b1833e0)

ninjafix – unused imports in test/unit/org/apache/cassandra/db/commitlog/CommitLogReplayerTest.java

Rebase notes:
 - squash into b1833e0

 (Rebase of commit e5bebc7)

 (Rebase of commit bbff159)
The PrimaryKeyWithSource class has been
present for two years in the code base
as an optimization for hybrid vector workloads,
which have to materialize many primary keys
in the search-then-sort query path.

However, the logic is invalid for version
aa (because we have the bug where compacted
sstables write per row, not per partition)
and it is also invalid for static columns.
This commit avoids creation of PrimaryKeyWithSource
in those cases.

 (Rebase of commit c0390b2)

 (Rebase of commit 1e79c35)
CNDB-15683: Fix incorrect results when querying mixed AA and EC indexes

This commit fixes multiple issues with KeyRangeIterator implementations
occasionally skipping or emitting duplicate keys when working on
a mix of primary keys with empty / non-empty clusterings.
This situation is possible while scanning tables with static columns
or when some indexes are partition-aware (e.g. version AA) and
others have been updated to a row-aware version (e.g. DC or EC).
Due to those bugs, users could get incorrect results from SAI queries,
e.g. results containing duplicated rows, duplicated partitions or
even missing rows.

The commit introduces extensive randomized property-based tests for
KeyRangeUnionIterator and KeyIntersectionIterator. Previously,
the tests did not test for keys with mixed empty/non-empty clusterings.

Changes in KeyRangeUnionIterator:

KeyRangeUnionIterator merges streams of primary keys in such a way that
duplicates are removed. Unfortunately it does not properly account
for the fact that if a key with an empty clustering meets a key
with a non-empty clustering and the same partition key, we must
always return the key with an empty clustering. A key with an empty
clustering will always fetch the rows matched by any specific row
key for the same partition, but the reverse is not true.

The iterator implementation has been modified to always pick the
key that matches more rows - a key with empty clustering wins
over a key with non-empty clustering. Additionally, once a key
with an empty clustering is emitted, no more keys in that partition
are emitted.

Changes in KeyRangeIntersectionIterator:

Due to a very similar problem like in KeyRangeUnionIterator,
KeyRangeIntersectionIterator could return either too few or
too many keys, when keys with empty clusterings and keys
with non-empty clusterings were present in the input key streams.

In particular consider 2 input streams A and B with the following
keys:

A:
0: (1, Clustering.EMPTY)

B:
0: (1, 1)
1: (1, 2)

Key A.0 matches the whole partition 1. Therefore, the correct result
of intersection are both keys of stream B. Unfortunately, the algorithm
before this patch would advance both A and B iterators when emitting
the first matching key. At the beginning of the second step,
the iterator A would be already exhausted and no more keys would
be produced. Finally key B.1 would be missing from the results.

This patch fixes it by introducing two changes to the intersection
algorithm:

1. A key with non-empty clustering wins over a key with
empty clustering and same partition.

2. The selected highest key is not consumed while searching
for the highest matching key, but that happens only after the
search loop finds a match. Then we have more information
which iterators would be moved to the next item. Iterators positioned
at a key with an empty clustering can be advanced only after
we run out of keys with non-empty clustering in the same partition
or if there are no other keys with non-empty clustering.

This patch also fixes another issue where we could return
a less-specific key matching a full partition instead of a key
matching one row:

A:
0: (1, Clustering.EMPTY)

B:
0: (1, 1)

In that case the iterator returned a key with empty clustering,
which would result in fetching and postfiltering many unnecessary rows.

CNDB-15683: Fix incorrect results when querying mixed AA and EC indexes (#2066)

When row-aware and non-row-aware indexes are mixed, we now check
the clustering index filter for all the keys that have clustering
information, i.e. keys coming from the row-aware
indexes. Earlier that check was accidentally disabled
if at least one non-row-aware index was used by the query.
That could cause retrieving rows that do not match
the clustering condition of the query.

Rebase notes:
 - includes CNDB-15683

 (Rebase of commit b25cba1)

 (Rebase of commit b722e78)
…raStreamReceiver if CDC is enabled on the node (#2043)

Repairs use the local write path for streams on CDC-enabled tables,
based on table schema. This interacts poorly with the separation of CNDB
services.

This commit fixes the issue by only using the CDC write path for a stream if CDC
is enabled in the node's configuration (as well as in the schema). This avoids
attempting to use the local write path if commitlog-based CDC is not enabled.

 (Rebase of commit b3bac73)

 (Rebase of commit 6726c0e)
…1914)

...
We need that knowledge for CNDB
...
It exposes `containsDateRangeTypeColumn` methods

---------

Co-authored-by: Massimiliano Tomassi <max.tomassi@datastax.com>

 (Rebase of commit cc8a190)

 (Rebase of commit 995ca3d)
…earlier than CA (#2071)

Creating vector indexes if version is earlier than CA would usually fail in the asynchronous build.
This patch makes them fail synchronously at CREATE INDEX depending on the local index version.
If the local node has the right version but any of the remotes doesn't, the failure will remain
asynchronous.

 (Rebase of commit de6fda5)

 (Rebase of commit 2869687)
michaelsembwever and others added 26 commits August 19, 2026 12:07
    Port of CASSANDRA-19968

    Stop transforming single-partition queries using secondary indexes into range commands,
    but use SinglePartitionReadCommand instead, as in not-indexed queries.

    Benefits of single-partition commands are:
    * They use speculative retries, which aren't supported for range queries.
    * They use digest reads, which are benficial for network usage and throughput.
    * Metrics and observability are able to distinguish between single-partition and cluster-wide SAI queries.

forward merges c628d7b

 (Rebase of commit cab17f1)
Use graceful shutdown for the commitlog segment allocator instead of
shutdownNow(), allowing the allocator to run its shutdown path and
discard the prepared segment cooperatively.

 (Rebase of commit 1976b54)
…by simple class name (#2514)

### What is the issue
In HCD we have a new custom Index Type
com.datastax.opensearch.OpenSearchIndex that indexes data on OpenSearch.

We want to make it userfriendly to create the index:
`CREATE CUSTOM INDEX .... USING 'OpenSearchIndex' WITH OPTIONS ...`

We also want that users do not need to allow all the secondary indexes
in cassandra.yaml (in HCD secondary_indexes_enabled is disabled by
default)

See HCD-459.

HCD  PR here: riptano/hcd#243
CNDB PR here: riptano/cndb#18508

### What does this PR fix and why was it fixed

Add the `cassandra.trusted_index_implementations ` system property, a
comma-separated list of fully qualified custom index class names.

Each listed class is registered as an index name alias, so users can
create it with `CREATE CUSTOM INDEX ... USING '<SimpleClassName>'`
without the package name, like it is already possible for
StorageAttachedIndex.

Trusted implementations are also exempted from the
`secondary_indexes_enabled` guardrail: they can be created even when the
creation of secondary indexes is disabled.

To keep their number under control, the new `trusted_indexes_per_table`
guardrail limits how many indexes of each trusted implementation class
can be created on a table (warn/fail thresholds, counted per
implementation class, exposed through nodetool and `JMX` like the other
index guardrails). It defaults to a failure threshold of 10 under the
HCD guardrails profile and is disabled under the other profiles.

Also expand index name aliases in
CreateIndexStatement.isUnknownCustomIndexCreateStatement(), so that
aliased index classes are not misreported as unknown when
cassandra.index.unknown_custom_class.ignore is set.

 (Rebase of commit 99fff3f)
… (#2528)

While SonarCloud report comment is not published in PRs, this is a
workaround to simplify manually checking the SonarCloud report.

 (Rebase of commit 5081856)
…c-5 only) (#2518)

### What is the issue

In order to implement correctly the replication of Logged Batches to
OpenSearch we need a mechanism to intercept the "recovery" of batches
that have not been completely processed by the Coordinator.

See https://datastax.jira.com/browse/HCD-438

### What does this PR fix and why was it fixed
Batchlog recovery (BatchlogManager.replayFailedBatches) re-applies
logged batches after a coordinator failure with no way for plugins to
observe it: the Mutator interface is coordinator-side only and replay
bypasses it on every node.

Add a pluggable BatchlogManagerInterceptor, wired like the custom
Mutator via -Dcassandra.custom_batchlog_manager_interceptor_class, that
is notified on the replaying node once per replayed batch, with the
complete list of the batch's mutations (so implementations can process
a batch's mutations together), after the batch's mutations have
completed — applied locally, acknowledged by remote replicas, or
hinted — and before the batchlog entry is deleted. The default
implementation is a no-op. The interceptor is resolved eagerly at
startup so a misconfigured custom class fails the node immediately
(matching custom_mutator_class behaviour) and the installed
implementation is visible in the startup log.

If the callback throws, the batch is not removed from the batchlog and
the callback is retried on later replay cycles — without re-delivering
the batch. Retained batches are remembered in memory, mapped to the
hosts hinted while replaying them, and retries invoke the callback
alone:

- The batch is re-read individually (a point read by id) instead of
  being kept inside the range scan, so lastReplayedUuid advances
  unconditionally and the scan does not re-walk a growing tombstone
  range while a batch is retained. Without this, a failing callback
  would have re-applied the batch's mutations and written a fresh
  round of hints for every down replica on every 10s replay cycle
  (batchlog replay hints bypass max_hint_window), accumulating hints
  until the batch aged past gc_grace_seconds. The retry pass runs
  after the scan (retries never starve replay of new content) and is
  throttled by the replay rate limiter.
- On callback success the batch's previously hinted hosts rejoin the
  cycle's hint fsync barrier, and the batch is deleted only after that
  fsync — preserving the hints-durable-before-batch-deletion invariant
  even when the original cycle aborted before its own fsync. The
  in-memory entry is dropped, and the batch counted as replayed, only
  once its row is actually deleted, so an aborted cycle can neither
  double-count a batch nor fall back to a full (hint-rewriting)
  replay; the scan skips rows of batches awaiting their callback for
  the same reason.
- Per-entry failure handling in the retry pass: a row removed by
  another path drops the pending callback with a warning (the batch
  still counts as replayed); a row that no longer deserializes
  (IOException) drops it with an error and deletes the row through the
  shared fsync barrier; any other error keeps the entry for a later
  retry, so one bad row cannot wedge batchlog replay node-wide.
- The memory is intentionally not persisted: a restart falls back to
  replaying the whole batch from scratch, which the callback's
  at-least-once contract already tolerates.

Semantics are therefore at-least-once and implementations must be
idempotent; the javadoc spells out the carve-outs (truncated/dropped
tables, gc_grace expiry, removal by another path, decommission) and
that callbacks are not re-delivered in batch order. Repeated callback
failures and the number of retained batches are reported through
NoSpamLogger at a 1-minute interval.

With the default no-op implementation the retained-batches map stays
empty and the retry pass exits before issuing any read: the standard
replay path performs no additional queries or I/O.

Tests cover callback content and ordering relative to mutation
application, retry-until-success with a failing callback, hints for a
down replica being written exactly once across replay cycles (via
StorageMetrics.totalHints, with a registered down replica and an RF=2
keyspace), the scan-skip guard after a simulated aborted cycle, and
externally removed batches being forgotten without further callbacks.

 (Rebase of commit a132f1c)
…ery (#2488) (#2507)

Queries with index-based ORDER BY will still use the index, since we
don't have a way to run those without the index.

Queries in which the indexes changes the semantics of the expression,
such as those with analyzers, will preserve the index semantics, even if
the index itself is not read.

Co-authored-by: Andrés de la Peña <adelapena@users.noreply.github.com>

 (Rebase of commit 72685b7)
…y in 5.0 (#2521)

Several members of PrimaryKeyMap.Factory are initialized in the
constructor and not modified outside, however, they are not final
allowing to unexpectedly modify them in future changes.

Refactors the constructors of PrimaryKeyMap.Factory implementations to
have all constructed members final. Reduces the scope of the try block.

 (Rebase of commit d3dcb74)
…tEncryptor (#2524)

### What is the issue

getEncryptor(...) (introduced by CNDB-15098 to encrypt the Stats
metadata component of TDE'd SSTables) opens the COMPRESSION_INFO
component just to extract the table's compression/encryption parameters:

CompressionMetadata cm = CompressionMetadata.open(compressionFile, 1024,
false);
    ICompressor compressor = cm.parameters.getSstableCompressor();

CompressionMetadata is a ref-counted WrappedSharedCloseable holding the
chunk-offset index in off-heap Memory (ChunkOffsetMemory), and the
method dropped the instance without ever calling close(). Since
getEncryptor runs on BOTH metadata paths -- serialize (every
flush/compaction/metadata rewrite of an encrypted SSTable) and
deserialize (every metadata read) -- each such operation leaked one
unreleased Ref, surfacing at GC time as:

    ERROR [Ref] LEAK DETECTED: a reference (class org.apache.cassandra.
utils.concurrent.WrappedSharedCloseable$Tidy@...:[org.apache.cassandra.
    io.compress.CompressionMetadata$ChunkOffsetMemory@...]) ... was not
    released before the reference was garbage collected

### What does this PR fix and why was it fixed
Close the CompressionMetadata with try-with-resources once the
parameters have been read: only cm.parameters is needed, and the
ICompressor obtained from it does not reference the off-heap memory.
Same pattern SSTableMetadataViewer already uses for the same call.

 (Rebase of commit 0162a93)
…re a node finishes leaving (#2533)

### What is the issue

There was no way for a plugin to run work when an administrator
decommissions a node. The extension points that come close do not fit:
addPreShutdownHook fires on any graceful stop and cannot tell a
decommission from a restart (and decommission never kills the JVM, so it
does not fire then at all), and
IEndpointLifecycleSubscriber.onLeaveCluster only reaches the leaving
node at the very end of unbootstrap(), after everything has streamed
away.

This implementation is required to ensure that all the mutations
processed locally are sent to OpenSearch before decomissioning the node,
see https://datastax.jira.com/browse/HCD-474

### What does this PR fix and why was it fixed

Add a DecommissionHook interface plus
StorageService.registerDecommissionHook / unregisterDecommissionHook.
Several hooks may be registered and run in registration order.

Hooks run in decommission() between unbootstrap() and the shutdown that
follows, which is the only window that satisfies all of:

- the batch log has completed its final replay and hints are transferred
or dropped, and all ranges have streamed to their new owners;
- the node has left the ring, so coordinators no longer route mutations
to it. This has to be after leaveRing(), not at LEAVING:
addLeavingEndpoint only records the endpoint in a set that write routing
never consults, so a LEAVING node stays a natural write replica and
pending ranges merely add the new owners on top;
- messaging, the native transport and the stages are all still up, so a
hook can run CQL queries and coordinate against the rest of the cluster.

A hook may block indefinitely; decommission does not proceed until every
hook returns.

That window is also the one place nothing may escape from. leaveRing()
has persisted NEEDS_BOOTSTRAP but DECOMMISSIONED is not set yet, so a
throw here strands the node for good: a retry is rejected by the
ring-membership check at the top of decommission(), since the node is no
longer in TokenMetadata, and a restart bootstraps it back into the ring.
So runDecommissionHooks() reports rather than throws -- every hook runs,
the node finishes its decommission, and decommission() then throws
naming the hooks that failed, so `nodetool decommission` still surfaces
the error. For the same reason a hook's throwable is not passed to
JVMStabilityInspector.inspectThrowable, which would rethrow a wrapped
OutOfMemoryError straight out of this window.

Interrupt state is consumed rather than propagated, both when a hook
throws InterruptedException and when it restores the flag before
returning: leaving it set would fail every remaining hook the moment it
blocked, break the awaits in the shutdown below, and leak onto the
pooled JMX handler thread.

 (Rebase of commit 46c96f4)
Check the allocation limits before starting a mutation and, once started, allow it to fully progress to completion:

 - `SubAllocator.allocate()` now only tracks usage; it never consults the limit and never blocks. Cleaner/flush triggering is unchanged.
 - New `SubAllocator.awaitRoom()` / `MemtableAllocator.awaitRoomToStart()` waits, without reserving memory, until the pool is below its limit. `AbstractAllocatorMemtable.put()` calls it before delegating to `performPut()`, so all subclasses are gated automatically. Groups marked by `Barrier.markBlocking()` skip or are released from this wait, as they were from `allocate()`, so a flush can always drain the ops its barrier awaits.
 - New `Memtable.putNested()` / `AbstractAllocatorMemtable.putNested()` variant for writes that occur inside an already-started mutation (e.g. legacy 2i writing to its index table's memtable from `indexer.onInserted()` under the base shard write lock). Nested writes skip the room gate — blocking there would hold the enclosing locks and re-create the deadlock. `ColumnFamilyStore.apply()` routes on updateIndexes to dispatch `put()` vs `putNested()`.

Beyond the optimization described in the ticket, this fixes the HCD-442 deadlock: blocking mid-mutation under TrieMemtable's shard writeLock strands pre-barrier writes queued on the lock, where `markBlocking()` cannot reach them, and the flush writeBarrier never completes. Adds a regression test reproducing this deterministically on the previous code.

 (Rebase of commit 3706060)
…v2) (#2525)

### What is the issue

The Mutator SPI's only Paxos hook, mutatePaxos, is a commit-phase
transport
hook of the v1 engine: with paxos_variant=v2 LWTs bypass the Mutator
entirely,
and even on v1 a custom Mutator cannot observe the begin or the
completion of a
CAS operation (condition-not-met, unknown-result, CL=ANY commits and
pre-commit
failures are all invisible), while repairs of other proposers'
in-progress
rounds fire mutatePaxos indistinguishably from client traffic.

CNDB Issue: riptano/cndb#18612
CNDB PR: riptano/cndb#18613
HCD Issue: https://datastax.jira.com/browse/HCD-439
HCD PR: riptano/hcd#262 (not ready yet)

### What does this PR fix and why was it fixed

Add an operation-level surface to the SPI, in three default methods so
existing
implementations stay source- and binary-compatible:

- Mutator.mutateCas: called by StorageProxy.cas exactly once per client
CAS
operation (single conditional statement or conditional batch). The
default
  implementation performs the existing engine dispatch
(Paxos.useV2() ? Paxos.cas : legacyCas), so wrapping implementations get
begin/completion for both paxos variants, across live variant flips,
with the
outcome derivable from the return value (null = applied, rows =
condition not
met) or the thrown exception. The javadoc spells out the v2 caveat that
fate-unknown propose failures surface as plain timeout/failure
exceptions
  (only the v1 engine throws CasWriteUnknownResultException).

- Mutator.onCasCommit(Commit, ConsistencyLevel, CasCommitOrigin): fired
BEFORE
  every coordinator commit dispatch of both engines and of background
  PaxosRepair, for every decided value, always through
MutatorProvider.notifyCasCommit, which contains (logs and ignores, after
JVMStabilityInspector) implementation exceptions so a misbehaving
Mutator can
never abort a paxos operation, serial read or repair. CLIENT_OPERATION
fires
at most once per mutateCas, inside that call, on the same thread, before
the
commit is dispatched, so implementations can correlate the two without
ballot
inspection. REPAIR_IN_PROGRESS labels the completion of another
proposer's
accepted round (including via SERIAL reads and background paxos repair,
which
may run on messaging threads); REFRESH_COMMITTED labels re-transmissions
of
already-committed values. Empty proposals are never committed and
produce no
callback. Because it fires before dispatch, a read issued from this
callback
sees the pre-commit value; use onCasCommitApplied (below) or a deferred
  SERIAL read to observe the committed value.

- Mutator.onCasCommitApplied(Commit, ConsistencyLevel, CasCommitOrigin):
fired
AFTER a dispatched commit has been acknowledged by a consistencyLevel
quorum,
i.e. once the value is durably readable at that CL, always after the
matching
onCasCommit for the same ballot and only on the success path. If the
commit
times out or fails (the value is decided and will be completed later by
a
repair, but no quorum ack was received) onCasCommit still fires and this
one
does not: absence after an onCasCommit means "commit not confirmed", not
"not
decided". Routed through MutatorProvider.notifyCasCommitApplied with the
same
exception containment. It is delivered for CLIENT_OPERATION under both
engines
(on the request thread, inside mutateCas), for v1 REPAIR_IN_PROGRESS,
and for
  all three background PaxosRepair commit sites; it is intentionally NOT
delivered (only the dispatched onCasCommit is) for the v1
REFRESH_COMMITTED
site (asynchronous fire-and-forget sendCommit with no awaited ack) nor
for the
v2 engine's begin()-path REFRESH_COMMITTED / REPAIR_IN_PROGRESS sites
(where
the commit piggybacks on the following prepare via commitAndPrepare and
has no
separable ack). For those, a deferred LOCAL_SERIAL/SERIAL read issued
from
onCasCommit is self-correcting and observes the value regardless. The
javadoc
  documents this coverage in full.

Call sites: v1 doPaxos and beginAndRepairPaxos (StorageProxy) fire the
applied
callback after the blocking commitPaxos returns (guarded on
consistencyForCommit != ANY, which does not block); v2 Paxos.cas fires
it after
commit.awaitUntil succeeds (a committedAgreed local carries the value
from the
propose SUCCESS branch to the await point); the three PaxosRepair commit
dispatches thread the committed value and origin into their completion
callbacks
(CommittingRepair / CommitAndRestart) and fire the applied callback when
the
commit status is success. MutatorProvider.instance is now public so the
paxos
package reaches the installed singleton; CommitVerbHandler (currently
not
registered for any verb in this tree) now uses that singleton instead of
constructing a new custom Mutator per invocation.

Backward compatibility: existing Mutator implementations -- including
those
overriding mutatePaxos -- require no changes. mutateCas, onCasCommit and
onCasCommitApplied are default methods, so third-party implementations
stay
source- and binary-compatible, and the default mutateCas performs
exactly the
dispatch StorageProxy.cas used to perform inline. The v1 commit phase
still
invokes mutatePaxos on the installed Mutator at the same point as before
(commitPaxos is untouched), and the v2 engine did not invoke mutatePaxos
before
and still does not. mutatePaxos and onAppliedProposal semantics are
unchanged.

MutatorCasTest covers both variants on a single node (via
Paxos.setPaxosVariant
-- DatabaseDescriptor.setPaxosVariant alone does not update the engine's
own
volatile snapshot read by useV2()): applied and non-applying operations
(exactly
one begin/completion, at most one CLIENT_OPERATION dispatched commit --
asserted
to fire on the calling thread -- and none for non-applying), a v1
unavailable
operation and a v2 throwing-condition operation completing exceptionally
with no
commit, and an injected accepted-but-uncommitted round whose completion
by a
later operation is reported as REPAIR_IN_PROGRESS with the foreign
payload. The
applied callback is pinned with a monotonic sequence stamp: exactly one
CLIENT_OPERATION applied per applied CAS under both variants, on the
calling
thread, sequenced after the dispatched onCasCommit; none for
non-applying or
exceptional operations; and, for the injected repair, an applied
callback for
the v1 completion route (after its blocking commit) but none for the v2
begin()-path completion (dispatched-only by design).

Backward compatibility:

existing Mutator implementations -- including those overriding
mutatePaxos -- **require no changes**.

`mutateCas` and `onCasCommit` are default methods, so third-party
implementations stay source- and binary-compatible, and the default
mutateCas performs exactly the dispatch StorageProxy.cas used to perform
inline.

The v1 commit phase still invokes mutatePaxos on the installed Mutator
at the same point as before (commitPaxos is untouched), and the v2
engine did not invoke mutatePaxos before and still does not.

MutatorCasTest pins this: its recording Mutator counts mutatePaxos
invocations and asserts exactly one per applied v1 CAS, none for
non-applying operations, and none under v2. mutatePaxos and
onAppliedProposal semantics are unchanged.

MutatorCasTest covers both variants on a single node (via
Paxos.setPaxosVariant -- DatabaseDescriptor.setPaxosVariant alone does
not update the engine's own volatile snapshot read by useV2()): applied
and non-applying operations (exactly one begin/completion, at most one
CLIENT_OPERATION commit -- asserted to fire on the calling thread -- and
none for non-applying), a v1 unavailable operation and a v2
throwing-condition operation completing exceptionally with no commit
(the RF=2 unavailability scenario cannot reproduce under v2, whose
consensus quorum is sized from the actual electorate), and an injected
accepted-but-uncommitted round whose completion by a later operation is
reported as REPAIR_IN_PROGRESS with the foreign payload.

Second commit:

HCD-439: Replace onCasCommitApplied with terminal onCasCommitCompleted
Unify the post-commit callback into a single terminal notification that
pairs every onCasCommit with a success/failure outcome, so a Mutator can
close out the operation it opened on onCasCommit.

- Add Mutator.CasCommitOutcome { APPLIED, CONFIRMED_BY_PREPARE, SUPERSEDED,
  UNCONFIRMED } and onCasCommitCompleted(commit, cl, origin, outcome),
  removing onCasCommitApplied.
- v2 Paxos.cas CLIENT_OPERATION: APPLIED on commit ack, UNCONFIRMED on
  commit-await failure before rethrow.
- v2 begin() fused commit-and-prepare (REPAIR_IN_PROGRESS / REFRESH_COMMITTED):
  stash the fused commit and deliver the terminal when the following prepare
  resolves - CONFIRMED_BY_PREPARE on a promise-quorum, SUPERSEDED if
  pre-empted, UNCONFIRMED otherwise (including an awaitUntil throw). This is
  the path that previously delivered no post-commit callback at all.
- v1 doPaxos / beginAndRepairPaxos: APPLIED on commit ack, UNCONFIRMED on
  commitPaxos timeout before rethrow.
- Background PaxosRepair: APPLIED on success (retries on failure, as before).

Documented residual gaps (no terminal, dispatch-only): v1 fire-and-forget
REFRESH sendCommit and any consistencyForCommit == ANY commit.

 (Rebase of commit 08c7ea0)
…2535)

Add testing to verify that `CompositeType` factories freeze subtypes,
so strings produced by `CompositeType.toString` and stored in the
sstable headers won't produce log errors when the type system
detects them as corrupted and automatically fixes them.

 (Rebase of commit f68b2c1)
### What is the issue
4.1.135.Final is affected by several CVEs, including
[CVE-2026-56821](https://www.cve.org/CVERecord?id=CVE-2026-56821) and
[CVE-2026-56822](https://www.cve.org/CVERecord?id=CVE-2026-56822).

### What does this PR fix and why was it fixed
Upgrades Netty to 4.1.136.Final.

 (Rebase of commit 5638606)
…red paged reads (#2540)

### What is the issue

Custom QueryHandlers cannot fully implement paging

HCD side implementation: riptano/hcd#265 (custom
QueryHandler that proxies OpenSearch + lookup in Cassandra and handle
paging)

### What does this PR fix and why was it fixed
Expose a public entry point that runs a SELECT against a caller-supplied
ReadQuery instead of the query derived from the statement's own
restrictions, returning a single page. This lets a component that
resolves which partitions to read (and in what order) out of band — e.g.
a custom QueryHandler backed by an external index that assembles a
SinglePartitionReadCommand.Group in the desired order — reuse the whole
normal read flow: consistency validation, guardrails, read-threshold
tracking, selection/projection, LIMIT/OFFSET, aggregation, dynamic-data
masking, the single-shot fast path, read metrics, and page continuation
via setHasMorePages. A MultiPartitionPager yields partitions in the
Group's command order, so the caller controls result order across page
boundaries.

The method mirrors the public execute(QueryState, QueryOptions,
RequestTime) exactly, substituting the supplied ReadQuery for
getQuery(...).

Adds unit coverage (order preservation across pages including boundaries
that fall inside a partition, single page, LIMIT, OFFSET, PER PARTITION
LIMIT, clustering order, aggregation, function projection, empty and
null query) and a two-node distributed test that pages an
externally-ordered read across nodes at CL ALL with the paging state
round-tripped through its wire format.

 (Rebase of commit 205db5b)
### What is the issue
Fixes riptano/cndb#17486

### What does this PR fix and why was it fixed
Summary
Adds a GitHub workflow, SonarQube Code Quality Scan, to CC
`datastax/cassandra` repository. This provides a supplementary scanning
tool in GitHub Actions while the primary scanning (with coverage) will
be implemented in the Jenkins pipeline.

#### New Ad-Hoc Debugging Tool
sonarqube-scan.yaml  (manual workflow only):

Added a new standalone workflow for ad-hoc Sonar scanning without
requiring full test suite execution or coverage generation. Useful for:
* Quick quality checks during development
* Testing Sonar configuration changes
* Debugging Sonar issues

##### Features:
* Manual dispatch only (not triggered automatically)
* Retry logic for transient failures (503, timeouts, connection errors)
* Distinguishes quality gate failures (code issues) vs infrastructure
failures
* Optional debug mode (off by default for clean logs)

##### Changes
1. GitHub Actions Workflow ( .github/workflows/sonarqube-scan.yaml )
* Manual-only workflow for on-demand code quality scans
* Runs SonarQube scanner without test coverage (supplementary to
Jenkins)
* Trigger: Manual dispatch only ( workflow_dispatch )
* Purpose: Testing/debugging tool for quick scans without full test
suite

2. SonarQube Analysis Script ( .github/scripts/run_sonar_analysis.sh )
* Orchestrates the SonarQube scan with intelligent error handling and
retry logic
* Configures scanner arguments dynamically based on branch/PR context
* Handles transient infrastructure failures

3. SonarQube Configuration ( sonar-project.properties )
* Project Key:  544478-247111689  (IBM SonarQube format: orgId-repoId)
* Server: https://sonarqube-prod.whitewater.ibm.com (Cloudflare
endpoint, no truststore needed)
* Dashboard:
https://sonarqube-prod.apps.wdc-sonarqube-prod.core.cirrus.ibm.com

##### Known Limitations
* No code coverage: GitHub Actions workflow doesn't run tests, so
coverage metrics are 0%
* Primary scanning should be in Jenkins: Where tests and coverage are
already integrated
* This is a supplementary tool for quick scans and debugging

This is the link to GItHub Actions for the new SonarQube workflow:
https://github.com/datastax/cassandra/actions/workflows/sonarqube-scan.yaml

 (Rebase of commit 14b1d4c)
…rror into one status child

CASSANDRA-21396 added CassandraXMLJUnitResultFormatterTest, which tests that
addFailure() + addError() on the same test case produces exactly one <testcase>
element with one merged status child. Our rewritten formatter (from STAR-1248)
did not carry forward the CASSANDRA-21396 fix, so the test failed.

Two fixes applied to CassandraXMLJUnitResultFormatter:

1. addFailure() + addError() on the same test must merge messages rather than
   overwrite. Introduce mergeOrPutFailure() that appends the additional throwable
   message to the existing element's ATTR_MESSAGE attribute instead of replacing
   the whole element in failedTests.

2. maybeAddClassCaseElement() unconditionally appended a class-level <testcase>
   to the XML output, giving two testcase elements whenever any real test ran.
   Only append it to rootElement when no individual test elements have been
   recorded yet (i.e., all activity happened in @BeforeClass/@afterclass). The
   element is still created and tracked internally for time-accounting.
…eCompatibilityMode.NONE

The upstream test parameterizes over skiplist, skiplist_sharded, and trie
memtable implementations. The default test config sets
storage_compatibility_mode: HCD_1, which causes
MemtableParams.mapCC5KeyToCC4ClassName() to throw a ConfigurationException
when attempting to persist 'skiplist_sharded' as schema — because that type
does not exist in CC4.

Fix: add a @BeforeClass/@afterclass pair that temporarily sets
storage_compatibility_mode to NONE before CQLTester initialises, then restores
it. NONE is correct for a pure CC5 unit test that exercises all available
memtable types without CC4 compatibility constraints.
…ty_mode=NONE

AutoRepairTablePropertyTest, AutoRepairSchedulerStatsHelper, and
AutoRepairFlagToggleTest start in-JVM clusters that exercise the auto_repair
schema column and system_auth.cidr_groups table. Both require
StorageCompatibilityMode.NONE:

- auto_repair column does not exist in CC4 schema; writing it under HCD_1
  compatibility mode triggers a ConfigurationException.
- system_auth.cidr_groups is CC5-only; the table is absent under CC4
  compatibility, causing a KeyspaceNotDefinedException.

Fix: set storage_compatibility_mode=NONE in the cluster config for all three
test classes before the in-JVM daemon initialises.
…esSystemViewTest

AutoRepairFlagToggleTest:
- testEnablingAutoRepairFlag: remove nodetool drain before shutdown().
  drain shuts down MemtablePostFlush; the subsequent shutdown() tries to
  flush again causing RejectedExecutionException. The explicit
  flush("system_schema") preceding it is sufficient.

CompactionStatsTest:
- Add missing row(String, int, String, long, long, long) overload that
  delegates to row(String, int, int, String, long, long, long). The 6-arg
  calls hit CQLTester.row(Object...) returning a 6-element array; the SELECT
  returns 12 columns, causing 'expected:<6> but was:<12>' from assertRows.

SSTablesSystemViewTest:
- Update expected values after CNDB-10907 (overlap diagnostics) added
  overlap-related columns to the sstables virtual table output.
…rade to 1.1.0

cassandra-5.0.9 introduced commit bccb5ad 'Remove path for unused geomet
package in cqlsh' which removed 'geomet-' from the third_parties list in
bin/cqlsh.py. Apache Cassandra does not use geo types so it was considered
unused, but STAR-254 added pylib/cqlshlib/geotypes.py which does
'from geomet import wkt' unconditionally at import time.

With 'geomet-' removed from third_parties, lib/geomet-0.1.0.zip is no longer
added to sys.path, so every cqlsh invocation crashes with:
  ModuleNotFoundError: No module named 'geomet'

Fix: restore 'geomet-' to third_parties and upgrade the bundled zip from
0.1.0 to 1.1.0 (matching cassandra-driver 3.29.3's geomet>=1.1 requirement)
by fetching it from PyPI in build-resolver.xml.
…efore SSTable import

truncateBlocking() schedules SSTable file deletion asynchronously on
NonPeriodicTasks. loadNewSSTables() was called immediately after, creating a
race where the SSTableLister found files from the just-deleted SSTable and then
failed to rename them (NoSuchFileException on TOC.txt).

Fix: add LifecycleTransaction.waitForDeletions() after truncateBlocking() in
loadTestSStables(). ImportTest already uses this pattern for the same reason.
… for INDEX_BUILD_STARTED flush before inserting data

The index build triggered by CREATE INDEX flushes the memtable asynchronously
via INDEX_BUILD_STARTED. Using execute() to create the index returns immediately
without waiting for that flush to complete. If data is inserted before the flush
finishes, the flush itself generates a second SSTable; the tests then fail with
"expected exactly one SSTable" assertions.

Fix: replace execute() with createIndex() (CQLTester helper) in all three tests.
createIndex() blocks until the index is queryable (INDEX_BUILD_STARTED flush
has completed), ensuring exactly one SSTable when data is subsequently inserted.

Affects: RowAwareSkinnyPrimaryKeyMapTest, RowAwareStaticClusteringPrimaryKeyMapTest,
RowAwareWidePrimaryKeyMapTest.
… cassandra-5.0.9

The import org.apache.cassandra.schema.Schema was already unused in ImportTest.java
in the cassandra-5.0.9 upstream. This is a pre-existing issue in the Apache code
brought in by the rebase onto cassandra-5.0.9.
@JeremiahDJordan
JeremiahDJordan force-pushed the cndb-18860-main-5.0.9-rebase branch from 4faa67a to 38b34ce Compare August 19, 2026 17:13
…ore SELECT

testCreateAndDropIndex() called createIndexAsync() then later queried the table
using the index. Our SecondaryIndexManager.checkQueryability() throws
IndexBuildInProgressException when the index status is FULL_REBUILD_STARTED,
which is stricter than vanilla 5.0.9 and exposed the pre-existing race.

Fix: add waitForIndexQueryable() immediately before the assertRows() SELECT,
exactly at the point where queryability is first required. All preceding
assertInvalidMessage() calls test schema-level duplicate detection and do not
go through checkQueryability(), so they do not need the index to be queryable.
@sonarqube-dx-prod

Copy link
Copy Markdown

Quality Gate failed Quality Gate failed

Failed conditions
605 New issues

See analysis details on SonarQube

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE SonarQube for IDE

@plpesvc-ds

Copy link
Copy Markdown

❌ Build ds-cassandra-pr-gate/PR-2565 rejected by Butler


2 regressions found
See build details here


Found 2 new test failures

Test Explanation Runs Upstream
o.a.c.index.sai.cql.VectorCompaction100dTest.testOneToOneCompaction[version=eb enableNVQ=false] () NEW 🔴 0 / 30
o.a.c.index.sai.cql.VectorSiftSmallTest.testMultiSegmentBuild[ec false] () NEW 🔴🔴 0 / 30

Found 3 known test failures

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.