I use Apache Spark to read data from Cassandra, which under the hood does, token range based scan. But, recently I see a lot of failure during data read from my Cassandra table.
Read fails with below cause:
Caused by: com.datastax.driver.core.exceptions.ReadFailureException: Cassandra failure during read query at consistency LOCAL_ONE (1 responses were required but only 0 replica responded, 1 failed)
at com.datastax.driver.core.exceptions.ReadFailureException.copy(ReadFailureException.java:85)
at com.datastax.driver.core.exceptions.ReadFailureException.copy(ReadFailureException.java:27)
at com.datastax.driver.core.DriverThrowables.propagateCause(DriverThrowables.java:37)
at com.datastax.driver.core.DefaultResultSetFuture.getUninterruptibly(DefaultResultSetFuture.java:245)
at com.datastax.driver.core.AbstractSession.execute(AbstractSession.java:68)
When I checked the systemlogs I found out below issue:
ERROR [ReadStage-1] 2020-04-09 10:25:59,701 StorageProxy.java:1896 - Scanned over 100001 tombstones during query 'SELECT * FROM my_keyspace.my_table WHERE token(id) > -5868586221781016365 AND token(id) <= -5347313590141106513 LIMIT 5000' (last scanned row partion key was (my_key_1)); query aborted
The error log is straight forward, the reads are failing due to tombstone scans.
What I don't get is, I was running the same Spark job for over a year and never faced this problem. But, that's secondary. First I would like to know how can I fix this?
The tombstones could be created for the column values that are not passed, as I use Cassandra Insert Json feature to insert doc. Confirmed here, that this approach will create the tombstones.
How do I fix this? Creating multiple insert queries based on non-null values is a complicated option.
Even inserting dummy value is impractical in my opinion. All the clients that read data from these tables need to make the change.
I personally feel Cassandra should never create Tombstone for a column if the column doesn't exist in JSON.
What are the other options to solve this?
You have several possibilities:
unset
instead of missing values (done by default, look to docs) - you can simplify your work by mapping JSON into POJO, and using Object Mapper that also supports replacement of nulls with unset
. This is best approach, as tombstones will affect all applications, and Cassandra itself. But real solution will depend on your use case - are you inserting only new data, or you're updating existing as well? In the later case, you'll need to make sure that you overwrite previous dataspark.cassandra.input.fetch.size_in_rows
parameter, described in docs. If you're using DSE with DSE Analytics, you may need to disable continuous paging