[ 
https://issues.apache.org/jira/browse/CASSANDRA-21794?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18135884#comment-18135884
 ] 

Deepak Kumar Jothimanickam commented on CASSANDRA-21794:
--------------------------------------------------------

[~mck] Good day, Can you assign the ticket to me, I want to work on this

 

> Commit log replay after an interrupted DROP TABLE discards whole mutations 
> that also touched the dropped table (5.0: acknowledged rows of other tables 
> in the same logged batch are lost; trunk: the node does not start)
> -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
>
>                 Key: CASSANDRA-21794
>                 URL: https://issues.apache.org/jira/browse/CASSANDRA-21794
>             Project: Apache Cassandra
>          Issue Type: Bug
>          Components: Local/Commit Log, Local/Startup and Shutdown
>            Reporter: Ke Han
>            Priority: Normal
>
>  
> Affects Version/s: 5.0.9, 5.0.x, 7.0 (trunk); by code inspection also 6.0 
> (same CoordinatorBehindException path as trunk) and 4.1.x (same per-mutation 
> skip as 5.0)
>  
> Environment: Single node, Linux 5.15 x86_64, ext4, OpenJDK 11, 
> commitlog_sync: batch, disk_failure_policy: stop (default). Built from 
> source: trunk 95b178a1 (2026-10-17) and cassandra-5.0 dd48703a (2026-10-17); 
> first seen on the 5.0.9 docker image.
>  
> Description:
>  
> *What happens*
>  
> A logged batch that writes the same partition key to two tables of one 
> keyspace is stored as one Mutation per partition key, carrying a 
> PartitionUpdate for each table. DROP TABLE a calls 
> ColumnFamilyStore.onTableDropped -> CommitLog.forceRecycleAllSegments, which 
> flushes every table with data in the current segments, so normally the replay 
> never needs those mutations again. If that forced flush does not complete (an 
> I/O error with disk_failure_policy: stop, a crash, a power loss) while the 
> drop itself has already been persisted, the rows of the other table b exist 
> only in the commit log, and the replay handles the mixed mutations as a whole:
>  
> - cassandra-5.0: PartitionUpdate deserialization throws UnknownTableException 
> for a's update, CommitLogReader.readMutation catches it and skips the entire 
> mutation ("Skipped N mutations from unknown (probably removed) CF"), so the 
> acknowledged b rows in it are lost silently.
> - trunk: the deserialization throws CoordinatorBehindException instead 
> (PartitionUpdate.serializer, lines 801-814: the local epoch is after the 
> mutation's epoch), which readMutation does not treat as an unknown table; 
> start-up stops with "Exiting due to error while processing commit log during 
> initialization". This happens for any leftover mutation of the dropped table, 
> not only mixed ones. With -Dcassandra.commitlog.ignorereplayerrors=true the 
> node starts and the mixed mutations are dropped, losing the b rows as on 5.0.
>  
> *Reproduce* (cqlsh and nodetool against a single node with commitlog_sync: 
> batch; strace >= 4.16, run as root or with ptrace permission)
>  
> {code:bash}
> bin/cqlsh -e "
> CREATE KEYSPACE ks WITH replication = \{'class': 'SimpleStrategy', 
> 'replication_factor': 1};
> CREATE TABLE ks.a (id int PRIMARY KEY, v text);
> CREATE TABLE ks.b (id int PRIMARY KEY, v text);
> BEGIN BATCH
>  INSERT INTO ks.a (id, v) VALUES (1, 'a1'); INSERT INTO ks.a (id, v) VALUES 
> (2, 'a2'); INSERT INTO ks.a (id, v) VALUES (3, 'a3');
>  INSERT INTO ks.a (id, v) VALUES (4, 'a4'); INSERT INTO ks.a (id, v) VALUES 
> (5, 'a5');
>  INSERT INTO ks.b (id, v) VALUES (1, 'b1'); INSERT INTO ks.b (id, v) VALUES 
> (2, 'b2'); INSERT INTO ks.b (id, v) VALUES (3, 'b3');
>  INSERT INTO ks.b (id, v) VALUES (4, 'b4'); INSERT INTO ks.b (id, v) VALUES 
> (5, 'b5'); INSERT INTO ks.b (id, v) VALUES (6, 'b6');
>  INSERT INTO ks.b (id, v) VALUES (7, 'b7'); INSERT INTO ks.b (id, v) VALUES 
> (8, 'b8'); INSERT INTO ks.b (id, v) VALUES (9, 'b9');
>  INSERT INTO ks.b (id, v) VALUES (10, 'b10');
> APPLY BATCH;
> SELECT count(*) FROM ks.b;"                                # 10
>  
> # one EIO on the first fsync of b's table directory (the forced flush of b 
> creates its flush txn log there);
> # use signal=KILL instead of error=EIO for a crash at the same point
> BDIR=$(ls -d /var/lib/cassandra/data/ks/b-*)
> strace -f -o /tmp/strace.log -e trace=fsync -e inject=fsync:error=EIO:when=1 
> -P $BDIR -p $(pgrep -f CassandraDaemon) &
> sleep 5
> bin/cqlsh -e "DROP TABLE ks.a;"     # FSWriteError, "Stopping transports as 
> disk_failure_policy is stop"
> kill %1; kill -9 $(pgrep -f CassandraDaemon)
> bin/cassandra                       # restart
> bin/cqlsh -e "SELECT id, v FROM ks.b;"
> {code}
>  
> *Actual*
>  
> cassandra-5.0 dd48703a (same with signal=KILL instead of EIO):
>  
> {noformat}
> WARN  [main] CommitLogReplayer.java:247 - Skipped 5 mutations from unknown 
> (probably removed) CF with id c76c6560-cb2e-11f1-af99-b54fecb308d4
> INFO  [main] CommitLog.java:210 - Log replay complete, 13 replayed mutations 
> in 526 ms
>  
>  id | v
> ----+-----
>  10 | b10
>   8 |  b8
>   7 |  b7
>   6 |  b6
>   9 |  b9
> (5 rows)
> {noformat}
>  
> Rows 1..5 of b, acknowledged by the batch long before the DROP, are gone. 
> Control: the same script with a's inserts using ids 101..105 (no shared 
> partition keys) also skips 5 mutations (a's own) and returns all 10 rows of b.
>  
> trunk 95b178a1 (EIO or SIGKILL at the same point; also with the ids 101..105 
> control):
>  
> {noformat}
> ERROR [main] DefaultDiskErrorsHandler.java:177 - Exiting due to error while 
> processing commit log during initialization.
> org.apache.cassandra.db.commitlog.CommitLogReadHandler$CommitLogReadException:
>  Unexpected error deserializing mutation; saved to 
> /tmp/mutation4107001581964003181dat.  This may be caused by replaying a 
> mutation against a table with the same name but incompatible schema.  
> Exception follows: 
> org.apache.cassandra.exceptions.CoordinatorBehindException: Couldn't find 
> table with id tid:4. If a table was just created, this is likely due to the 
> schema not being fully propagated.  Please wait for schema agreement on table 
> creation.
> at 
> org.apache.cassandra.db.commitlog.CommitLogReader.readMutation(CommitLogReader.java:472)
> at 
> org.apache.cassandra.db.commitlog.CommitLogReader.readSection(CommitLogReader.java:405)
> at 
> org.apache.cassandra.db.commitlog.CommitLogReader.readCommitLogSegment(CommitLogReader.java:251)
> ...
> at 
> org.apache.cassandra.service.CassandraDaemon.setup(CassandraDaemon.java:362)
> {noformat}
>  
> The node does not start. Restarted with 
> -Dcassandra.commitlog.ignorereplayerrors=true it starts ("Ignoring commit log 
> replay error" for each mixed mutation) and SELECT returns only ids 6..10.
>  
> *Expected*
>  
> All 10 rows of b after the restart, and a node that starts. Mutations for a 
> table that was dropped after they were written are expected garbage; the 
> replay should skip only the PartitionUpdate of the dropped table and apply 
> the rest of the mutation (on trunk, a mutation older than the drop should be 
> treated like an unknown table during local replay, not as a coordinator that 
> is behind).
>  
> *Code (trunk 95b178a1)*
>  
> - src/java/org/apache/cassandra/db/commitlog/CommitLogReader.java:446-458 
> catches UnknownTableException around Mutation.serializer.deserialize and 
> returns, dropping the whole mutation; any other exception goes to the 
> "Unexpected error deserializing mutation" path (460-480), which is fatal 
> unless ignorereplayerrors is set.
> - src/java/org/apache/cassandra/db/Mutation.java:598-612 deserializes the 
> PartitionUpdates one by one; the first unknown table aborts the whole 
> mutation.
> - src/java/org/apache/cassandra/db/partitions/PartitionUpdate.java:801-814 
> turns UnknownTableException into CoordinatorBehindException when the local 
> epoch is after the mutation's epoch, which is what happens when replaying a 
> mutation written before the DROP (observed in every run).
> - src/java/org/apache/cassandra/db/ColumnFamilyStore.java:3414 
> (onTableDropped) is where the forced flush happens; nothing on start-up 
> accounts for it not having completed.
> - cassandra-5.0 dd48703a: CommitLogReader.java:438 (catch 
> UnknownTableException), CommitLogReplayer.java:247 (the "Skipped ... probably 
> removed" warning).
>  
> *Related*
>  
> - CASSANDRA-1353 (fixed 2010): "commit log recovery is broken when the CL 
> contains mutations to CFs that have been dropped". The skipping that exists 
> today works per mutation, not per table.
> - CASSANDRA-9179 (fixed): replay skips mutations of a dropped-and-recreated 
> table; by design for that table, it does not cover other tables in the same 
> mutation.
> - CASSANDRA-16878 (Review In Progress): a race in commit log replay can cause 
> rejected mutations; different cause.
>  



--
This message was sent by Atlassian Jira
(v10.3.26#10030026)

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to