query: do not kill unpaged queries when they reach the tombstone-limit

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: #17241

Closes scylladb/scylladb#17242
This commit is contained in:
Botond Dénes
2024-02-09 04:05:26 -05:00
committed by Avi Kivity
parent 9b85d1aebf
commit f068d1a6fa
2 changed files with 23 additions and 3 deletions

View File

@@ -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;
}

View File

@@ -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