From f068d1a6fac3bfde2dc9782e7693382cda880a47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Botond=20D=C3=A9nes?= Date: Fri, 9 Feb 2024 04:05:26 -0500 Subject: [PATCH] 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 --- query-result-writer.hh | 6 +++--- test/cql-pytest/test_tombstone_limit.py | 20 ++++++++++++++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/query-result-writer.hh b/query-result-writer.hh index 6b9406c333..bb392e570e 100644 --- a/query-result-writer.hh +++ b/query-result-writer.hh @@ -136,9 +136,9 @@ public: return stop_iteration::no; } if (!_slice.options.contains()) { - 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; } diff --git a/test/cql-pytest/test_tombstone_limit.py b/test/cql-pytest/test_tombstone_limit.py index 87e0f02f96..5b57c41338 100644 --- a/test/cql-pytest/test_tombstone_limit.py +++ b/test/cql-pytest/test_tombstone_limit.py @@ -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