diff --git a/service/storage_proxy.cc b/service/storage_proxy.cc index 9d495fcecb..773cdf64cf 100644 --- a/service/storage_proxy.cc +++ b/service/storage_proxy.cc @@ -6153,6 +6153,12 @@ future storage_proxy::cas(schema_ptr schema, shared_ptr reque db::consistency_level cl_for_paxos, db::consistency_level cl_for_learn, clock_type::time_point write_timeout, clock_type::time_point cas_timeout, bool write) { + auto& table = local_db().find_column_family(schema->id()); + if (table.uses_tablets()) { + auto msg = format("Cannot use LightWeight Transactions for table {}.{}: LWT is not yet supported with tablets", schema->ks_name(), schema->cf_name()); + co_await coroutine::return_exception(exceptions::invalid_request_exception(msg)); + } + assert(partition_ranges.size() == 1); assert(query::is_single_partition(partition_ranges[0])); diff --git a/test/cql-pytest/cassandra_tests/validation/entities/secondary_index_test.py b/test/cql-pytest/cassandra_tests/validation/entities/secondary_index_test.py index 86f9ed54fe..2fa809555b 100644 --- a/test/cql-pytest/cassandra_tests/validation/entities/secondary_index_test.py +++ b/test/cql-pytest/cassandra_tests/validation/entities/secondary_index_test.py @@ -93,8 +93,9 @@ def dotestCreateAndDropIndex(cql, table, indexName, addKeyspaceOnDrop): f"DROP INDEX {KEYSPACE}.{indexName}") @pytest.fixture(scope="module") -def table1(cql, test_keyspace): - with create_table(cql, test_keyspace, "(a int primary key, b int)") as table: +# FIXME: LWT is not supported with tablets yet. See #18066 +def table1(cql, test_keyspace_vnodes): + with create_table(cql, test_keyspace_vnodes, "(a int primary key, b int)") as table: yield table # Reproduces #8717 (CREATE INDEX IF NOT EXISTS was broken): @@ -449,6 +450,9 @@ TOO_BIG = 1024 * 65 # both singly and in batches (CASSANDRA-10536) # Reproduces #8627 @pytest.mark.xfail(reason="issue #8627") +@pytest.mark.parametrize("test_keyspace", + [pytest.param("tablets", marks=[pytest.mark.xfail(reason="issue #18066")]), "vnodes"], + indirect=True) def testIndexOnCompositeValueOver64k(cql, test_keyspace): too_big = bytearray([1])*TOO_BIG with create_table(cql, test_keyspace, "(a int, b int, c blob, PRIMARY KEY (a))") as table: @@ -468,6 +472,9 @@ def testIndexOnCompositeValueOver64k(cql, test_keyspace): "APPLY BATCH", too_big) +@pytest.mark.parametrize("test_keyspace", + [pytest.param("tablets", marks=[pytest.mark.xfail(reason="issue #18066")]), "vnodes"], + indirect=True) def testIndexOnPartitionKeyInsertValueOver64k(cql, test_keyspace): too_big = bytearray([1])*TOO_BIG with create_table(cql, test_keyspace, "(a int, b int, c blob, PRIMARY KEY ((a, b)))") as table: @@ -522,6 +529,9 @@ def testIndexOnPartitionKeyWithStaticColumnAndNoRows(cql, test_keyspace): execute(cql, table, "UPDATE %s SET s=? WHERE pk1=? AND pk2=?", 9, 1, 20) assert_rows(execute(cql, table, "SELECT * FROM %s WHERE pk2 = ?", 20), [1, 20, None, 9, None]) +@pytest.mark.parametrize("test_keyspace", + [pytest.param("tablets", marks=[pytest.mark.xfail(reason="issue #18066")]), "vnodes"], + indirect=True) def testIndexOnClusteringColumnInsertValueOver64k(cql, test_keyspace): too_big = bytearray([1])*TOO_BIG with create_table(cql, test_keyspace, "(a int, b int, c blob, PRIMARY KEY ((a, b)))") as table: @@ -554,6 +564,9 @@ def testIndexOnClusteringColumnInsertValueOver64k(cql, test_keyspace): # Reproduces #8627 @pytest.mark.xfail(reason="issue #8627") +@pytest.mark.parametrize("test_keyspace", + [pytest.param("tablets", marks=[pytest.mark.xfail(reason="issue #18066")]), "vnodes"], + indirect=True) def testIndexOnFullCollectionEntryInsertCollectionValueOver64k(cql, test_keyspace): too_big = bytearray([1])*TOO_BIG map = {0: too_big} diff --git a/test/cql-pytest/cassandra_tests/validation/entities/timestamp_test.py b/test/cql-pytest/cassandra_tests/validation/entities/timestamp_test.py index 6dfe57e639..36458f1713 100644 --- a/test/cql-pytest/cassandra_tests/validation/entities/timestamp_test.py +++ b/test/cql-pytest/cassandra_tests/validation/entities/timestamp_test.py @@ -58,6 +58,9 @@ def testTimestampTTL(cql, test_keyspace): [1, None, None]) # Migrated from cql_tests.py:TestCQL.invalid_custom_timestamp_test() +@pytest.mark.parametrize("test_keyspace", + [pytest.param("tablets", marks=[pytest.mark.xfail(reason="issue #18066")]), "vnodes"], + indirect=True) def testInvalidCustomTimestamp(cql, test_keyspace): # Conditional updates with create_table(cql, test_keyspace, "(k int, v int, PRIMARY KEY (k, v))") as table: diff --git a/test/cql-pytest/cassandra_tests/validation/operations/compact_storage_test.py b/test/cql-pytest/cassandra_tests/validation/operations/compact_storage_test.py index e0f3fe062d..9b962e2513 100644 --- a/test/cql-pytest/cassandra_tests/validation/operations/compact_storage_test.py +++ b/test/cql-pytest/cassandra_tests/validation/operations/compact_storage_test.py @@ -1233,6 +1233,9 @@ def testInsertWithCompactStorageAndTwoClusteringColumns(cql, test_keyspace, forc # InsertUpdateIfConditionTest # Test for CAS with compact storage table, and CASSANDRA-6813 in particular, # migrated from cql_tests.py:TestCQL.cas_and_compact_test() +@pytest.mark.parametrize("test_keyspace", + [pytest.param("tablets", marks=[pytest.mark.xfail(reason="issue #18066")]), "vnodes"], + indirect=True) def testCompactStorage(cql, test_keyspace): with create_table(cql, test_keyspace, "(partition text, key text, owner text, PRIMARY KEY (partition, key)) WITH COMPACT STORAGE") as table: execute(cql, table, "INSERT INTO %s (partition, key, owner) VALUES ('a', 'b', null)") diff --git a/test/cql-pytest/cassandra_tests/validation/operations/insert_update_if_condition_collections_test.py b/test/cql-pytest/cassandra_tests/validation/operations/insert_update_if_condition_collections_test.py index c0a67c0abc..f64342e865 100644 --- a/test/cql-pytest/cassandra_tests/validation/operations/insert_update_if_condition_collections_test.py +++ b/test/cql-pytest/cassandra_tests/validation/operations/insert_update_if_condition_collections_test.py @@ -25,6 +25,9 @@ def is_scylla(cql): names = [row.table_name for row in cql.execute("SELECT * FROM system_schema.tables WHERE keyspace_name = 'system'")] yield any('scylla' in name for name in names) +@pytest.mark.parametrize("test_keyspace", + [pytest.param("tablets", marks=[pytest.mark.xfail(reason="issue #18066")]), "vnodes"], + indirect=True) def testInsertSetIfNotExists(cql, test_keyspace, is_scylla): with create_table(cql, test_keyspace, "(k int PRIMARY KEY, s set)") as table: assertRows(execute(cql, table, "INSERT INTO %s (k, s) VALUES (0, {1, 2, 3}) IF NOT EXISTS"), @@ -472,6 +475,9 @@ def check_invalid_list(cql, table, condition, expected): assertRows(execute(cql, table, "SELECT * FROM %s"), row(0, ["foo", "bar", "foobar"])) # Migrated from cql_tests.py:TestCQL.list_item_conditional_test() +@pytest.mark.parametrize("test_keyspace", + [pytest.param("tablets", marks=[pytest.mark.xfail(reason="issue #18066")]), "vnodes"], + indirect=True) def testListItem(cql, test_keyspace): for frozen in [False, True]: typename = "list" @@ -496,6 +502,9 @@ def testListItem(cql, test_keyspace): # Test expanded functionality from CASSANDRA-6839, # migrated from cql_tests.py:TestCQL.expanded_list_item_conditional_test() +@pytest.mark.parametrize("test_keyspace", + [pytest.param("tablets", marks=[pytest.mark.xfail(reason="issue #18066")]), "vnodes"], + indirect=True) def testExpandedListItem(cql, test_keyspace): for frozen in [False, True]: typename = "list" @@ -670,6 +679,9 @@ def testWholeMap(cql, test_keyspace): check_invalid_map(cql, table, "m IN null", SyntaxException) # Migrated from cql_tests.py:TestCQL.map_item_conditional_test() +@pytest.mark.parametrize("test_keyspace", + [pytest.param("tablets", marks=[pytest.mark.xfail(reason="issue #18066")]), "vnodes"], + indirect=True) def testMapItem(cql, test_keyspace): for frozen in [False, True]: typename = "map" @@ -696,6 +708,9 @@ def testMapItem(cql, test_keyspace): else: assert list(execute(cql, table, "UPDATE %s set m['foo'] = 'bar', m['bar'] = 'foo' WHERE k = 1 IF m[?] IN (?, ?)", "foo", "blah", None))[0][0] == True +@pytest.mark.parametrize("test_keyspace", + [pytest.param("tablets", marks=[pytest.mark.xfail(reason="issue #18066")]), "vnodes"], + indirect=True) def testFrozenWithNullValues(cql, test_keyspace): with create_table(cql, test_keyspace, f"(k int PRIMARY KEY, m frozen>)") as table: execute(cql, table, "INSERT INTO %s (k, m) VALUES (0, null)") @@ -714,6 +729,9 @@ def testFrozenWithNullValues(cql, test_keyspace): # Test expanded functionality from CASSANDRA-6839, # migrated from cql_tests.py:TestCQL.expanded_map_item_conditional_test() +@pytest.mark.parametrize("test_keyspace", + [pytest.param("tablets", marks=[pytest.mark.xfail(reason="issue #18066")]), "vnodes"], + indirect=True) def testExpandedMapItem(cql, test_keyspace): for frozen in [False, True]: typename = "map" diff --git a/test/cql-pytest/cassandra_tests/validation/operations/insert_update_if_condition_statics_test.py b/test/cql-pytest/cassandra_tests/validation/operations/insert_update_if_condition_statics_test.py index c7599a5294..0ce19f32fe 100644 --- a/test/cql-pytest/cassandra_tests/validation/operations/insert_update_if_condition_statics_test.py +++ b/test/cql-pytest/cassandra_tests/validation/operations/insert_update_if_condition_statics_test.py @@ -28,6 +28,9 @@ def is_scylla(cql): yield any('scylla' in name for name in names) # Migrated from cql_tests.py:TestCQL.static_columns_cas_test() +@pytest.mark.parametrize("test_keyspace", + [pytest.param("tablets", marks=[pytest.mark.xfail(reason="issue #18066")]), "vnodes"], + indirect=True) def testStaticColumnsCas(cql, test_keyspace, is_scylla): with create_table(cql, test_keyspace, "(id int, k text, version int static, v text, PRIMARY KEY (id, k))") as table: # Test that INSERT IF NOT EXISTS concerns only the static column if no clustering nor regular columns @@ -146,6 +149,9 @@ def testStaticColumnsCas(cql, test_keyspace, is_scylla): [row(False,1,"k2","newVal"),row(False,1,"k2","newVal")] if is_scylla else [row(False, 1, "k2", "newVal")]) # Test CASSANDRA-10532 +@pytest.mark.parametrize("test_keyspace", + [pytest.param("tablets", marks=[pytest.mark.xfail(reason="issue #18066")]), "vnodes"], + indirect=True) def testStaticColumnsCasDelete(cql, test_keyspace, is_scylla): with create_table(cql, test_keyspace, "(pk int, ck int, static_col int static, value int, PRIMARY KEY (pk, ck))") as table: execute(cql, table, "INSERT INTO %s (pk, ck, value) VALUES (?, ?, ?)", 1, 1, 2) @@ -206,6 +212,9 @@ def testStaticColumnsCasDelete(cql, test_keyspace, is_scylla): row(1, 5, null, 6), row(1, 7, null, 8)) +@pytest.mark.parametrize("test_keyspace", + [pytest.param("tablets", marks=[pytest.mark.xfail(reason="issue #18066")]), "vnodes"], + indirect=True) def testStaticColumnsCasUpdate(cql, test_keyspace, is_scylla): with create_table(cql, test_keyspace, "(pk int, ck int, static_col int static, value int, PRIMARY KEY (pk, ck))") as table: execute(cql, table, "INSERT INTO %s (pk, ck, value) VALUES (?, ?, ?)", 1, 1, 2) @@ -258,6 +267,9 @@ def testStaticColumnsCasUpdate(cql, test_keyspace, is_scylla): row(1, 5, 1, 6), row(1, 7, 1, 8)) +@pytest.mark.parametrize("test_keyspace", + [pytest.param("tablets", marks=[pytest.mark.xfail(reason="issue #18066")]), "vnodes"], + indirect=True) def testConditionalUpdatesOnStaticColumns(cql, test_keyspace, is_scylla): with create_table(cql, test_keyspace, "(a int, b int, s int static, d text, PRIMARY KEY (a, b))") as table: assertInvalidMessage(cql, table, "unset", "UPDATE %s SET s = 6 WHERE a = 6 IF s = ?", unset()) @@ -289,6 +301,9 @@ def testConditionalUpdatesOnStaticColumns(cql, test_keyspace, is_scylla): assertRows(execute(cql, table, "SELECT * FROM %s WHERE a = 8"), row(8, null, 8, null)) +@pytest.mark.parametrize("test_keyspace", + [pytest.param("tablets", marks=[pytest.mark.xfail(reason="issue #18066")]), "vnodes"], + indirect=True) def testStaticsWithMultipleConditions(cql, test_keyspace, is_scylla): with create_table(cql, test_keyspace, "(a int, b int, s1 int static, s2 int static, d int, PRIMARY KEY (a, b))") as table: for i in range(1,6): @@ -324,6 +339,9 @@ def testStaticsWithMultipleConditions(cql, test_keyspace, is_scylla): + "APPLY BATCH"), [row(false,None,None,None,None,None),row(false,None,None,None,None,None),row(false,None,None,None,None,None),row(false,None,None,None,None,None)] if is_scylla else [row(false)]) +@pytest.mark.parametrize("test_keyspace", + [pytest.param("tablets", marks=[pytest.mark.xfail(reason="issue #18066")]), "vnodes"], + indirect=True) def testStaticColumnsCasUpdateWithNullStaticColumn(cql, test_keyspace, is_scylla): with create_table(cql, test_keyspace, "(pk int, ck int, s1 int static, s2 int static, value int, PRIMARY KEY (pk, ck))") as table: execute(cql, table, "INSERT INTO %s (pk, s1, s2) VALUES (1, 1, 1) USING TIMESTAMP 1000") @@ -341,6 +359,9 @@ def testStaticColumnsCasUpdateWithNullStaticColumn(cql, test_keyspace, is_scylla assertRows(execute(cql, table, "UPDATE %s SET s1 = ? WHERE pk = ? IF EXISTS", 2, 2), row(true,2,null,null,1,null) if is_scylla else row(true)) assertRows(execute(cql, table, "SELECT * FROM %s WHERE pk = ?", 2), row(2, null, 2, 1, null)) +@pytest.mark.parametrize("test_keyspace", + [pytest.param("tablets", marks=[pytest.mark.xfail(reason="issue #18066")]), "vnodes"], + indirect=True) def testStaticColumnsCasDeleteWithNullStaticColumn(cql, test_keyspace, is_scylla): with create_table(cql, test_keyspace, "(pk int, ck int, s1 int static, s2 int static, value int, PRIMARY KEY (pk, ck))") as table: execute(cql, table, "INSERT INTO %s (pk, s1, s2) VALUES (1, 1, 1) USING TIMESTAMP 1000") diff --git a/test/cql-pytest/test_lwt.py b/test/cql-pytest/test_lwt.py index dac55784e4..724e6fbcc3 100644 --- a/test/cql-pytest/test_lwt.py +++ b/test/cql-pytest/test_lwt.py @@ -15,9 +15,10 @@ from cassandra.protocol import InvalidRequest from util import new_test_table, unique_key_int @pytest.fixture(scope="module") -def table1(cql, test_keyspace): +# FIXME: LWT is not supported with tablets yet. See #18066 +def table1(cql, test_keyspace_vnodes): schema='p int, c int, r int, s int static, PRIMARY KEY(p, c)' - with new_test_table(cql, test_keyspace, schema) as table: + with new_test_table(cql, test_keyspace_vnodes, schema) as table: yield table # An LWT UPDATE whose condition uses non-static columns begins by reading diff --git a/test/cql-pytest/test_non_deterministic_functions.py b/test/cql-pytest/test_non_deterministic_functions.py index 62e8e1e11e..611f0d8c29 100644 --- a/test/cql-pytest/test_non_deterministic_functions.py +++ b/test/cql-pytest/test_non_deterministic_functions.py @@ -11,6 +11,8 @@ # same value for the execution on another shard). ############################################################################# +import pytest + from time import sleep from util import new_test_table @@ -44,17 +46,32 @@ def lwt_nondeterm_fn_repeated_execute(cql, test_keyspace, pk_type, fn): rows = list(cql.execute(select_str)) assert len(rows) == num_iterations * 2 +@pytest.mark.parametrize("test_keyspace", + [pytest.param("tablets", marks=[pytest.mark.xfail(reason="issue #18066")]), "vnodes"], + indirect=True) def test_lwt_uuid_fn_pk_insert(cql, test_keyspace): lwt_nondeterm_fn_repeated_execute(cql, test_keyspace, "uuid", "uuid") +@pytest.mark.parametrize("test_keyspace", + [pytest.param("tablets", marks=[pytest.mark.xfail(reason="issue #18066")]), "vnodes"], + indirect=True) def test_lwt_currenttimestamp_fn_pk_insert(cql, test_keyspace): lwt_nondeterm_fn_repeated_execute(cql, test_keyspace, "timestamp", "currenttimestamp") +@pytest.mark.parametrize("test_keyspace", + [pytest.param("tablets", marks=[pytest.mark.xfail(reason="issue #18066")]), "vnodes"], + indirect=True) def test_lwt_currenttime_fn_pk_insert(cql, test_keyspace): lwt_nondeterm_fn_repeated_execute(cql, test_keyspace, "time", "currenttime") +@pytest.mark.parametrize("test_keyspace", + [pytest.param("tablets", marks=[pytest.mark.xfail(reason="issue #18066")]), "vnodes"], + indirect=True) def test_lwt_currenttimeuuid_fn_pk_insert(cql, test_keyspace): lwt_nondeterm_fn_repeated_execute(cql, test_keyspace, "timeuuid", "currenttimeuuid") +@pytest.mark.parametrize("test_keyspace", + [pytest.param("tablets", marks=[pytest.mark.xfail(reason="issue #18066")]), "vnodes"], + indirect=True) def test_lwt_now_fn_pk_insert(cql, test_keyspace): lwt_nondeterm_fn_repeated_execute(cql, test_keyspace, "timeuuid", "now") diff --git a/test/cql-pytest/test_tablets.py b/test/cql-pytest/test_tablets.py index 88a6aa2bae..5ee80f68e4 100644 --- a/test/cql-pytest/test_tablets.py +++ b/test/cql-pytest/test_tablets.py @@ -201,3 +201,21 @@ def test_tablets_are_dropped_when_dropping_index(cql, test_keyspace, drop_index) except: pass raise e + + +# FIXME: LWT is not supported with tablets yet. See #18066 +# Until the issue is fixed, test that a LWT query indeed fails as expected +def test_lwt_support_with_tablets(cql, test_keyspace, skip_without_tablets): + with new_test_table(cql, test_keyspace, "key int PRIMARY KEY, val int") as table: + cql.execute(f"INSERT INTO {table} (key, val) VALUES(1, 0)") + with pytest.raises(InvalidRequest, match=f"{table}.*LWT is not yet supported with tablets"): + cql.execute(f"INSERT INTO {table} (key, val) VALUES(1, 1) IF NOT EXISTS") + # The query is rejected during the execution phase, + # so preparing the LWT query is expected to succeed. + stmt = cql.prepare(f"UPDATE {table} SET val = 1 WHERE KEY = ? IF EXISTS") + with pytest.raises(InvalidRequest, match=f"{table}.*LWT is not yet supported with tablets"): + cql.execute(stmt, [1]) + with pytest.raises(InvalidRequest, match=f"{table}.*LWT is not yet supported with tablets"): + cql.execute(f"DELETE FROM {table} WHERE key = 1 IF EXISTS") + res = cql.execute(f"SELECT val FROM {table} WHERE key = 1").one() + assert res.val == 0 diff --git a/test/cql-pytest/test_unset.py b/test/cql-pytest/test_unset.py index c7a1da570d..70dd2f3271 100644 --- a/test/cql-pytest/test_unset.py +++ b/test/cql-pytest/test_unset.py @@ -201,7 +201,13 @@ def test_unset_insert_where(cql, table2): # Similar to test_unset_insert_where() above, just use an LWT write ("IF # NOT EXISTS"). Test that using an UNSET_VALUE in an LWT condition causes # a clear error, not silent skip and not a crash as in issue #13001. -def test_unset_insert_where_lwt(cql, table2): +@pytest.mark.parametrize("test_keyspace", + [pytest.param("tablets", marks=[pytest.mark.xfail(reason="issue #18066")]), "vnodes"], + indirect=True) +def test_unset_insert_where_lwt(cql, test_keyspace): + # FIXME: new_test_table is used here due to https://github.com/scylladb/scylladb/issues/18066 + # When fixed, this test can go back to using the `table2` fixture. + with new_test_table(cql, test_keyspace, "p int, c int, PRIMARY KEY (p, c)") as table2: p = unique_key_int() stmt = cql.prepare(f'INSERT INTO {table2} (p, c) VALUES ({p}, ?) IF NOT EXISTS') with pytest.raises(InvalidRequest, match="unset"): @@ -219,7 +225,13 @@ def test_unset_update_where(cql, table3): # Like test_unset_insert_where_lwt, but using UPDATE # Python driver doesn't allow sending an UNSET_VALUE for the partition key, # so only the clustering key is tested. -def test_unset_update_where_lwt(cql, table3): +@pytest.mark.parametrize("test_keyspace", + [pytest.param("tablets", marks=[pytest.mark.xfail(reason="issue #18066")]), "vnodes"], + indirect=True) +def test_unset_update_where_lwt(cql, test_keyspace): + # FIXME: new_test_table is used here due to https://github.com/scylladb/scylladb/issues/18066 + # When fixed, this test can go back to using the `table3` fixture. + with new_test_table(cql, test_keyspace, "p int, c int, r int, PRIMARY KEY (p, c)") as table3: stmt = cql.prepare(f"UPDATE {table3} SET r = 42 WHERE p = 0 AND c = ? IF r = ?") with pytest.raises(InvalidRequest, match="unset"):