{"record":{"id":"d19d231bbab23b2d","repo":"apache/cassandra","slug":"can-only-safely-increase-number-of-transients-one","errorCode":null,"errorMessage":"Can only safely increase number of transients one at a time with incremental repair run in between each time","messagePattern":"Can only safely increase number of transients one at a time with incremental repair run in between each time","errorType":"validation","errorClass":"ConfigurationException","httpStatus":null,"severity":"error","filePath":"src/java/org/apache/cassandra/cql3/statements/schema/AlterKeyspaceStatement.java","lineNumber":226,"sourceCode":"            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\n    @Override\n    public AuditLogContext getAuditLogContext()\n    {\n        return new AuditLogContext(AuditLogEntryType.ALTER_KEYSPACE, keyspaceName);\n    }\n\n    public String toString()\n    {\n        return String.format(\"%s (%s)\", getClass().getSimpleName(), keyspaceName);\n    }\n\n    public static final class Raw extends CQLStatement.Raw\n    {\n        private final String keyspaceName;\n        private final KeyspaceAttributes attrs;\n        private final boolean ifExists;","sourceCodeStart":208,"sourceCodeEnd":244,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/cql3/statements/schema/AlterKeyspaceStatement.java#L208-L244","documentation":"When the total replica count changes and transient replicas are being increased, Cassandra only allows increasing transients by exactly one at a time, requiring an incremental repair between increases, because un-repaired ranges on new transient replicas could be read unsafely.","triggerScenarios":"`ALTER KEYSPACE ks WITH replication = {'dc1':'3/0' -> '3/2'}` (transients increased by 2 in one step) while the total replica count also changed.","commonSituations":"Operators jumping transient RF from 0 to 2 (or similar multi-step jump) to save capacity quickly, without running incremental repair between each increment.","solutions":["Increase transient replicas one at a time: '3/0' -> '3/1', run incremental repair, then '3/1' -> '3/2'.","Run `nodetool repair -inc` on the keyspace after each single increment.","If total replica count is unchanged, convert full->transient freely; otherwise follow the one-at-a-time rule."],"exampleFix":"// before\n\"ALTER KEYSPACE ks WITH replication = {'dc1':'3/2'}\"; // from '3/0'\n// after\n\"ALTER KEYSPACE ks WITH replication = {'dc1':'3/1'}\"; runIncrementalRepair();\n\"ALTER KEYSPACE ks WITH replication = {'dc1':'3/2'}\";","handlingStrategy":"validation","validationCode":"boolean numReplicasChanged = oldTrans + oldFull != newTrans + newFull; if (numReplicasChanged && newTrans > oldTrans && newTrans != oldTrans + 1) throw new IllegalArgumentException(\"increase transients by exactly 1 per step with repair between\");","typeGuard":null,"tryCatchPattern":"try { session.execute(alterKeyspace); } catch (ConfigurationException e) { if (e.getMessage().contains(\"increase number of transients one at a time\")) applyOneTransientAtATime(target); else throw e; }","preventionTips":["Cap transient-RF increases to +1 per ALTER KEYSPACE","Always run `nodetool repair -inc` between increments","Encode the stepwise policy in cluster-management automation"],"tags":["configuration","transient-replication","repair","alter-keyspace"],"backgroundTag":"invalid-state-transition","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"}