vectordb-bench 0.0.20__py3-none-any.whl → 0.0.22__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. vectordb_bench/backend/assembler.py +2 -2
  2. vectordb_bench/backend/clients/__init__.py +28 -2
  3. vectordb_bench/backend/clients/aliyun_opensearch/aliyun_opensearch.py +1 -7
  4. vectordb_bench/backend/clients/alloydb/alloydb.py +1 -4
  5. vectordb_bench/backend/clients/api.py +8 -15
  6. vectordb_bench/backend/clients/aws_opensearch/aws_opensearch.py +54 -8
  7. vectordb_bench/backend/clients/aws_opensearch/cli.py +85 -1
  8. vectordb_bench/backend/clients/aws_opensearch/config.py +10 -0
  9. vectordb_bench/backend/clients/chroma/chroma.py +1 -4
  10. vectordb_bench/backend/clients/elastic_cloud/elastic_cloud.py +1 -4
  11. vectordb_bench/backend/clients/memorydb/cli.py +2 -2
  12. vectordb_bench/backend/clients/memorydb/memorydb.py +2 -5
  13. vectordb_bench/backend/clients/milvus/milvus.py +1 -20
  14. vectordb_bench/backend/clients/mongodb/config.py +53 -0
  15. vectordb_bench/backend/clients/mongodb/mongodb.py +200 -0
  16. vectordb_bench/backend/clients/pgdiskann/pgdiskann.py +1 -4
  17. vectordb_bench/backend/clients/pgvecto_rs/pgvecto_rs.py +3 -11
  18. vectordb_bench/backend/clients/pgvector/pgvector.py +2 -7
  19. vectordb_bench/backend/clients/pgvectorscale/pgvectorscale.py +2 -7
  20. vectordb_bench/backend/clients/pinecone/pinecone.py +1 -4
  21. vectordb_bench/backend/clients/qdrant_cloud/qdrant_cloud.py +3 -6
  22. vectordb_bench/backend/clients/redis/redis.py +1 -4
  23. vectordb_bench/backend/clients/test/cli.py +1 -1
  24. vectordb_bench/backend/clients/test/test.py +1 -4
  25. vectordb_bench/backend/clients/weaviate_cloud/weaviate_cloud.py +1 -4
  26. vectordb_bench/backend/data_source.py +4 -12
  27. vectordb_bench/backend/runner/mp_runner.py +16 -34
  28. vectordb_bench/backend/runner/rate_runner.py +4 -4
  29. vectordb_bench/backend/runner/read_write_runner.py +11 -15
  30. vectordb_bench/backend/runner/serial_runner.py +20 -28
  31. vectordb_bench/backend/task_runner.py +6 -26
  32. vectordb_bench/frontend/components/custom/displaypPrams.py +12 -1
  33. vectordb_bench/frontend/components/run_test/submitTask.py +20 -3
  34. vectordb_bench/frontend/config/dbCaseConfigs.py +32 -0
  35. vectordb_bench/interface.py +10 -19
  36. vectordb_bench/log_util.py +15 -2
  37. vectordb_bench/models.py +4 -0
  38. {vectordb_bench-0.0.20.dist-info → vectordb_bench-0.0.22.dist-info}/METADATA +55 -2
  39. {vectordb_bench-0.0.20.dist-info → vectordb_bench-0.0.22.dist-info}/RECORD +43 -41
  40. {vectordb_bench-0.0.20.dist-info → vectordb_bench-0.0.22.dist-info}/LICENSE +0 -0
  41. {vectordb_bench-0.0.20.dist-info → vectordb_bench-0.0.22.dist-info}/WHEEL +0 -0
  42. {vectordb_bench-0.0.20.dist-info → vectordb_bench-0.0.22.dist-info}/entry_points.txt +0 -0
  43. {vectordb_bench-0.0.20.dist-info → vectordb_bench-0.0.22.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,200 @@
1
+ import logging
2
+ import time
3
+ from contextlib import contextmanager
4
+
5
+ from pymongo import MongoClient
6
+ from pymongo.operations import SearchIndexModel
7
+
8
+ from ..api import VectorDB
9
+ from .config import MongoDBIndexConfig
10
+
11
+ log = logging.getLogger(__name__)
12
+
13
+
14
+ class MongoDBError(Exception):
15
+ """Custom exception class for MongoDB client errors."""
16
+
17
+
18
+ class MongoDB(VectorDB):
19
+ def __init__(
20
+ self,
21
+ dim: int,
22
+ db_config: dict,
23
+ db_case_config: MongoDBIndexConfig,
24
+ collection_name: str = "vdb_bench_collection",
25
+ id_field: str = "id",
26
+ vector_field: str = "vector",
27
+ drop_old: bool = False,
28
+ **kwargs,
29
+ ):
30
+ self.dim = dim
31
+ self.db_config = db_config
32
+ self.case_config = db_case_config
33
+ self.collection_name = collection_name
34
+ self.id_field = id_field
35
+ self.vector_field = vector_field
36
+ self.drop_old = drop_old
37
+
38
+ # Update index dimensions
39
+ index_params = self.case_config.index_param()
40
+ log.info(f"index params: {index_params}")
41
+ index_params["fields"][0]["numDimensions"] = dim
42
+ self.index_params = index_params
43
+
44
+ # Initialize - they'll also be set in init()
45
+ uri = self.db_config["connection_string"]
46
+ self.client = MongoClient(uri)
47
+ self.db = self.client[self.db_config["database"]]
48
+ self.collection = self.db[self.collection_name]
49
+ if self.drop_old and self.collection_name in self.db.list_collection_names():
50
+ log.info(f"MongoDB client dropping old collection: {self.collection_name}")
51
+ self.db.drop_collection(self.collection_name)
52
+ self.client = None
53
+ self.db = None
54
+ self.collection = None
55
+
56
+ @contextmanager
57
+ def init(self):
58
+ """Initialize MongoDB client and cleanup when done"""
59
+ try:
60
+ uri = self.db_config["connection_string"]
61
+ self.client = MongoClient(uri)
62
+ self.db = self.client[self.db_config["database"]]
63
+ self.collection = self.db[self.collection_name]
64
+
65
+ yield
66
+ finally:
67
+ if self.client is not None:
68
+ self.client.close()
69
+ self.client = None
70
+ self.db = None
71
+ self.collection = None
72
+
73
+ def _create_index(self) -> None:
74
+ """Create vector search index"""
75
+ index_name = "vector_index"
76
+ index_params = self.index_params
77
+ log.info(f"index params {index_params}")
78
+ # drop index if already exists
79
+ if self.collection.list_indexes():
80
+ all_indexes = self.collection.list_search_indexes()
81
+ if any(idx.get("name") == index_name for idx in all_indexes):
82
+ log.info(f"Drop index: {index_name}")
83
+ try:
84
+ self.collection.drop_search_index(index_name)
85
+ while True:
86
+ indices = list(self.collection.list_search_indexes())
87
+ indices = [idx for idx in indices if idx["name"] == index_name]
88
+ log.debug(f"index status {indices}")
89
+ if len(indices) == 0:
90
+ break
91
+ log.info(f"index deleting {indices}")
92
+ except Exception:
93
+ log.exception(f"Error dropping index {index_name}")
94
+ try:
95
+ # Create vector search index
96
+ search_index = SearchIndexModel(definition=index_params, name=index_name, type="vectorSearch")
97
+
98
+ self.collection.create_search_index(search_index)
99
+ log.info(f"Created vector search index: {index_name}")
100
+ self._wait_for_index_ready(index_name)
101
+
102
+ # Create regular index on id field for faster lookups
103
+ self.collection.create_index(self.id_field)
104
+ log.info(f"Created index on {self.id_field} field")
105
+
106
+ except Exception:
107
+ log.exception(f"Error creating index {index_name}")
108
+ raise
109
+
110
+ def _wait_for_index_ready(self, index_name: str, check_interval: int = 5) -> None:
111
+ """Wait for index to be ready"""
112
+ while True:
113
+ indices = list(self.collection.list_search_indexes())
114
+ log.debug(f"index status {indices}")
115
+ if indices and any(idx.get("name") == index_name and idx.get("queryable") for idx in indices):
116
+ break
117
+ for idx in indices:
118
+ if idx.get("name") == index_name and idx.get("status") == "FAILED":
119
+ error_msg = f"Index {index_name} failed to build"
120
+ raise MongoDBError(error_msg)
121
+
122
+ time.sleep(check_interval)
123
+ log.info(f"Index {index_name} is ready")
124
+
125
+ def need_normalize_cosine(self) -> bool:
126
+ return False
127
+
128
+ def insert_embeddings(
129
+ self,
130
+ embeddings: list[list[float]],
131
+ metadata: list[int],
132
+ **kwargs,
133
+ ) -> (int, Exception | None):
134
+ """Insert embeddings into MongoDB"""
135
+
136
+ # Prepare documents in bulk
137
+ documents = [
138
+ {
139
+ self.id_field: id_,
140
+ self.vector_field: embedding,
141
+ }
142
+ for id_, embedding in zip(metadata, embeddings, strict=False)
143
+ ]
144
+
145
+ # Use ordered=False for better insert performance
146
+ try:
147
+ self.collection.insert_many(documents, ordered=False)
148
+ except Exception as e:
149
+ return 0, e
150
+ return len(documents), None
151
+
152
+ def search_embedding(
153
+ self,
154
+ query: list[float],
155
+ k: int = 100,
156
+ filters: dict | None = None,
157
+ **kwargs,
158
+ ) -> list[int]:
159
+ """Search for similar vectors"""
160
+ search_params = self.case_config.search_param()
161
+
162
+ vector_search = {"queryVector": query, "index": "vector_index", "path": self.vector_field, "limit": k}
163
+
164
+ # Add exact search parameter if specified
165
+ if search_params["exact"]:
166
+ vector_search["exact"] = True
167
+ else:
168
+ # Set numCandidates based on k value and data size
169
+ # For 50K dataset, use higher multiplier for better recall
170
+ num_candidates = min(10000, k * search_params["num_candidates_ratio"])
171
+ vector_search["numCandidates"] = num_candidates
172
+
173
+ # Add filter if specified
174
+ if filters:
175
+ log.info(f"Applying filter: {filters}")
176
+ vector_search["filter"] = {
177
+ "id": {"gte": filters["id"]},
178
+ }
179
+ pipeline = [
180
+ {"$vectorSearch": vector_search},
181
+ {
182
+ "$project": {
183
+ "_id": 0,
184
+ self.id_field: 1,
185
+ "score": {"$meta": "vectorSearchScore"}, # Include similarity score
186
+ }
187
+ },
188
+ ]
189
+
190
+ results = list(self.collection.aggregate(pipeline))
191
+ return [doc[self.id_field] for doc in results]
192
+
193
+ def optimize(self, data_size: int | None = None) -> None:
194
+ """MongoDB vector search indexes are self-optimizing"""
195
+ log.info("optimize for search")
196
+ self._create_index()
197
+ self._wait_for_index_ready("vector_index")
198
+
199
+ def ready_to_load(self) -> None:
200
+ """MongoDB is always ready to load"""
@@ -143,10 +143,7 @@ class PgDiskANN(VectorDB):
143
143
  )
