From f00b11e8016b592b560ed31caed91af217d00d1b Mon Sep 17 00:00:00 2001 From: Taewoon Kim Date: Wed, 28 Oct 2026 18:22:35 +0900 Subject: [PATCH] create_vector_index: describe mutations_before_rebuild as the floor it is (#110) The engine rebuilds after max(mutationsBeforeRebuild, min(graph size x rebuildGraphRatio, maxPendingMutations)) changes (LSMVectorIndex.computeRebuildThreshold, the same on 26.10.1, 26.10.2 and main), so the old docstring's 'override' and its 100-800 ranges had no effect past a few thousand vectors. The docstring now gives the formula, the defaults, and the setting that does lower the threshold (rebuildGraphRatio). A regression test pins the floor through get_stats()['effectiveMutationsThreshold'] on a 1000-vector index: floors 10 and 100 give 200, 5000 gives 5000. Co-Authored-By: Claude Opus 5.5 --- src/arcadedb/core.py | 15 ++++++++----- tests/test_vector_index_introspection.py | 28 ++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/src/arcadedb/core.py b/src/arcadedb/core.py index c233326..0b9b9d0 100644 --- a/src/arcadedb/core.py +++ b/src/arcadedb/core.py @@ -1545,11 +1545,16 @@ def create_vector_index( below the corpus makes the build re-read vectors from the documents. Set an absolute count only to bound a build on a deliberately small heap. - mutations_before_rebuild: Per-index override for mutations threshold - before triggering a graph rebuild (maps to Java metadata key - "mutationsBeforeRebuild"; uses GlobalConfiguration default if None). - Typical ranges: 100–300 for freshness-heavy workloads; 300–800 for - write-heavy workloads and larger graphs. + mutations_before_rebuild: Floor of the rebuild threshold (maps to Java + metadata key "mutationsBeforeRebuild"; the engine default, 100, if + None). The graph is rebuilt after max(this, min(graph size x + arcadedb.vectorIndex.rebuildGraphRatio, arcadedb.vectorIndex. + maxPendingMutations)) changes (defaults 0.2 and 50000), so any + value below the ratio term has no effect: at 100000 vectors the + threshold is 20000 whether this is 100 or 800. Raise it above that + term to rebuild less often. To rebuild sooner, lower + rebuildGraphRatio (a database-scope setting); with the ratio at 0 + the threshold is exactly this value. pq_subspaces: Number of PQ subspaces (M). Requires quantization="PRODUCT". pq_clusters: Clusters per subspace (K). Requires quantization="PRODUCT". In current ArcadeDB engine builds, this should not exceed the diff --git a/tests/test_vector_index_introspection.py b/tests/test_vector_index_introspection.py index 89a254d..0efb182 100644 --- a/tests/test_vector_index_introspection.py +++ b/tests/test_vector_index_introspection.py @@ -229,3 +229,31 @@ def test_without_product_quantization_it_raises(self, temp_db): with pytest.raises(arcadedb.ArcadeDBError): index.find_nearest_approximate([0.9, 0.1, 0.0], k=1) + + +class TestRebuildThreshold: + """``mutations_before_rebuild`` is the FLOOR of the rebuild threshold (#110). + + The engine rebuilds after max(mutations_before_rebuild, min(graph size x rebuildGraphRatio, maxPendingMutations)) + changes; with the defaults (ratio 0.2, cap 50000) a 1000-vector graph gives 200 for any floor up to 200. The + vectors are distinct and nonzero (cosine is the default similarity). + """ + + ROWS = [ + (f"v{i}", [1.0 + i % 10, 1.0 + i // 10 % 10, 1.0 + i // 100]) + for i in range(1000) + ] + + @pytest.mark.parametrize("floor, expected", [(10, 200), (100, 200), (5000, 5000)]) + def test_the_threshold_is_the_larger_of_the_floor_and_the_ratio_term( + self, temp_db, floor, expected + ): + _make_type(temp_db) + _insert(temp_db, self.ROWS) + index = temp_db.create_vector_index( + "Doc", "embedding", dimensions=3, mutations_before_rebuild=floor + ) + + stats = index.get_stats() + assert stats["totalVectors"] == len(self.ROWS) + assert stats["effectiveMutationsThreshold"] == expected