apache/cassandra · warning

Executing a LOGGED BATCH on table

Error message

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.

What it means

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.

Solutions

  1. Raise gc_grace_seconds on tables used in LOGGED batches (e.g. 864000 for 1 day)
  2. Switch to UNLOGGED batches if atomicity across tables is not required
  3. Restructure the data model to avoid multi-partition atomic batches entirely

Example fix

// before
CREATE TABLE t (k int PRIMARY KEY, v text) WITH gc_grace_seconds = 0;
// after
CREATE TABLE t (k int PRIMARY KEY, v text) WITH gc_grace_seconds = 86400;
Defensive patterns

Strategy: validation

Validate before calling

// Schema review check before using LOGGED batches on a table
function assertSafeForLoggedBatch(tableSchema) {
  const ggs = tableSchema.options['gc_grace_seconds'];
  if (ggs === 0) throw new Error('gc_grace_seconds=0 on table used in LOGGED batch: ' + tableSchema.name);
}

Prevention

When it happens

Trigger: Executing BEGIN ... APPLY BATCH (logged) via getMutations() (mutations/executeInternalWithoutConditions paths) where any mutated table has gc_grace_seconds set to 0.

Common situations: 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.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


AI-assisted analysis of apache/cassandra@88fd0f6a0e (2026-09-10). Data as JSON: /api/errors/c0f2976af5bf7862. Report an issue: GitHub.

Appendix: source

Thrown at src/java/org/apache/cassandra/cql3/statements/BatchStatement.java:412

        {
            ModificationStatement statement = statements.get(i);
            if (isLogged() && statement.metadata().params.gcGraceSeconds == 0)
            {
                if (tablesWithZeroGcGs == null)
                    tablesWithZeroGcGs = new HashSet<>();
                tablesWithZeroGcGs.add(statement.metadata.toString());
            }
            QueryOptions statementOptions = options.forStatement(i);
            long timestamp = attrs.getTimestamp(batchTimestamp, statementOptions);
            statement.addUpdates(collector, partitionKeys.get(i), state, statementOptions, local, timestamp, nowInSeconds, requestTime);
        }

        if (tablesWithZeroGcGs != null)
        {
            String suffix = tablesWithZeroGcGs.size() == 1 ? "" : "s";
            NoSpamLogger.log(logger, NoSpamLogger.Level.WARN, 1, TimeUnit.MINUTES, LOGGED_BATCH_LOW_GCGS_WARNING,
                             suffix, tablesWithZeroGcGs);
            ClientWarn.instance.warn(MessageFormatter.arrayFormat(LOGGED_BATCH_LOW_GCGS_WARNING, new Object[] { suffix, tablesWithZeroGcGs })
                                                     .getMessage());
        }
        // local is either executeWithoutConditions modifying a virtual table (doesn't support txns) or executeLocal
        // which is called by test or internal things that are bypassing distributed system modification/checks
        return collector.toMutations(state, local ? PotentialTxnConflicts.ALLOW : PotentialTxnConflicts.DISALLOW);
    }

    /**
     * Checks batch size to ensure threshold is met. If not, a warning is logged.
     *
     * @param mutations - the batch mutations.
     */
    private static void verifyBatchSize(Collection<? extends IMutation> mutations) throws InvalidRequestException
    {
        // We only warn for batch spanning multiple mutations (#10876)
        if (mutations.size() <= 1)
            return;

View on GitHub (pinned to 88fd0f6a0e)