Skip to content

Commit

Permalink
query: do not kill unpaged queries when they reach the tombstone-limit
Browse files Browse the repository at this point in the history
The reason we introduced the tombstone-limit
(query_tombstone_page_limit), was to allow paged queries to return
incomplete/empty pages in the face of large tombstone spans. This works
by cutting the page after the tombstone-limit amount of tombstones were
processed. If the read is unpaged, it is killed instead. This was a
mistake. First, it doesn't really make sense, the reason we introduced
the tombstone limit, was to allow paged queries to process large
tombstone-spans without timing out. It does not help unpaged queries.
Furthermore, the tombstone-limit can kill internal queries done on
behalf of user queries, because all our internal queries are unpaged.
This can cause denial of service.

So in this patch we disable the tombstone-limit for unpaged queries
altogether, they are allowed to continue even after having processed the
configured limit of tombstones.

Fixes: scylladb#17241
  • Loading branch information
denesb committed Feb 9, 2024
1 parent 876478b commit 3512592
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 3 deletions.
6 changes: 3 additions & 3 deletions query-result-writer.hh
Original file line number Diff line number Diff line change
Expand Up @@ -136,9 +136,9 @@ public:
return stop_iteration::no;
}
if (!_slice.options.contains<partition_slice::option::allow_short_read>()) {
throw std::runtime_error(fmt::format(
"Tombstones processed by unpaged query exceeds limit of {} (configured via query_tombstone_page_limit)",
_tombstone_limit));
// The read is unpaged, we cannot interrupt it early without failing it.
// Better let it continue.
return stop_iteration::no;
}
return stop_iteration::yes;
}
Expand Down
20 changes: 20 additions & 0 deletions test/cql-pytest/test_tombstone_limit.py
Original file line number Diff line number Diff line change
Expand Up @@ -346,3 +346,23 @@ def test_empty_table(cql, test_keyspace, lowered_tombstone_limit, driver_bug_1):
assert list(cql.execute(f"SELECT * FROM {table}")) == []
assert list(cql.execute(f"SELECT * FROM {table} WHERE pk = 0")) == []
assert list(cql.execute(f"SELECT * FROM {table} WHERE v = 0 ALLOW FILTERING")) == []


# Unpaged query should be not affected
def test_unpaged_query(cql, table, lowered_tombstone_limit, driver_bug_1):
# Use update to avoid creating a row-marker ...
upsert_row_id = cql.prepare(f"UPDATE {table} SET v = ? WHERE pk = ? AND ck = ?")
# ... so deleting the only live cell in the row makes it empty.
delete_row_id = cql.prepare(f"DELETE v FROM {table} WHERE pk = ? AND ck = ?")

pk = unique_key_int()

for ck in range(0, 20):
cql.execute(upsert_row_id, (0, pk, ck))

for ck in range(0, 16):
cql.execute(delete_row_id, (pk, ck))

statement = SimpleStatement(f"SELECT * FROM {table} WHERE pk = {pk}", fetch_size=None)
rows = list(cql.execute(statement))
assert len(rows) == 4

0 comments on commit 3512592

Please sign in to comment.