diff --git a/tests/test_antfly.py b/tests/test_antfly.py index 024a3b14f..cfc34bd8e 100644 --- a/tests/test_antfly.py +++ b/tests/test_antfly.py @@ -20,9 +20,10 @@ def search_param(self): class _Response: - is_success = True - status_code = 200 - text = "" + def __init__(self, status_code: int = 200): + self.status_code = status_code + self.is_success = 200 <= status_code < 300 + self.text = "" def raise_for_status(self): return None @@ -105,3 +106,36 @@ def test_write_readiness_probe_does_not_create_a_tombstone(): "inserts": {"key:__circus_write_probe__": {"id": -1}}, "sync_level": "write", } + + +def test_vector_benchmark_removes_default_full_text_index(monkeypatch): + adapter = _adapter() + + class Client: + def __init__(self): + self.present = True + self.deleted = [] + + def get(self, path: str): + return _Response(200 if self.present else 404) + + def delete(self, path: str): + self.deleted.append(path) + self.present = False + return _Response(204) + + client = Client() + monkeypatch.setattr("time.sleep", lambda _: None) + + adapter._remove_default_full_text_index(client) + + assert client.deleted == ["/tables/vdbbench/indexes/full_text_index_v0"] + + +def test_vector_benchmark_can_keep_default_full_text_index(monkeypatch): + adapter = _adapter() + + monkeypatch.delenv("ANTFLY_VDBBENCH_KEEP_DEFAULT_FULL_TEXT", raising=False) + assert adapter._keep_default_full_text_index() is False + monkeypatch.setenv("ANTFLY_VDBBENCH_KEEP_DEFAULT_FULL_TEXT", "yes") + assert adapter._keep_default_full_text_index() is True diff --git a/vectordb_bench/backend/clients/antfly/antfly.py b/vectordb_bench/backend/clients/antfly/antfly.py index be9a99c5b..aa26201a9 100644 --- a/vectordb_bench/backend/clients/antfly/antfly.py +++ b/vectordb_bench/backend/clients/antfly/antfly.py @@ -22,6 +22,7 @@ INDEX_READY_POLL_INTERVAL = 2 INDEX_NAME = "vec" INDEX_TYPES = ("embeddings", "aknn_v0") +DEFAULT_FULL_TEXT_INDEX_NAME = "full_text_index_v0" SOURCE_FIELD = "vec_data" @@ -128,6 +129,13 @@ def __init__( # while the shard is still initializing. self._wait_for_write_ready(client) + # Antfly's table quickstart contract creates a default full-text + # index even when the caller supplies no indexes. VectorDBBench is + # an ANN benchmark, so remove that unrelated replay consumer before + # loading unless a diagnostic run explicitly asks to retain it. + if not self._keep_default_full_text_index(): + self._remove_default_full_text_index(client) + self._ensure_external_index(client, dim) # Do not wait for an empty external index to finish rebuilding here. # antfly-zig keeps an empty external dense index in backfill state @@ -137,6 +145,31 @@ def __init__( finally: client.close() + def _remove_default_full_text_index(self, client: httpx.Client) -> None: + path = f"/tables/{self.collection_name}/indexes/{DEFAULT_FULL_TEXT_INDEX_NAME}" + existing = client.get(path) + if existing.status_code == 404: + return + existing.raise_for_status() + + response = client.delete(path) + log.info("Remove default full-text index response: %s", response.status_code) + if response.status_code != 404: + response.raise_for_status() + + deadline = time.monotonic() + TABLE_READY_TIMEOUT + while time.monotonic() < deadline: + probe = client.get(path) + if probe.status_code == 404: + return + probe.raise_for_status() + time.sleep(TABLE_READY_POLL_INTERVAL) + message = f"Antfly default full-text index removal did not become visible within {TABLE_READY_TIMEOUT}s" + raise TimeoutError(message) + + def _keep_default_full_text_index(self) -> bool: + return os.environ.get("ANTFLY_VDBBENCH_KEEP_DEFAULT_FULL_TEXT", "").lower() in {"1", "true", "yes"} + def _ensure_external_index(self, client: httpx.Client, dim: int) -> None: """Create the external embeddings index, verifying shard registration.