Oversimplifying the data model, we have the following tables:
CREATE TABLE storage (
id timeuuid,
foo blob,
bar blob,
baz blob,
data blob,
PRIMARY KEY ((id))
);
CREATE TABLE storage_idx_by_foo (
foo blob,
id timeuuid,
PRIMARY KEY ((foo), id)
);
CREATE TABLE storage_idx_by_bar (
bar blob,
id timeuuid,
PRIMARY KEY ((bar), id)
);
CREATE TABLE storage_idx_by_baz (
baz blob,
id timeuuid,
PRIMARY KEY ((baz), id)
);
The first table can contain hundreds of millions of records, and we use the index tables to easily locate data based on some queryable parameters.
The problem comes when we have to purge data based on either foo, bar or baz. We have to delete the entry from the storage table and all the index tables. So, assuming we delete by e.g. foo, steps taken are:
- Find the id based on the appropriate index table (in this case storage_idx_by_foo)
- Get the bar and baz and delete the record from the storage table
- Delete records from the remaining two index tables (we have bar/baz and id)
Step number 3 is a problem because of tombstones - if we delete millions of records from the two remaining index tables (meaning not by partition), Cassandra will create millions of tombstones which will cause a lot of headaches when reading data before compaction happens.
Some quick brainstorming suggests we can:
- Force a compaction after the purging process
- Not delete from those two tables and handle index entries pointing to non-existing stuff in code
- ????
What is the suggested approach? I am guessing other Cassandra users have encountered this issue as well, but I couldn't find any advice online other than "you are doing Cassandra wrong". I don't think we could have modelled our data differently to avoid this problem (or if we could, I would appreciate feedback on that as well).
Currently, we are leaning towards option number 2, although I do not like the idea of garbage being left in the database.