{"record":{"id":"37199b2f1533b3d3","repo":"apache/cassandra","slug":"received-base-materialized-view-mutation-for-key","errorCode":null,"errorMessage":"Received base materialized view mutation for key {} that does not belong to this node. There is probably a range movement happening (move or decommission),but this node hasn't updated its ring metadata yet. Adding mutation to local batchlog to be replayed later.","messagePattern":"Received base materialized view mutation for key (.+?) that does not belong to this node\\. There is probably a range movement happening \\(move or decommission\\),but this node hasn't updated its ring metadata yet\\. Adding mutation to local batchlog to be replayed later\\.","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/java/org/apache/cassandra/service/StorageProxy.java","lineNumber":1160,"sourceCode":"                BatchlogCleanup cleanup = new BatchlogCleanup(mutations.size(),\n                                                              () -> asyncRemoveFromBatchlog(localReplicaPlan, batchUUID, requestTime));\n\n                // add a handler for each mutation - includes checking availability, but doesn't initiate any writes, yet\n                for (Mutation mutation : mutations)\n                {\n                    String keyspaceName = mutation.getKeyspaceName();\n                    Token tk = mutation.key().getToken();\n                    Function<ClusterMetadata, Optional<Replica>> pairedEndpointSupplier = (cm) -> ViewUtils.getViewNaturalEndpoint(cm, keyspaceName, baseToken, tk);\n                    Function<ClusterMetadata, VersionedEndpoints.ForToken>pendingReplicasSupplier = (cm) -> cm.pendingEndpointsFor(Keyspace.open(keyspaceName).getMetadata(), tk);\n\n                    Optional<Replica> pairedEndpoint = pairedEndpointSupplier.apply(metadata);\n                    VersionedEndpoints.ForToken pendingReplicas = pendingReplicasSupplier.apply(metadata);\n\n                    // if there are no paired endpoints there are probably range movements going on, so we write to the local batchlog to replay later\n                    if (!pairedEndpoint.isPresent())\n                    {\n                        if (pendingReplicas.isEmpty())\n                            logger.warn(\"Received base materialized view mutation for key {} that does not belong \" +\n                                        \"to this node. There is probably a range movement happening (move or decommission),\" +\n                                        \"but this node hasn't updated its ring metadata yet. Adding mutation to \" +\n                                        \"local batchlog to be replayed later.\",\n                                        mutation.key());\n                        continue;\n                    }\n\n                    // When local node is the endpoint we can just apply the mutation locally,\n                    // unless there are pending endpoints, in which case we want to do an ordinary\n                    // write so the view mutation is sent to the pending endpoint\n                    if (pairedEndpoint.get().isSelf() && StorageService.instance.isJoined()\n                        && pendingReplicas.isEmpty())\n                    {\n                        try\n                        {\n                            mutation.apply(writeCommitLog);\n                            nonLocalMutations.remove(mutation);\n                            // won't trigger cleanup","sourceCodeStart":1142,"sourceCodeEnd":1178,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/service/StorageProxy.java#L1142-L1178","documentation":"When applying a base-table mutation that requires a materialized-view update, the coordinator computes which replica pair (base endpoint + view endpoint) should handle it. If no paired endpoint exists for this node's token, ring metadata is out of sync with an in-progress range movement (move/decommission/bootstrap). The mutation is written to the local batchlog for later replay so the view update is not lost.","triggerScenarios":"applyAggregationOnBase likely called during MV write path when pairedEndpoint is absent and pendingReplicas is empty — i.e., the mutation key's token maps to no current+pending replica pair on this node, typically because token metadata hasn't refreshed after a ring change.","commonSituations":"Decommission, bootstrap, or range move in progress while writes continue; gossip/ring metadata propagation lag between nodes; client hitting a node that hasn't yet learned about new token ownership.","solutions":["Wait for ring convergence (nodetool ring/describering show consistent ownership) — batchlog replay handles the mutation automatically","Ensure 'nodetool drain/ decommission' completed cleanly and all nodes agree on tokens","Run repair afterwards to reconcile MV rows; the message itself recommends checking range movement state","If persistent with no movement ongoing, check for gossip partitioning/firewall issues between nodes"],"exampleFix":"// before\n$ nodetool decommission   # writes continuing elsewhere hit this warning\n\n// after\n$ nodetool netstats && nodetool describering -- keyspace   # confirm ring settled\n$ nodetool repair -pr keyspace mv_table","handlingStrategy":"retry","validationCode":"// before MV-heavy writes during topology changes, confirm ring stability\n// (via JMX/metadata): pending ranges empty and all nodes agree on ownership\nassert pendingRanges().isEmpty() : \"range movement in progress; defer writes or expect batchlog replay\";","typeGuard":null,"tryCatchPattern":"try {\n    executeMvWrite(mutation);\n} catch (WriteFailureException | WriteTimeoutException e) {\n    // mutation was added to local batchlog; verify replay, then repair\n    scheduleRepair(keyspace, mvTable);\n}","preventionTips":["Quiesce or throttle writes during move/decommission/bootstrap","Monitor gossip convergence before declaring topology changes complete","Run repair after every topology change on MV keyspaces"],"tags":["materialized-views","ring","topology","batchlog"],"backgroundTag":"ring-metadata-stale","analyzedSha":"88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1","analyzedAt":"2026-09-10T07:29:22.284Z","contentChangedAt":"2026-09-10T07:29:22.284Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}