144
144
  self.conn.commit()
145
145
 
146
- def ready_to_load(self):
147
- pass
148
-
149
- def optimize(self):
146
+ def optimize(self, data_size: int | None = None):
150
147
  self._post_insert()
151
148
 
152
149
  def _post_insert(self):
@@ -153,10 +153,7 @@ class PgVectoRS(VectorDB):
153
153
  )
154
154
  self.conn.commit()
155
155
 
156
- def ready_to_load(self):
157
- pass
158
-
159
- def optimize(self):
156
+ def optimize(self, data_size: int | None = None):
160
157
  self._post_insert()
161
158
 
162
159
  def _post_insert(self):
@@ -200,10 +197,7 @@ class PgVectoRS(VectorDB):
200
197
  self.cursor.execute(index_create_sql)
201
198
  self.conn.commit()
202
199
  except Exception as e:
203
- log.warning(
204
- f"Failed to create pgvecto.rs index {self._index_name} \
205
- at table {self.table_name} error: {e}",
206
- )
200
+ log.warning(f"Failed to create pgvecto.rs index {self._index_name} at table {self.table_name} error: {e}")
207
201
  raise e from None
208
202
 
209
203
  def _create_table(self, dim: int):
@@ -258,9 +252,7 @@ class PgVectoRS(VectorDB):
258
252
 
