{"record":{"id":"c0f2976af5bf7862","repo":"apache/cassandra","slug":"executing-a-logged-batch-on-table-configured","errorCode":null,"errorMessage":"Executing a LOGGED BATCH on table{} {}, configured with a gc_grace_seconds of 0. The gc_grace_seconds is used to TTL batchlog entries, so setting gc_grace_seconds too low on tables involved in an atomic batch might cause batchlog entries to expire before being replayed.","messagePattern":"Executing a LOGGED BATCH on table(.+?) (.+?), configured with a gc_grace_seconds of 0\\. The gc_grace_seconds is used to TTL batchlog entries, so setting gc_grace_seconds too low on tables involved in an atomic batch might cause batchlog entries to expire before being replayed\\.","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/java/org/apache/cassandra/cql3/statements/BatchStatement.java","lineNumber":412,"sourceCode":"        {\n            ModificationStatement statement = statements.get(i);\n            if (isLogged() && statement.metadata().params.gcGraceSeconds == 0)\n            {\n                if (tablesWithZeroGcGs == null)\n                    tablesWithZeroGcGs = new HashSet<>();\n                tablesWithZeroGcGs.add(statement.metadata.toString());\n            }\n            QueryOptions statementOptions = options.forStatement(i);\n            long timestamp = attrs.getTimestamp(batchTimestamp, statementOptions);\n            statement.addUpdates(collector, partitionKeys.get(i), state, statementOptions, local, timestamp, nowInSeconds, requestTime);\n        }\n\n        if (tablesWithZeroGcGs != null)\n        {\n            String suffix = tablesWithZeroGcGs.size() == 1 ? \"\" : \"s\";\n            NoSpamLogger.log(logger, NoSpamLogger.Level.WARN, 1, TimeUnit.MINUTES, LOGGED_BATCH_LOW_GCGS_WARNING,\n                             suffix, tablesWithZeroGcGs);\n            ClientWarn.instance.warn(MessageFormatter.arrayFormat(LOGGED_BATCH_LOW_GCGS_WARNING, new Object[] { suffix, tablesWithZeroGcGs })\n                                                     .getMessage());\n        }\n        // local is either executeWithoutConditions modifying a virtual table (doesn't support txns) or executeLocal\n        // which is called by test or internal things that are bypassing distributed system modification/checks\n        return collector.toMutations(state, local ? PotentialTxnConflicts.ALLOW : PotentialTxnConflicts.DISALLOW);\n    }\n\n    /**\n     * Checks batch size to ensure threshold is met. If not, a warning is logged.\n     *\n     * @param mutations - the batch mutations.\n     */\n    private static void verifyBatchSize(Collection<? extends IMutation> mutations) throws InvalidRequestException\n    {\n        // We only warn for batch spanning multiple mutations (#10876)\n        if (mutations.size() <= 1)\n            return;\n","sourceCodeStart":394,"sourceCodeEnd":430,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/cql3/statements/BatchStatement.java#L394-L430","documentation":"BatchStatement warns (NoSpamLogger at most once per minute) when a LOGGED batch writes to tables configured with gc_grace_seconds=0. In a logged batch, gc_grace_seconds governs batchlog entry TTL, so 0 lets batchlog entries expire before replay can fix partial batch application, weakening atomicity.","triggerScenarios":"Executing BEGIN ... APPLY BATCH (logged) via getMutations() (mutations/executeInternalWithoutConditions paths) where any mutated table has gc_grace_seconds set to 0.","commonSituations":"Using LOGGED batches on TTL/counter/leveled tables tuned with gc_grace_seconds: 0 for read-repair reasons; copy-pasted schema defaults from non-batch workloads; batch misuse for large multi-partition writes.","solutions":["Raise gc_grace_seconds on tables used in LOGGED batches (e.g. 864000 for 1 day)","Switch to UNLOGGED batches if atomicity across tables is not required","Restructure the data model to avoid multi-partition atomic batches entirely"],"exampleFix":"// before\nCREATE TABLE t (k int PRIMARY KEY, v text) WITH gc_grace_seconds = 0;\n// after\nCREATE TABLE t (k int PRIMARY KEY, v text) WITH gc_grace_seconds = 86400;","handlingStrategy":"validation","validationCode":"// Schema review check before using LOGGED batches on a table\nfunction assertSafeForLoggedBatch(tableSchema) {\n  const ggs = tableSchema.options['gc_grace_seconds'];\n  if (ggs === 0) throw new Error('gc_grace_seconds=0 on table used in LOGGED batch: ' + tableSchema.name);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Default gc_grace_seconds to 864000 unless you have a documented reason for 0","Reserve LOGGED batches for small, few-partition atomic writes","Audit schema for gc_grace_seconds=0 tables used in batches"],"tags":["batch","gc-grace-seconds","data-consistency"],"backgroundTag":"invalid-config-value","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"}