Skip to content

Commit ab4fece

Browse files
test: add tests for prepared statement metadata consistency corner cases
Implement corner-cases of prepared statement metadata, as described in scylladb#20860. Although the purpose of the test was to verify the newly implemented SCYLLA_USE_METADATA_ID protocol extension, the test also passes with scylla-driver 3.29.3 that doesn't implement the support for this extension. That is because the driver doesn't implement support for skip_metadata flag, so fresh metadata are included in every prepared statement response, regardless of the metadata_id. This change: - Add test_changed_prepared_statement_metadata_columns to verify a scenario when a number of columns changes in a table used by a prepared statement - Add test_changed_prepared_statement_metadata_types to verify a scenario when a type of a column changes in a table used by a prepared statement - Add test_changed_prepared_statement_metadata_udt to veriy a scenario when a UDT changes in a table used by a prepared statement I tested the code with a modified Python driver (ref. scylladb/python-driver#457): - If SKIP_METADATA is enabled (scylladb/python-driver@c1809c1) but not other changes are introduced, all three test cases fail. - If SKIP_METADATA is disabled (no scylladb/python-driver@c1809c1) all test cases pass because fresh metadata are included in each reply. - If SKIP_METADATA is enabled (scylladb/python-driver@c1809c1) and SCYLLA_USE_METADATA_ID extension is included (scylladb/python-driver@8aba164) all test cases pass and verifies the correctness the implementation.
1 parent a16c652 commit ab4fece

File tree

1 file changed

+87
-0
lines changed

1 file changed

+87
-0
lines changed

test/cluster/test_metadata_id.py

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
#
2+
# Copyright (C) 2025-present ScyllaDB
3+
#
4+
# SPDX-License-Identifier: LicenseRef-ScyllaDB-Source-Available-1.0
5+
#
6+
7+
import logging
8+
import pytest
9+
10+
from test.cluster.util import new_test_keyspace, new_test_table
11+
12+
from test.cluster.conftest import cluster_con
13+
from cassandra.policies import WhiteListRoundRobinPolicy
14+
15+
logger = logging.getLogger(__name__)
16+
17+
async def create_server_and_cqls(manager, cqls_num):
18+
server = await manager.server_add()
19+
20+
def create_cluster_connection():
21+
return cluster_con([server.ip_addr], 9042, False,
22+
load_balancing_policy=WhiteListRoundRobinPolicy([server.ip_addr])).connect()
23+
24+
return tuple(create_cluster_connection() for _ in range(cqls_num))
25+
26+
@pytest.mark.asyncio
27+
async def test_changed_prepared_statement_metadata_columns(manager):
28+
cql1, cql2, cql3 = await create_server_and_cqls(manager, cqls_num=3)
29+
30+
async with new_test_keyspace(manager, "WITH replication = {'class': 'NetworkTopologyStrategy', 'replication_factor': 1};") as ks:
31+
async with new_test_table(manager, ks, "pk BIGINT PRIMARY KEY, c INT") as table:
32+
cql1.execute(f"INSERT INTO {table} (pk, c) VALUES (0, 0)")
33+
34+
prepared = cql1.prepare(f"SELECT * FROM {table}")
35+
assert len(cql1.execute(prepared).one()) == 2
36+
37+
prepared2 = cql2.prepare(f"SELECT * FROM {table}")
38+
assert len(cql2.execute(prepared2).one()) == 2
39+
40+
cql3.execute(f"ALTER TABLE {table} ADD c2 INT")
41+
cql3.execute(f"INSERT INTO {table} (pk, c, c2) VALUES (1, 1, 1)")
42+
43+
assert len(cql1.execute(prepared).one()) == 3
44+
assert len(cql2.execute(prepared2).one()) == 3
45+
46+
@pytest.mark.asyncio
47+
async def test_changed_prepared_statement_metadata_types(manager):
48+
cql1, cql2, cql3 = await create_server_and_cqls(manager, cqls_num=3)
49+
50+
async with new_test_keyspace(manager, "WITH replication = {'class': 'NetworkTopologyStrategy', 'replication_factor': 1};") as ks:
51+
async with new_test_table(manager, ks, "pk BIGINT PRIMARY KEY, c INT") as table:
52+
await cql1.run_async(f"INSERT INTO {table} (pk, c) VALUES (0, 0)")
53+
54+
prepared = cql1.prepare(f"SELECT * FROM {table}")
55+
cql1.execute(prepared)
56+
prepared2 = cql2.prepare(f"SELECT * FROM {table}")
57+
cql2.execute(prepared2)
58+
59+
cql3.execute(f"ALTER TABLE {table} DROP c")
60+
cql3.execute(f"ALTER TABLE {table} ADD c TEXT")
61+
cql3.execute(f"INSERT INTO {table} (pk, c) VALUES (1, 'abc')")
62+
63+
assert len(cql1.execute(prepared).one()) == 2
64+
assert len(cql2.execute(prepared2).one()) == 2
65+
66+
@pytest.mark.asyncio
67+
async def test_changed_prepared_statement_metadata_udt(manager):
68+
cql1, cql2, cql3 = await create_server_and_cqls(manager, cqls_num=3)
69+
70+
async with new_test_keyspace(manager, "WITH replication = {'class': 'NetworkTopologyStrategy', 'replication_factor': 1};") as ks:
71+
cql1.execute(f"CREATE TYPE {ks}.mytype (i1 int, i2 int)")
72+
async with new_test_table(manager, ks, "pk BIGINT PRIMARY KEY, c mytype") as table:
73+
74+
mytypeval = "{i1: 1, i2: 2}"
75+
cql1.execute(f"INSERT INTO {table} (pk, c) VALUES (0, {mytypeval})")
76+
77+
prepared = cql1.prepare(f"SELECT * FROM {table}")
78+
cql1.execute(prepared)
79+
prepared2 = cql2.prepare(f"SELECT * FROM {table}")
80+
cql2.execute(prepared2)
81+
82+
cql3.execute(f"ALTER TYPE {ks}.mytype ADD i3 INT")
83+
mytypeval = "{i1: 1, i2: 2, i3: 3}"
84+
cql3.execute(f"INSERT INTO {table} (pk, c) VALUES (1, {mytypeval})")
85+
86+
assert "i3" in str(cql1.execute(prepared).one())
87+
assert "i3" in str(cql2.execute(prepared2).one())

0 commit comments

Comments
 (0)