259
253
  return len(metadata), None
260
254
  except Exception as e:
261
- log.warning(
262
- f"Failed to insert data into pgvecto.rs table ({self.table_name}), error: {e}",
263
- )
255
+ log.warning(f"Failed to insert data into pgvecto.rs table ({self.table_name}), error: {e}")
264
256
  return 0, e
265
257
 
266
258
  def search_embedding(
@@ -228,10 +228,7 @@ class PgVector(VectorDB):
228
228
  )
229
229
  self.conn.commit()
230
230
 
231
- def ready_to_load(self):
232
- pass
233
-
234
- def optimize(self):
231
+ def optimize(self, data_size: int | None = None):
235
232
  self._post_insert()
236
233
 
237
234
  def _post_insert(self):
@@ -415,9 +412,7 @@ class PgVector(VectorDB):
415
412
 
416
413
  return len(metadata), None
417
414
  except Exception as e:
418
- log.warning(
419
- f"Failed to insert data into pgvector table ({self.table_name}), error: {e}",
420
- )
415
+ log.warning(f"Failed to insert data into pgvector table ({self.table_name}), error: {e}")
421
416
  return 0, e
422
417
 
423
418
  def search_embedding(
@@ -143,10 +143,7 @@ class PgVectorScale(VectorDB):
143
143
  )
