{"record":{"id":"eff12a63c6fa3dec","repo":"apache/cassandra","slug":"cannot-use-transient-replication-on-keyspaces-usin-eff12a","errorCode":null,"errorMessage":"Cannot use transient replication on keyspaces using secondary indexes","messagePattern":"Cannot use transient replication on keyspaces using secondary indexes","errorType":"validation","errorClass":"ConfigurationException","httpStatus":null,"severity":"error","filePath":"src/java/org/apache/cassandra/cql3/statements/schema/AlterKeyspaceStatement.java","lineNumber":210,"sourceCode":"        ReplicationFactor newRF = proposed.replicationStrategy.getReplicationFactor();\n\n        int oldTrans = oldRF.transientReplicas();\n        int oldFull = oldRF.fullReplicas;\n        int newTrans = newRF.transientReplicas();\n        int newFull = newRF.fullReplicas;\n\n        if (newTrans > 0)\n        {\n            if (DatabaseDescriptor.getNumTokens() > 1)\n                throw new ConfigurationException(String.format(\"Transient replication is not supported with vnodes yet\"));\n\n\n            if (!current.views.isEmpty())\n                throw new ConfigurationException(\"Cannot use transient replication on keyspaces using materialized views\");\n\n            for (TableMetadata table : current.tables)\n                if (!table.indexes.isEmpty())\n                    throw new ConfigurationException(\"Cannot use transient replication on keyspaces using secondary indexes\");\n        }\n\n        //This is true right now because the transition from transient -> full lacks the pending state\n        //necessary for correctness. What would happen if we allowed this is that we would attempt\n        //to read from a transient replica as if it were a full replica.\n        if (oldFull > newFull && oldTrans > 0)\n            throw new ConfigurationException(\"Can't add full replicas if there are any transient replicas. You must first remove all transient replicas, then change the # of full replicas, then add back the transient replicas\");\n\n        //Don't increase transient replication factor by more than one at a time if changing number of replicas\n        //Just like with changing full replicas it's not safe to do this as you could read from too many replicas\n        //that don't have the necessary data. W/O transient replication this alteration was allowed and it's not clear\n        //if it should be.\n        //This is structured so you can convert as many full replicas to transient replicas as you want.\n        boolean numReplicasChanged = oldTrans + oldFull != newTrans + newFull;\n        if (numReplicasChanged && (newTrans > oldTrans && newTrans != oldTrans + 1))\n            throw new ConfigurationException(\"Can only safely increase number of transients one at a time with incremental repair run in between each time\");\n    }\n","sourceCodeStart":192,"sourceCodeEnd":228,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/cql3/statements/schema/AlterKeyspaceStatement.java#L192-L228","documentation":"A keyspace using secondary indexes cannot enable transient replication, because 2i write/read paths also assume full replicas. Validation fires when altering a keyspace that has any table with indexes to a nonzero transient replication factor.","triggerScenarios":"`ALTER KEYSPACE ks WITH replication = {..., 'dc1':'3/1'}` where any table in ks has secondary indexes (table.indexes not empty).","commonSituations":"Adding transient replication to a keyspace where users created local/global secondary indexes for ad-hoc queries.","solutions":["Drop the secondary indexes before enabling transient replication (and consider SASI-free/query-table alternatives compatible with your setup).","Use only full replicas for keyspaces with secondary indexes.","Move indexed tables to a keyspace that uses non-transient replication."],"exampleFix":"// before\n\"ALTER KEYSPACE ks WITH replication = {'class':'NetworkTopologyStrategy','dc1':'3/1'}\"; // ks has 2i\n// after\ndropIndexes(ks); // then\n\"ALTER KEYSPACE ks WITH replication = {'class':'NetworkTopologyStrategy','dc1':'3/1'}\";","handlingStrategy":"validation","validationCode":"boolean hasIndexes = ksm.tables.stream().anyMatch(t -> !t.indexes.isEmpty()); if (hasIndexes && wantsTransient) throw new IllegalArgumentException(\"drop secondary indexes before transient replication\");","typeGuard":null,"tryCatchPattern":"try { session.execute(alterKeyspace); } catch (ConfigurationException e) { if (e.getMessage().contains(\"transient replication on keyspaces using secondary indexes\")) dropSecondaryIndexes(ks); else throw e; }","preventionTips":["Inventory secondary indexes per keyspace before RF changes","Prefer query tables or materialized views (in a non-transient KS) over 2i on transient keyspaces","Gate transient-replication rollouts behind a schema audit"],"tags":["configuration","transient-replication","secondary-index","alter-keyspace"],"backgroundTag":"conflicting-config-options","analyzedSha":"88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1","analyzedAt":"2026-09-10T07:29:22.284Z","contentChangedAt":"2026-09-10T07:29:22.284Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}