Skip to content

Commit 9442e0c

Browse files
bigsheeperclaude
andcommitted
test(cdc): add collection properties, resource group, multi-db, search verification, and switchover tests
- test_collection_properties: TTL, mmap, autocompaction, multi-property, drop property - test_resource_group: create, drop, update, transfer replica - test_multi_database: cross-DB operations, drop DB - test_search_verification: 7 vector types × search/query/iterator/hybrid/filtered - test_switchover: basic, during-writes, all-types, loaded, indexed, rapid stress, failover Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Yihao Dai <yihao.dai@zilliz.com>
1 parent 0f5fe31 commit 9442e0c

File tree

5 files changed

+2218
-0
lines changed

5 files changed

+2218
-0
lines changed
Lines changed: 281 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,281 @@
1+
"""
2+
CDC sync tests for collection property operations.
3+
"""
4+
5+
import time
6+
from .base import TestCDCSyncBase, logger
7+
8+
9+
class TestCDCSyncCollectionProperties(TestCDCSyncBase):
10+
"""Test CDC sync for collection property (alter/drop) operations."""
11+
12+
def setup_method(self):
13+
"""Setup for each test method."""
14+
self.resources_to_cleanup = []
15+
16+
def teardown_method(self):
17+
"""Cleanup after each test method - only cleanup upstream, downstream will sync."""
18+
upstream_client = getattr(self, "_upstream_client", None)
19+
20+
if upstream_client:
21+
for resource_type, resource_name in self.resources_to_cleanup:
22+
if resource_type == "collection":
23+
self.cleanup_collection(upstream_client, resource_name)
24+
25+
time.sleep(1) # Allow cleanup to sync to downstream
26+
27+
def _create_basic_collection(self, client, c_name):
28+
"""Create a default schema collection, insert 100 rows, and create HNSW index."""
29+
schema = self.create_default_schema(client)
30+
client.create_collection(
31+
collection_name=c_name,
32+
schema=schema,
33+
consistency_level="Strong",
34+
)
35+
36+
# Insert 100 rows
37+
test_data = self.generate_test_data(100)
38+
client.insert(c_name, test_data)
39+
40+
# Create HNSW index on vector field
41+
index_params = client.prepare_index_params()
42+
index_params.add_index(
43+
field_name="vector",
44+
index_type="HNSW",
45+
metric_type="L2",
46+
params={"M": 8, "efConstruction": 64},
47+
)
48+
client.create_index(c_name, index_params)
49+
50+
def test_ttl_sync(self, upstream_client, downstream_client, sync_timeout):
51+
"""Test ALTER_COLLECTION_PROPERTIES (TTL) sync."""
52+
# Store upstream client for teardown
53+
self._upstream_client = upstream_client
54+
55+
c_name = self.gen_unique_name("test_col_ttl")
56+
self.resources_to_cleanup.append(("collection", c_name))
57+
58+
# Initial cleanup
59+
self.cleanup_collection(upstream_client, c_name)
60+
61+
# Create collection
62+
self._create_basic_collection(upstream_client, c_name)
63+
64+
# Wait for collection to sync downstream
65+
def check_create():
66+
return downstream_client.has_collection(c_name)
67+
68+
assert self.wait_for_sync(check_create, sync_timeout, f"create collection {c_name}")
69+
70+
# Alter collection TTL property
71+
upstream_client.alter_collection_properties(
72+
collection_name=c_name,
73+
properties={"collection.ttl.seconds": "3600"},
74+
)
75+
76+
# Wait for property to sync downstream
77+
def check_ttl():
78+
try:
79+
desc = downstream_client.describe_collection(c_name)
80+
props = desc.get("properties", {})
81+
logger.info(f"Downstream collection properties: {props}")
82+
return str(props.get("collection.ttl.seconds", "")) == "3600"
83+
except Exception as e:
84+
logger.warning(f"Check TTL sync failed: {e}")
85+
return False
86+
87+
assert self.wait_for_sync(check_ttl, sync_timeout, f"TTL property sync for {c_name}")
88+
89+
def test_mmap_sync(self, upstream_client, downstream_client, sync_timeout):
90+
"""Test ALTER_COLLECTION_PROPERTIES (mmap.enabled) sync."""
91+
# Store upstream client for teardown
92+
self._upstream_client = upstream_client
93+
94+
c_name = self.gen_unique_name("test_col_mmap")
95+
self.resources_to_cleanup.append(("collection", c_name))
96+
97+
# Initial cleanup
98+
self.cleanup_collection(upstream_client, c_name)
99+
100+
# Create collection
101+
self._create_basic_collection(upstream_client, c_name)
102+
103+
# Wait for collection to sync downstream
104+
def check_create():
105+
return downstream_client.has_collection(c_name)
106+
107+
assert self.wait_for_sync(check_create, sync_timeout, f"create collection {c_name}")
108+
109+
# Alter mmap.enabled property
110+
upstream_client.alter_collection_properties(
111+
collection_name=c_name,
112+
properties={"mmap.enabled": "true"},
113+
)
114+
115+
# Wait for property to sync downstream
116+
def check_mmap():
117+
try:
118+
desc = downstream_client.describe_collection(c_name)
119+
props = desc.get("properties", {})
120+
logger.info(f"Downstream collection properties: {props}")
121+
return str(props.get("mmap.enabled", "")).lower() == "true"
122+
except Exception as e:
123+
logger.warning(f"Check mmap sync failed: {e}")
124+
return False
125+
126+
assert self.wait_for_sync(check_mmap, sync_timeout, f"mmap property sync for {c_name}")
127+
128+
def test_autocompaction_sync(self, upstream_client, downstream_client, sync_timeout):
129+
"""Test ALTER_COLLECTION_PROPERTIES (autocompaction.enabled) sync."""
130+
# Store upstream client for teardown
131+
self._upstream_client = upstream_client
132+
133+
c_name = self.gen_unique_name("test_col_autocomp")
134+
self.resources_to_cleanup.append(("collection", c_name))
135+
136+
# Initial cleanup
137+
self.cleanup_collection(upstream_client, c_name)
138+
139+
# Create collection
140+
self._create_basic_collection(upstream_client, c_name)
141+
142+
# Wait for collection to sync downstream
143+
def check_create():
144+
return downstream_client.has_collection(c_name)
145+
146+
assert self.wait_for_sync(check_create, sync_timeout, f"create collection {c_name}")
147+
148+
# Alter autocompaction property
149+
upstream_client.alter_collection_properties(
150+
collection_name=c_name,
151+
properties={"collection.autocompaction.enabled": "true"},
152+
)
153+
154+
# Wait for property to sync downstream
155+
def check_autocomp():
156+
try:
157+
desc = downstream_client.describe_collection(c_name)
158+
props = desc.get("properties", {})
159+
logger.info(f"Downstream collection properties: {props}")
160+
return str(props.get("collection.autocompaction.enabled", "")).lower() == "true"
161+
except Exception as e:
162+
logger.warning(f"Check autocompaction sync failed: {e}")
163+
return False
164+
165+
assert self.wait_for_sync(
166+
check_autocomp, sync_timeout, f"autocompaction property sync for {c_name}"
167+
)
168+
169+
def test_alter_multiple_properties(self, upstream_client, downstream_client, sync_timeout):
170+
"""Test ALTER_COLLECTION_PROPERTIES with multiple properties at once sync."""
171+
# Store upstream client for teardown
172+
self._upstream_client = upstream_client
173+
174+
c_name = self.gen_unique_name("test_col_multi_props")
175+
self.resources_to_cleanup.append(("collection", c_name))
176+
177+
# Initial cleanup
178+
self.cleanup_collection(upstream_client, c_name)
179+
180+
# Create collection
181+
self._create_basic_collection(upstream_client, c_name)
182+
183+
# Wait for collection to sync downstream
184+
def check_create():
185+
return downstream_client.has_collection(c_name)
186+
187+
assert self.wait_for_sync(check_create, sync_timeout, f"create collection {c_name}")
188+
189+
# Alter all 3 properties at once
190+
upstream_client.alter_collection_properties(
191+
collection_name=c_name,
192+
properties={
193+
"collection.ttl.seconds": "3600",
194+
"mmap.enabled": "true",
195+
"collection.autocompaction.enabled": "true",
196+
},
197+
)
198+
199+
# Wait for all 3 properties to sync downstream
200+
def check_all_props():
201+
try:
202+
desc = downstream_client.describe_collection(c_name)
203+
props = desc.get("properties", {})
204+
logger.info(f"Downstream collection properties: {props}")
205+
ttl_ok = str(props.get("collection.ttl.seconds", "")) == "3600"
206+
mmap_ok = str(props.get("mmap.enabled", "")).lower() == "true"
207+
autocomp_ok = str(props.get("collection.autocompaction.enabled", "")).lower() == "true"
208+
return ttl_ok and mmap_ok and autocomp_ok
209+
except Exception as e:
210+
logger.warning(f"Check all properties sync failed: {e}")
211+
return False
212+
213+
assert self.wait_for_sync(
214+
check_all_props, sync_timeout, f"all properties sync for {c_name}"
215+
)
216+
217+
def test_drop_properties_sync(self, upstream_client, downstream_client, sync_timeout):
218+
"""Test DROP_COLLECTION_PROPERTIES — set TTL + mmap, drop TTL, verify TTL gone and mmap remains."""
219+
# Store upstream client for teardown
220+
self._upstream_client = upstream_client
221+
222+
c_name = self.gen_unique_name("test_col_drop_props")
223+
self.resources_to_cleanup.append(("collection", c_name))
224+
225+
# Initial cleanup
226+
self.cleanup_collection(upstream_client, c_name)
227+
228+
# Create collection
229+
self._create_basic_collection(upstream_client, c_name)
230+
231+
# Wait for collection to sync downstream
232+
def check_create():
233+
return downstream_client.has_collection(c_name)
234+
235+
assert self.wait_for_sync(check_create, sync_timeout, f"create collection {c_name}")
236+
237+
# Set TTL and mmap properties
238+
upstream_client.alter_collection_properties(
239+
collection_name=c_name,
240+
properties={
241+
"collection.ttl.seconds": "3600",
242+
"mmap.enabled": "true",
243+
},
244+
)
245+
246+
# Wait for both properties to sync downstream
247+
def check_props_set():
248+
try:
249+
desc = downstream_client.describe_collection(c_name)
250+
props = desc.get("properties", {})
251+
ttl_set = str(props.get("collection.ttl.seconds", "")) == "3600"
252+
mmap_set = str(props.get("mmap.enabled", "")).lower() == "true"
253+
return ttl_set and mmap_set
254+
except Exception as e:
255+
logger.warning(f"Check properties set failed: {e}")
256+
return False
257+
258+
assert self.wait_for_sync(check_props_set, sync_timeout, f"set properties for {c_name}")
259+
260+
# Drop TTL property
261+
upstream_client.drop_collection_properties(
262+
collection_name=c_name,
263+
property_keys=["collection.ttl.seconds"],
264+
)
265+
266+
# Wait for TTL to be gone but mmap to remain on downstream
267+
def check_drop_ttl():
268+
try:
269+
desc = downstream_client.describe_collection(c_name)
270+
props = desc.get("properties", {})
271+
logger.info(f"Downstream collection properties after drop: {props}")
272+
ttl_gone = "collection.ttl.seconds" not in props
273+
mmap_remains = str(props.get("mmap.enabled", "")).lower() == "true"
274+
return ttl_gone and mmap_remains
275+
except Exception as e:
276+
logger.warning(f"Check drop TTL sync failed: {e}")
277+
return False
278+
279+
assert self.wait_for_sync(
280+
check_drop_ttl, sync_timeout, f"drop TTL property sync for {c_name}"
281+
)

0 commit comments

Comments
 (0)