144
144
  self.conn.commit()
145
145
 
146
- def ready_to_load(self):
147
- pass
148
-
149
- def optimize(self):
146
+ def optimize(self, data_size: int | None = None):
150
147
  self._post_insert()
151
148
 
152
149
  def _post_insert(self):
@@ -255,9 +252,7 @@ class PgVectorScale(VectorDB):
255
252
 
256
253
  return len(metadata), None
257
254
  except Exception as e:
258
- log.warning(
259
- f"Failed to insert data into pgvector table ({self.table_name}), error: {e}",
260
- )
255
+ log.warning(f"Failed to insert data into pgvector table ({self.table_name}), error: {e}")
261
256
  return 0, e
262
257
 
263
258
  def search_embedding(
@@ -59,10 +59,7 @@ class Pinecone(VectorDB):
59
59
  self.index = pc.Index(self.index_name)
60
60
  yield
61
61
 
62
- def ready_to_load(self):
63
- pass
64
-
65
- def optimize(self):
62
+ def optimize(self, data_size: int | None = None):
66
63
  pass
67
64
 
68
65
  def insert_embeddings(
@@ -62,10 +62,7 @@ class QdrantCloud(VectorDB):
62
62
  self.qdrant_client = None
63
63
  del self.qdrant_client
64
64
 
65
- def ready_to_load(self):
66
- pass
67
-
68
- def optimize(self):
65
+ def optimize(self, data_size: int | None = None):
69
66
  assert self.qdrant_client, "Please call self.init() before"
70
67
  # wait for vectors to be fully indexed
71
68
  try:
@@ -76,8 +73,8 @@ class QdrantCloud(VectorDB):
76
73
  continue
77
74
  if info.status == CollectionStatus.GREEN:
78
75
  msg = (
79
- f"Stored vectors: {info.vectors_count}, Indexed vectors: {info.indexed_vectors_count}, ",
80
- f"Collection status: {info.indexed_vectors_count}",
76
+ f"Stored vectors: {info.vectors_count}, Indexed vectors: {info.indexed_vectors_count}, "
77
+ f"Collection status: {info.indexed_vectors_count}"
81
78
  )
82
79
  log.info(msg)
83
80
  return
@@ -95,10 +95,7 @@ class Redis(VectorDB):
95
95
  def ready_to_search(self) -> bool:
96
96
  """Check if the database is ready to search."""
97
97
 
98
- def ready_to_load(self) -> bool:
99
- pass
100
-
101
- def optimize(self) -> None:
98
+ def optimize(self, data_size: int | None = None):
102
99
  pass
103
100
 
104
101
  def insert_embeddings(
@@ -17,7 +17,7 @@ class TestTypedDict(CommonTypedDict): ...
17
17
  @click_parameter_decorators_from_typed_dict(TestTypedDict)
18
18
  def Test(**parameters: Unpack[TestTypedDict]):
19
19
  run(
20
- db=DB.NewClient,
20
+ db=DB.Test,
21
21
  db_config=TestConfig(db_label=parameters["db_label"]),
22
22
  db_case_config=TestIndexConfig(),
23
23
  **parameters,
@@ -33,10 +33,7 @@ class Test(VectorDB):
33
33
 
34
34
  yield
35
35
 
36
- def ready_to_load(self) -> bool:
37
- return True
38
-
39
- def optimize(self) -> None:
36
+ def optimize(self, data_size: int | None = None):
40
37
  pass
41
38
 
42
39
  def insert_embeddings(
@@ -67,10 +67,7 @@ class WeaviateCloud(VectorDB):
67
67
  self.client = None
68
68
  del self.client
69
69
 
70
- def ready_to_load(self):
71
- """Should call insert first, do nothing"""
72
-
73
- def optimize(self):
70
+ def optimize(self, data_size: int | None = None):
74
71
  assert self.client.schema.exists(self.collection_name)
75
72
  self.client.schema.update_config(
76
73
  self.collection_name,
@@ -63,9 +63,7 @@ class AliyunOSSReader(DatasetReader):
63
63
  # check size equal
64
64
  remote_size, local_size = info.content_length, local.stat().st_size
65
65
  if remote_size != local_size:
66
- log.info(
67
- f"local file: {local} size[{local_size}] not match with remote size[{remote_size}]",
68
- )
66
+ log.info(f"local file: {local} size[{local_size}] not match with remote size[{remote_size}]")
69
67
  return False
70
68
 
71
69
  return True
@@ -89,9 +87,7 @@ class AliyunOSSReader(DatasetReader):
89
87
  local_file = local_ds_root.joinpath(file)
90
88
 
91
89
  if (not local_file.exists()) or (not self.validate_file(remote_file, local_file)):
92
- log.info(
93
- f"local file: {local_file} not match with remote: {remote_file}; add to downloading list",
94
- )
90
+ log.info(f"local file: {local_file} not match with remote: {remote_file}; add to downloading list")
95
91
  downloads.append((remote_file, local_file))
96
92
 
97
93
  if len(downloads) == 0:
@@ -135,9 +131,7 @@ class AwsS3Reader(DatasetReader):
135
131
  local_file = local_ds_root.joinpath(file)
136
132
 
137
133
  if (not local_file.exists()) or (not self.validate_file(remote_file, local_file)):
138
- log.info(
139
- f"local file: {local_file} not match with remote: {remote_file}; add to downloading list",
140
- )
134
+ log.info(f"local file: {local_file} not match with remote: {remote_file}; add to downloading list")
141
135
  downloads.append(remote_file)
142
136
 
143
137
  if len(downloads) == 0:
@@ -157,9 +151,7 @@ class AwsS3Reader(DatasetReader):
157
151
  # check size equal
158
152
  remote_size, local_size = info.get("size"), local.stat().st_size
159
153
  if remote_size != local_size:
160
- log.info(
161
- f"local file: {local} size[{local_size}] not match with remote size[{remote_size}]",
162
- )
154
+ log.info(f"local file: {local} size[{local_size}] not match with remote size[{remote_size}]")
163
155
  return False
164
156
 
165
157
  return True
@@ -79,14 +79,14 @@ class MultiProcessingSearchRunner:
79
79
 
80
80
  if count % 500 == 0:
81
81
  log.debug(
82
- f"({mp.current_process().name:16}) ",
83
- f"search_count: {count}, latest_latency={time.perf_counter()-s}",
82
+ f"({mp.current_process().name:16}) "
83
+ f"search_count: {count}, latest_latency={time.perf_counter()-s}"
84
84
  )
85
85
 
86
86
  total_dur = round(time.perf_counter() - start_time, 4)
87
87
  log.info(
88
88
  f"{mp.current_process().name:16} search {self.duration}s: "
89
- f"actual_dur={total_dur}s, count={count}, qps in this process: {round(count / total_dur, 4):3}",
89
+ f"actual_dur={total_dur}s, count={count}, qps in this process: {round(count / total_dur, 4):3}"
90
90
  )
91
91
 
92
92
  return (count, total_dur, latencies)
@@ -94,9 +94,7 @@ class MultiProcessingSearchRunner:
94
94
  @staticmethod
95
95
  def get_mp_context():
96
96
  mp_start_method = "spawn"
97
- log.debug(
98
- f"MultiProcessingSearchRunner get multiprocessing start method: {mp_start_method}",
99
- )
97
+ log.debug(f"MultiProcessingSearchRunner get multiprocessing start method: {mp_start_method}")
100
98
  return mp.get_context(mp_start_method)
101
99
 
102
100
  def _run_all_concurrencies_mem_efficient(self):
@@ -113,9 +111,7 @@ class MultiProcessingSearchRunner:
113
111
  mp_context=self.get_mp_context(),
114
112
  max_workers=conc,
115
113
  ) as executor:
116
- log.info(
117
- f"Start search {self.duration}s in concurrency {conc}, filters: {self.filters}",
118
- )
114
+ log.info(f"Start search {self.duration}s in concurrency {conc}, filters: {self.filters}")
119
115
  future_iter = [executor.submit(self.search, self.test_data, q, cond) for i in range(conc)]
120
116
  # Sync all processes
121
117
  while q.qsize() < conc:
@@ -124,9 +120,7 @@ class MultiProcessingSearchRunner:
124
120
 
125
121
  with cond:
126
122
  cond.notify_all()
127
- log.info(
128
- f"Syncing all process and start concurrency search, concurrency={conc}",
129
- )
123
+ log.info(f"Syncing all process and start concurrency search, concurrency={conc}")
130
124
 
131
125
  start = time.perf_counter()
132
126
  all_count = sum([r.result()[0] for r in future_iter])
@@ -140,18 +134,14 @@ class MultiProcessingSearchRunner:
140
134
  conc_qps_list.append(qps)
141
135
  conc_latency_p99_list.append(latency_p99)
142
136
  conc_latency_avg_list.append(latency_avg)
143
- log.info(
144
- f"End search in concurrency {conc}: dur={cost}s, total_count={all_count}, qps={qps}",
145
- )
137
+ log.info(f"End search in concurrency {conc}: dur={cost}s, total_count={all_count}, qps={qps}")
146
138
 
147
139
  if qps > max_qps:
148
140
  max_qps = qps
149
- log.info(
150
- f"Update largest qps with concurrency {conc}: current max_qps={max_qps}",
151
- )
141
+ log.info(f"Update largest qps with concurrency {conc}: current max_qps={max_qps}")
152
142
  except Exception as e:
153
143
  log.warning(
154
- f"Fail to search all concurrencies: {self.concurrencies}, max_qps before failure={max_qps}, reason={e}",
144
+ f"Fail to search, concurrencies: {self.concurrencies}, max_qps before failure={max_qps}, reason={e}"
155
145
  )
156
146
  traceback.print_exc()
157
147
 
@@ -193,9 +183,7 @@ class MultiProcessingSearchRunner:
193
183
  mp_context=self.get_mp_context(),
194
184
  max_workers=conc,
195
185
  ) as executor:
196
- log.info(
197
- f"Start search_by_dur {duration}s in concurrency {conc}, filters: {self.filters}",
198
- )
186
+ log.info(f"Start search_by_dur {duration}s in concurrency {conc}, filters: {self.filters}")
199
187
  future_iter = [
200
188
  executor.submit(self.search_by_dur, duration, self.test_data, q, cond) for i in range(conc)
201
189
  ]
@@ -206,24 +194,18 @@ class MultiProcessingSearchRunner:
206
194
 
207
195
  with cond:
208
196
  cond.notify_all()
209
- log.info(
210
- f"Syncing all process and start concurrency search, concurrency={conc}",
211
- )
197
+ log.info(f"Syncing all process and start concurrency search, concurrency={conc}")
212
198
 
213
199
  start = time.perf_counter()
214
200
  all_count = sum([r.result() for r in future_iter])
215
201
  cost = time.perf_counter() - start
216
202
 
217
203
  qps = round(all_count / cost, 4)
218
- log.info(
219
- f"End search in concurrency {conc}: dur={cost}s, total_count={all_count}, qps={qps}",
220
- )
204
+ log.info(f"End search in concurrency {conc}: dur={cost}s, total_count={all_count}, qps={qps}")
221
205
 
222
206
  if qps > max_qps:
223
207
  max_qps = qps
224
- log.info(
225
- f"Update largest qps with concurrency {conc}: current max_qps={max_qps}",
226
- )
208
+ log.info(f"Update largest qps with concurrency {conc}: current max_qps={max_qps}")
227
209
  except Exception as e:
228
210
  log.warning(
229
211
  f"Fail to search all concurrencies: {self.concurrencies}, max_qps before failure={max_qps}, reason={e}",
@@ -275,14 +257,14 @@ class MultiProcessingSearchRunner:
275
257
 
276
258
  if count % 500 == 0:
277
259
  log.debug(
278
- f"({mp.current_process().name:16}) search_count: {count}, ",
279
- f"latest_latency={time.perf_counter()-s}",
260
+ f"({mp.current_process().name:16}) search_count: {count}, "
261
+ f"latest_latency={time.perf_counter()-s}"
280
262
  )
281
263
 
282
264
  total_dur = round(time.perf_counter() - start_time, 4)
283
265
  log.debug(
284
266
  f"{mp.current_process().name:16} search {self.duration}s: "
285
- f"actual_dur={total_dur}s, count={count}, qps in this process: {round(count / total_dur, 4):3}",
267
+ f"actual_dur={total_dur}s, count={count}, qps in this process: {round(count / total_dur, 4):3}"
286
268
  )
287
269
 
288
270
  return count
@@ -73,14 +73,14 @@ class RatedMultiThreadingInsertRunner:
73
73
 
74
74
  if len(not_done) > 0:
75
75
  log.warning(
76
- f"Failed to finish all tasks in 1s, [{len(not_done)}/{len(executing_futures)}] ",
77
- f"tasks are not done, waited={wait_interval:.2f}, trying to wait in the next round",
76
+ f"Failed to finish all tasks in 1s, [{len(not_done)}/{len(executing_futures)}] "
77
+ f"tasks are not done, waited={wait_interval:.2f}, trying to wait in the next round"
78
78
  )
79
79
  executing_futures = list(not_done)
80
80
  else:
81
81
  log.debug(
82
- f"Finished {len(executing_futures)} insert-{config.NUM_PER_BATCH} ",
83
- f"task in 1s, wait_interval={wait_interval:.2f}",
82
+ f"Finished {len(executing_futures)} insert-{config.NUM_PER_BATCH} "
83
+ f"task in 1s, wait_interval={wait_interval:.2f}"
84
84
  )
85
85
  executing_futures = []
86
86
  except Exception as e:
@@ -45,8 +45,8 @@ class ReadWriteRunner(MultiProcessingSearchRunner, RatedMultiThreadingInsertRunn
45
45
  self.read_dur_after_write = read_dur_after_write
46
46
 
47
47
  log.info(
48
- f"Init runner, concurencys={concurrencies}, search_stage={search_stage}, ",
49
- f"stage_search_dur={read_dur_after_write}",
48
+ f"Init runner, concurencys={concurrencies}, search_stage={search_stage}, "
49
+ f"stage_search_dur={read_dur_after_write}"
50
50
  )
51
51
 
52
52
  test_emb = np.stack(dataset.test_data["emb"])
@@ -80,7 +80,7 @@ class ReadWriteRunner(MultiProcessingSearchRunner, RatedMultiThreadingInsertRunn
80
80
  """Optimize needs to run in differenct process for pymilvus schema recursion problem"""
81
81
  with self.db.init():
82
82
  log.info("Search after write - Optimize start")
83
- self.db.optimize()
83
+ self.db.optimize(data_size=self.data_volume)
84
84
  log.info("Search after write - Optimize finished")
85
85
 
86
86
  def run_search(self):
@@ -88,12 +88,10 @@ class ReadWriteRunner(MultiProcessingSearchRunner, RatedMultiThreadingInsertRunn
88
88
  res, ssearch_dur = self.serial_search_runner.run()
89
89
  recall, ndcg, p99_latency = res
90
90
  log.info(
91
- f"Search after write - Serial search - recall={recall}, ndcg={ndcg}, p99={p99_latency}, ",
91
+ f"Search after write - Serial search - recall={recall}, ndcg={ndcg}, p99={p99_latency}, "
92
92
  f"dur={ssearch_dur:.4f}",
93
93
  )
94
- log.info(
95
- f"Search after wirte - Conc search start, dur for each conc={self.read_dur_after_write}",
96
- )
94
+ log.info(f"Search after wirte - Conc search start, dur for each conc={self.read_dur_after_write}")
97
95
  max_qps = self.run_by_dur(self.read_dur_after_write)
98
96
  log.info(f"Search after wirte - Conc search finished, max_qps={max_qps}")
99
97
 
@@ -157,9 +155,7 @@ class ReadWriteRunner(MultiProcessingSearchRunner, RatedMultiThreadingInsertRunn
157
155
 
158
156
  got = wait_next_target(start_batch, target_batch)
159
157
  if got is False:
160
- log.warning(
161
- f"Abnormal exit, target_batch={target_batch}, start_batch={start_batch}",
162
- )
158
+ log.warning(f"Abnormal exit, target_batch={target_batch}, start_batch={start_batch}")
163
159
  return None
164
160
 
165
161
  log.info(f"Insert {perc}% done, total batch={total_batch}")
@@ -167,8 +163,8 @@ class ReadWriteRunner(MultiProcessingSearchRunner, RatedMultiThreadingInsertRunn
167
163
  res, ssearch_dur = self.serial_search_runner.run()
168
164
  recall, ndcg, p99_latency = res
169
165
  log.info(
170
- f"[{target_batch}/{total_batch}] Serial search - {perc}% done, recall={recall}, ",
171
- f"ndcg={ndcg}, p99={p99_latency}, dur={ssearch_dur:.4f}",
166
+ f"[{target_batch}/{total_batch}] Serial search - {perc}% done, recall={recall}, "
167
+ f"ndcg={ndcg}, p99={p99_latency}, dur={ssearch_dur:.4f}"
172
168
  )
173
169
 
174
170
  # Search duration for non-last search stage is carefully calculated.
@@ -183,8 +179,8 @@ class ReadWriteRunner(MultiProcessingSearchRunner, RatedMultiThreadingInsertRunn
183
179
  each_conc_search_dur = csearch_dur / len(self.concurrencies)
184
180
  if each_conc_search_dur < 30:
185
181
  warning_msg = (
186
- f"Results might be inaccurate, duration[{csearch_dur:.4f}] left for conc-search is too short, ",
187
- f"total available dur={total_dur_between_stages}, serial_search_cost={ssearch_dur}.",
182
+ f"Results might be inaccurate, duration[{csearch_dur:.4f}] left for conc-search is too short, "
183
+ f"total available dur={total_dur_between_stages}, serial_search_cost={ssearch_dur}."
188
184
  )
189
185
  log.warning(warning_msg)
190
186
 
@@ -193,7 +189,7 @@ class ReadWriteRunner(MultiProcessingSearchRunner, RatedMultiThreadingInsertRunn
193
189
  each_conc_search_dur = 60
194
190
 
195
191
  log.info(
196
- f"[{target_batch}/{total_batch}] Concurrent search - {perc}% start, dur={each_conc_search_dur:.4f}",
192
+ f"[{target_batch}/{total_batch}] Concurrent search - {perc}% start, dur={each_conc_search_dur:.4f}"
197
193
  )
198
194
  max_qps = self.run_by_dur(each_conc_search_dur)
199
195
  result.append((perc, max_qps, recall, ndcg, p99_latency))