498b235461
Build and test / Build and test AMD64 Ubuntu 22.04 (push) Failing after 0s
Publish Builder / amazonlinux2023 (push) Failing after 1s
Build and test / UT for Go (push) Has been skipped
Publish KRTE Images / KRTE (push) Failing after 1s
Build and test / Integration Test (push) Has been skipped
Build and test / Upload Code Coverage (push) Has been skipped
Publish Builder / rockylinux9 (push) Failing after 1s
Publish Builder / ubuntu22.04 (push) Failing after 0s
Publish Builder / ubuntu24.04 (push) Failing after 0s
Publish Gpu Builder / publish-gpu-builder (push) Failing after 1s
Publish Test Images / PyTest (push) Failing after 0s
Build and test / UT for Cpp (push) Has been cancelled
277 lines
11 KiB
Python
277 lines
11 KiB
Python
"""
|
|
CDC sync tests for collection property operations.
|
|
"""
|
|
|
|
import time
|
|
|
|
from .base import TestCDCSyncBase, logger
|
|
|
|
|
|
class TestCDCSyncCollectionProperties(TestCDCSyncBase):
|
|
"""Test CDC sync for collection property (alter/drop) operations."""
|
|
|
|
def setup_method(self):
|
|
"""Setup for each test method."""
|
|
self.resources_to_cleanup = []
|
|
|
|
def teardown_method(self):
|
|
"""Cleanup after each test method - only cleanup upstream, downstream will sync."""
|
|
upstream_client = getattr(self, "_upstream_client", None)
|
|
|
|
if upstream_client:
|
|
for resource_type, resource_name in self.resources_to_cleanup:
|
|
if resource_type == "collection":
|
|
self.cleanup_collection(upstream_client, resource_name)
|
|
|
|
time.sleep(1) # Allow cleanup to sync to downstream
|
|
|
|
def _create_basic_collection(self, client, c_name):
|
|
"""Create a default schema collection, insert 100 rows, and create HNSW index."""
|
|
schema = self.create_default_schema(client)
|
|
client.create_collection(
|
|
collection_name=c_name,
|
|
schema=schema,
|
|
consistency_level="Strong",
|
|
)
|
|
|
|
# Insert 100 rows
|
|
test_data = self.generate_test_data(100)
|
|
client.insert(c_name, test_data)
|
|
|
|
# Create HNSW index on vector field
|
|
index_params = client.prepare_index_params()
|
|
index_params.add_index(
|
|
field_name="vector",
|
|
index_type="HNSW",
|
|
metric_type="L2",
|
|
params={"M": 8, "efConstruction": 64},
|
|
)
|
|
client.create_index(c_name, index_params)
|
|
|
|
def test_ttl_sync(self, upstream_client, downstream_client, sync_timeout):
|
|
"""Test ALTER_COLLECTION_PROPERTIES (TTL) sync."""
|
|
# Store upstream client for teardown
|
|
self._upstream_client = upstream_client
|
|
|
|
c_name = self.gen_unique_name("test_col_ttl")
|
|
self.resources_to_cleanup.append(("collection", c_name))
|
|
|
|
# Initial cleanup
|
|
self.cleanup_collection(upstream_client, c_name)
|
|
|
|
# Create collection
|
|
self._create_basic_collection(upstream_client, c_name)
|
|
|
|
# Wait for collection to sync downstream
|
|
def check_create():
|
|
return downstream_client.has_collection(c_name)
|
|
|
|
assert self.wait_for_sync(check_create, sync_timeout, f"create collection {c_name}")
|
|
|
|
# Alter collection TTL property
|
|
upstream_client.alter_collection_properties(
|
|
collection_name=c_name,
|
|
properties={"collection.ttl.seconds": "3600"},
|
|
)
|
|
|
|
# Wait for property to sync downstream
|
|
def check_ttl():
|
|
try:
|
|
desc = downstream_client.describe_collection(c_name)
|
|
props = desc.get("properties", {})
|
|
logger.info(f"Downstream collection properties: {props}")
|
|
return str(props.get("collection.ttl.seconds", "")) == "3600"
|
|
except Exception as e:
|
|
logger.warning(f"Check TTL sync failed: {e}")
|
|
return False
|
|
|
|
assert self.wait_for_sync(check_ttl, sync_timeout, f"TTL property sync for {c_name}")
|
|
|
|
def test_mmap_sync(self, upstream_client, downstream_client, sync_timeout):
|
|
"""Test ALTER_COLLECTION_PROPERTIES (mmap.enabled) sync."""
|
|
# Store upstream client for teardown
|
|
self._upstream_client = upstream_client
|
|
|
|
c_name = self.gen_unique_name("test_col_mmap")
|
|
self.resources_to_cleanup.append(("collection", c_name))
|
|
|
|
# Initial cleanup
|
|
self.cleanup_collection(upstream_client, c_name)
|
|
|
|
# Create collection
|
|
self._create_basic_collection(upstream_client, c_name)
|
|
|
|
# Wait for collection to sync downstream
|
|
def check_create():
|
|
return downstream_client.has_collection(c_name)
|
|
|
|
assert self.wait_for_sync(check_create, sync_timeout, f"create collection {c_name}")
|
|
|
|
# Alter mmap.enabled property
|
|
upstream_client.alter_collection_properties(
|
|
collection_name=c_name,
|
|
properties={"mmap.enabled": "true"},
|
|
)
|
|
|
|
# Wait for property to sync downstream
|
|
def check_mmap():
|
|
try:
|
|
desc = downstream_client.describe_collection(c_name)
|
|
props = desc.get("properties", {})
|
|
logger.info(f"Downstream collection properties: {props}")
|
|
return str(props.get("mmap.enabled", "")).lower() == "true"
|
|
except Exception as e:
|
|
logger.warning(f"Check mmap sync failed: {e}")
|
|
return False
|
|
|
|
assert self.wait_for_sync(check_mmap, sync_timeout, f"mmap property sync for {c_name}")
|
|
|
|
def test_autocompaction_sync(self, upstream_client, downstream_client, sync_timeout):
|
|
"""Test ALTER_COLLECTION_PROPERTIES (autocompaction.enabled) sync."""
|
|
# Store upstream client for teardown
|
|
self._upstream_client = upstream_client
|
|
|
|
c_name = self.gen_unique_name("test_col_autocomp")
|
|
self.resources_to_cleanup.append(("collection", c_name))
|
|
|
|
# Initial cleanup
|
|
self.cleanup_collection(upstream_client, c_name)
|
|
|
|
# Create collection
|
|
self._create_basic_collection(upstream_client, c_name)
|
|
|
|
# Wait for collection to sync downstream
|
|
def check_create():
|
|
return downstream_client.has_collection(c_name)
|
|
|
|
assert self.wait_for_sync(check_create, sync_timeout, f"create collection {c_name}")
|
|
|
|
# Alter autocompaction property
|
|
upstream_client.alter_collection_properties(
|
|
collection_name=c_name,
|
|
properties={"collection.autocompaction.enabled": "true"},
|
|
)
|
|
|
|
# Wait for property to sync downstream
|
|
def check_autocomp():
|
|
try:
|
|
desc = downstream_client.describe_collection(c_name)
|
|
props = desc.get("properties", {})
|
|
logger.info(f"Downstream collection properties: {props}")
|
|
return str(props.get("collection.autocompaction.enabled", "")).lower() == "true"
|
|
except Exception as e:
|
|
logger.warning(f"Check autocompaction sync failed: {e}")
|
|
return False
|
|
|
|
assert self.wait_for_sync(check_autocomp, sync_timeout, f"autocompaction property sync for {c_name}")
|
|
|
|
def test_alter_multiple_properties(self, upstream_client, downstream_client, sync_timeout):
|
|
"""Test ALTER_COLLECTION_PROPERTIES with multiple properties at once sync."""
|
|
# Store upstream client for teardown
|
|
self._upstream_client = upstream_client
|
|
|
|
c_name = self.gen_unique_name("test_col_multi_props")
|
|
self.resources_to_cleanup.append(("collection", c_name))
|
|
|
|
# Initial cleanup
|
|
self.cleanup_collection(upstream_client, c_name)
|
|
|
|
# Create collection
|
|
self._create_basic_collection(upstream_client, c_name)
|
|
|
|
# Wait for collection to sync downstream
|
|
def check_create():
|
|
return downstream_client.has_collection(c_name)
|
|
|
|
assert self.wait_for_sync(check_create, sync_timeout, f"create collection {c_name}")
|
|
|
|
# Alter all 3 properties at once
|
|
upstream_client.alter_collection_properties(
|
|
collection_name=c_name,
|
|
properties={
|
|
"collection.ttl.seconds": "3600",
|
|
"mmap.enabled": "true",
|
|
"collection.autocompaction.enabled": "true",
|
|
},
|
|
)
|
|
|
|
# Wait for all 3 properties to sync downstream
|
|
def check_all_props():
|
|
try:
|
|
desc = downstream_client.describe_collection(c_name)
|
|
props = desc.get("properties", {})
|
|
logger.info(f"Downstream collection properties: {props}")
|
|
ttl_ok = str(props.get("collection.ttl.seconds", "")) == "3600"
|
|
mmap_ok = str(props.get("mmap.enabled", "")).lower() == "true"
|
|
autocomp_ok = str(props.get("collection.autocompaction.enabled", "")).lower() == "true"
|
|
return ttl_ok and mmap_ok and autocomp_ok
|
|
except Exception as e:
|
|
logger.warning(f"Check all properties sync failed: {e}")
|
|
return False
|
|
|
|
assert self.wait_for_sync(check_all_props, sync_timeout, f"all properties sync for {c_name}")
|
|
|
|
def test_drop_properties_sync(self, upstream_client, downstream_client, sync_timeout):
|
|
"""Test DROP_COLLECTION_PROPERTIES — set TTL + mmap, drop TTL, verify TTL gone and mmap remains."""
|
|
# Store upstream client for teardown
|
|
self._upstream_client = upstream_client
|
|
|
|
c_name = self.gen_unique_name("test_col_drop_props")
|
|
self.resources_to_cleanup.append(("collection", c_name))
|
|
|
|
# Initial cleanup
|
|
self.cleanup_collection(upstream_client, c_name)
|
|
|
|
# Create collection
|
|
self._create_basic_collection(upstream_client, c_name)
|
|
|
|
# Wait for collection to sync downstream
|
|
def check_create():
|
|
return downstream_client.has_collection(c_name)
|
|
|
|
assert self.wait_for_sync(check_create, sync_timeout, f"create collection {c_name}")
|
|
|
|
# Set TTL and mmap properties
|
|
upstream_client.alter_collection_properties(
|
|
collection_name=c_name,
|
|
properties={
|
|
"collection.ttl.seconds": "3600",
|
|
"mmap.enabled": "true",
|
|
},
|
|
)
|
|
|
|
# Wait for both properties to sync downstream
|
|
def check_props_set():
|
|
try:
|
|
desc = downstream_client.describe_collection(c_name)
|
|
props = desc.get("properties", {})
|
|
ttl_set = str(props.get("collection.ttl.seconds", "")) == "3600"
|
|
mmap_set = str(props.get("mmap.enabled", "")).lower() == "true"
|
|
return ttl_set and mmap_set
|
|
except Exception as e:
|
|
logger.warning(f"Check properties set failed: {e}")
|
|
return False
|
|
|
|
assert self.wait_for_sync(check_props_set, sync_timeout, f"set properties for {c_name}")
|
|
|
|
# Drop TTL property
|
|
upstream_client.drop_collection_properties(
|
|
collection_name=c_name,
|
|
property_keys=["collection.ttl.seconds"],
|
|
)
|
|
|
|
# Wait for TTL to be gone but mmap to remain on downstream
|
|
def check_drop_ttl():
|
|
try:
|
|
desc = downstream_client.describe_collection(c_name)
|
|
props = desc.get("properties", {})
|
|
logger.info(f"Downstream collection properties after drop: {props}")
|
|
ttl_gone = "collection.ttl.seconds" not in props
|
|
mmap_remains = str(props.get("mmap.enabled", "")).lower() == "true"
|
|
return ttl_gone and mmap_remains
|
|
except Exception as e:
|
|
logger.warning(f"Check drop TTL sync failed: {e}")
|
|
return False
|
|
|
|
assert self.wait_for_sync(check_drop_ttl, sync_timeout, f"drop TTL property sync for {c_name}")
|