diff --git a/_includes/async-replication-per-collection-config.mdx b/_includes/async-replication-per-collection-config.mdx
index 14cb1dd7f..2e8f141cf 100644
--- a/_includes/async-replication-per-collection-config.mdx
+++ b/_includes/async-replication-per-collection-config.mdx
@@ -1,3 +1,3 @@
:::info Collection-level configuration — Added in `v1.36`
-Async replication parameters can also be set per-collection via the `asyncConfig` object in `replicationConfig`. Per-collection settings override the cluster-wide environment variable defaults. See [Collection `asyncConfig` parameters](/weaviate/config-refs/collections#async-config) for details.
+Async replication runs by default for any collection with a replication factor greater than `1` (as of `v1.38`). To fine-tune its behavior for a specific collection, set the `asyncConfig` object in `replicationConfig`. Cluster-wide environment variable settings override per-collection settings. See [Collection `asyncConfig` parameters](/weaviate/config-refs/collections#async-config) for details.
:::
diff --git a/_includes/code/config-refs/reference.collections.py b/_includes/code/config-refs/reference.collections.py
index 80d29c11c..5f1871bd9 100644
--- a/_includes/code/config-refs/reference.collections.py
+++ b/_includes/code/config-refs/reference.collections.py
@@ -57,7 +57,6 @@
),
replication_config=Configure.replication(
factor=1,
- async_enabled=False,
deletion_strategy=ReplicationDeletionStrategy.TIME_BASED_RESOLUTION,
),
)
@@ -733,7 +732,6 @@
# highlight-start
replication_config=Configure.replication(
factor=3,
- async_enabled=True,
deletion_strategy=ReplicationDeletionStrategy.TIME_BASED_RESOLUTION,
),
# highlight-end
@@ -743,7 +741,6 @@
# Test
collection = client.collections.use("Article")
config = collection.config.get()
-assert config.replication_config.async_enabled == True
assert (
config.replication_config.deletion_strategy
== ReplicationDeletionStrategy.TIME_BASED_RESOLUTION
diff --git a/_includes/code/configuration/replication-consistency.mdx b/_includes/code/configuration/replication-consistency.mdx
index 9e1e6d0cc..1392a7afd 100644
--- a/_includes/code/configuration/replication-consistency.mdx
+++ b/_includes/code/configuration/replication-consistency.mdx
@@ -43,8 +43,7 @@ curl \
}
],
"replicationConfig": {
- "factor": 3,
- "asyncEnabled": true
+ "factor": 3
}
}' \
http://localhost:8080/v1/schema
diff --git a/_includes/code/howto/go/docs/manage-data.classes_test.go b/_includes/code/howto/go/docs/manage-data.classes_test.go
index a6f07668b..cfc4209c2 100644
--- a/_includes/code/howto/go/docs/manage-data.classes_test.go
+++ b/_includes/code/howto/go/docs/manage-data.classes_test.go
@@ -585,8 +585,8 @@ func Test_ManageDataClasses(t *testing.T) {
articleClass := &models.Class{
Class: "Article",
Description: "Collection of articles",
+ // Async replication runs by default when the replication factor is greater than 1
ReplicationConfig: &models.ReplicationConfig{
- AsyncEnabled: true,
Factor: 3,
DeletionStrategy: models.ReplicationConfigDeletionStrategyTimeBasedResolution,
},
diff --git a/_includes/code/howto/manage-data.collections.py b/_includes/code/howto/manage-data.collections.py
index ffab3b269..994eac792 100644
--- a/_includes/code/howto/manage-data.collections.py
+++ b/_includes/code/howto/manage-data.collections.py
@@ -786,9 +786,9 @@
client.collections.create(
"Article",
# highlight-start
+ # Async replication runs by default when the replication factor is greater than 1
replication_config=Configure.replication(
factor=3,
- async_enabled=True,
),
# highlight-end
)
@@ -819,10 +819,8 @@
# highlight-start
replication_config=Configure.replication(
factor=3,
- async_enabled=True,
deletion_strategy=ReplicationDeletionStrategy.TIME_BASED_RESOLUTION,
async_config=Configure.Replication.async_config(
- max_workers=5,
hashtree_height=16,
frequency=30,
),
@@ -840,7 +838,6 @@
collection.config.update(
replication_config=Reconfigure.replication(
async_config=Reconfigure.Replication.async_config(
- max_workers=10,
frequency=60,
),
),
@@ -851,7 +848,6 @@
# Test
collection = client.collections.use("Article")
config = collection.config.get()
-assert config.replication_config.async_enabled == True
assert (
config.replication_config.deletion_strategy
== ReplicationDeletionStrategy.TIME_BASED_RESOLUTION
diff --git a/_includes/code/howto/manage-data.collections.ts b/_includes/code/howto/manage-data.collections.ts
index b12ce8e1c..8418cf75a 100644
--- a/_includes/code/howto/manage-data.collections.ts
+++ b/_includes/code/howto/manage-data.collections.ts
@@ -677,9 +677,9 @@ import { configure } from 'weaviate-client';
await client.collections.create({
name: 'Article',
// highlight-start
+ // Async replication runs by default when the replication factor is greater than 1
replication: configure.replication({
factor: 1,
- asyncEnabled: true,
}),
// highlight-end
})
@@ -710,10 +710,8 @@ await replicationClient.collections.create({
// highlight-start
replication: configure.replication({
factor: 3,
- asyncEnabled: true,
deletionStrategy: 'TimeBasedResolution',
asyncConfig: {
- maxWorkers: 5,
hashtreeHeight: 16,
frequency: 30,
},
@@ -729,7 +727,6 @@ const articleReplication = replicationClient.collections.use('Article')
await articleReplication.config.update({
replication: reconfigure.replication({
asyncConfig: {
- maxWorkers: 10,
frequency: 60,
},
}),
diff --git a/_includes/code/howto/search.boost.py b/_includes/code/howto/search.boost.py
new file mode 100644
index 000000000..cb91f51d9
--- /dev/null
+++ b/_includes/code/howto/search.boost.py
@@ -0,0 +1,240 @@
+# How-to: Search > Boost results — Python examples.
+#
+# Requires Weaviate v1.38+ and the Python client release that adds Boost
+# support (PR weaviate/weaviate-python-client#2030). Boost is gRPC-only —
+# REST/curl is not supported.
+#
+# Uses the text2vec-transformers vectorizer. Run against the local stack
+# in tests/docker-compose-anon.yml (Weaviate + transformers inference).
+
+import time
+from datetime import datetime, timedelta, timezone
+
+import weaviate
+from weaviate.classes.config import Configure, DataType, Property, Tokenization
+from weaviate.classes.query import Boost, Filter
+
+client = weaviate.connect_to_local()
+
+# ---- Fixture: an Articles collection with date + numeric properties ----
+client.collections.delete("Articles")
+client.collections.create(
+ name="Articles",
+ vector_config=Configure.Vectors.text2vec_transformers(),
+ properties=[
+ Property(name="title", data_type=DataType.TEXT),
+ Property(name="category", data_type=DataType.TEXT, tokenization=Tokenization.FIELD),
+ Property(name="published", data_type=DataType.DATE),
+ Property(name="likes", data_type=DataType.INT),
+ Property(name="price", data_type=DataType.NUMBER),
+ Property(name="draft", data_type=DataType.BOOL),
+ ],
+)
+
+now = datetime.now(timezone.utc)
+articles = client.collections.use("Articles")
+articles.data.insert_many([
+ {"title": "Transformers explained", "category": "research", "published": now - timedelta(days=2), "likes": 100, "price": 49.99, "draft": False},
+ {"title": "Old transformer survey", "category": "research", "published": now - timedelta(days=400), "likes": 5000, "price": 49.99, "draft": False},
+ {"title": "How to fine-tune a model", "category": "tutorial", "published": now - timedelta(days=1), "likes": 30, "price": 9.99, "draft": False},
+ {"title": "Pricing transformers", "category": "tutorial", "published": now - timedelta(days=10), "likes": 5000000, "price": 199.0, "draft": False},
+ {"title": "Draft: transformer architecture","category": "research", "published": now - timedelta(days=3), "likes": 200, "price": 9.99, "draft": True},
+])
+
+# Wait briefly for the vectorizer to finish indexing the new objects.
+time.sleep(3)
+
+
+# ==========================================
+# ===== Filter boost (soft WHERE) =====
+# ==========================================
+
+# START BoostFilter
+# Promote articles in the "research" category without filtering others out.
+response = articles.query.near_text(
+ query="transformer architectures",
+ limit=5,
+ # highlight-start
+ boost=Boost.filter(
+ Filter.by_property("category").equal("research"),
+ weight=0.5,
+ ),
+ # highlight-end
+ return_properties=["title", "category"],
+)
+
+for o in response.objects:
+ print(o.properties["category"], "-", o.properties["title"])
+# END BoostFilter
+
+assert response.objects[0].properties["category"] == "research"
+
+
+# ==========================================
+# ===== Property boost (numeric value) =====
+# ==========================================
+
+# START BoostProperty
+# Bias toward articles with more `likes`. LOG1P dampens the long tail so a
+# single 5-million-likes outlier doesn't dominate.
+response = articles.query.near_text(
+ query="transformer architectures",
+ limit=5,
+ # highlight-start
+ boost=Boost.property(
+ "likes",
+ modifier=Boost.Modifier.LOG1P,
+ weight=0.7,
+ ),
+ # highlight-end
+ return_properties=["title", "likes"],
+)
+
+for o in response.objects:
+ print(o.properties["likes"], "-", o.properties["title"])
+# END BoostProperty
+
+
+# ==========================================
+# ===== Time decay (boost recent docs) =====
+# ==========================================
+
+# START BoostTimeDecay
+# Score decays exponentially over time. "30d scale" + decay=0.5 means an
+# article that's 30 days old gets half the score of one published "now".
+response = articles.query.near_text(
+ query="transformer architectures",
+ limit=5,
+ # highlight-start
+ boost=Boost.time_decay(
+ "published",
+ origin="now",
+ scale=timedelta(days=30),
+ curve=Boost.Curve.EXPONENTIAL,
+ decay=0.5,
+ weight=0.6,
+ ),
+ # highlight-end
+ return_properties=["title", "published"],
+)
+# END BoostTimeDecay
+
+# The 400-day-old "Old transformer survey" should be demoted vs the 2-day-old article.
+top_titles = [o.properties["title"] for o in response.objects[:2]]
+assert "Old transformer survey" not in top_titles
+
+
+# ==========================================
+# ===== Numeric decay (closest to a value) =====
+# ==========================================
+
+# START BoostNumericDecay
+# Score peaks at a target price and falls off symmetrically. Gauss gives a
+# bell-shaped falloff: items within `offset` of $49.99 score 1.0, items at
+# $59.99 (one scale away) score `decay`.
+response = articles.query.near_text(
+ query="transformer architectures",
+ limit=5,
+ # highlight-start
+ boost=Boost.numeric_decay(
+ "price",
+ origin=49.99,
+ scale=10.0,
+ curve=Boost.Curve.GAUSSIAN,
+ decay=0.5,
+ weight=0.5,
+ ),
+ # highlight-end
+ return_properties=["title", "price"],
+)
+# END BoostNumericDecay
+
+# Both top-2 results have the target price; the $199 outlier is pushed down.
+assert all(o.properties["price"] == 49.99 for o in response.objects[:2])
+
+
+# ==========================================
+# ===== Blend multiple conditions =====
+# ==========================================
+
+# START BoostBlend
+# Combine two soft signals: recency (weight 2) + popularity (weight 1).
+# The outer weight=0.4 controls how much the blended rank affects the
+# final score; the inner weights are *per-condition* and balance each
+# other.
+response = articles.query.near_text(
+ query="transformer architectures",
+ limit=5,
+ # highlight-start
+ boost=Boost.blend(
+ Boost.time_decay("published", origin="now", scale=timedelta(days=30), weight=2.0),
+ Boost.property("likes", modifier=Boost.Modifier.LOG1P, weight=1.0),
+ weight=0.4,
+ depth=200, # rescore the top 200 vector matches
+ ),
+ # highlight-end
+ return_properties=["title", "likes", "published"],
+)
+# END BoostBlend
+
+# Recency dominates, but the LOG1P-dampened 5M-likes article still surfaces
+# in the top 3 — popularity is helping, not invisible.
+assert any(o.properties["likes"] == 5_000_000 for o in response.objects[:3])
+
+
+# ==========================================
+# ===== Negative weights demote =====
+# ==========================================
+
+# START BoostNegativeWeight
+# A negative per-condition weight pushes matching documents DOWN — they
+# stay in the result set but lose ground against everything else. Use
+# this to deprioritize drafts without filtering them out entirely.
+response = articles.query.bm25(
+ query="transformer",
+ limit=5,
+ # highlight-start
+ boost=Boost.blend(
+ Boost.filter(Filter.by_property("draft").equal(True), weight=-2.0),
+ weight=0.5,
+ ),
+ # highlight-end
+ return_properties=["title", "draft"],
+)
+
+# The draft article is still in results, just no longer first.
+all_titles = [o.properties["title"] for o in response.objects]
+assert any("Draft" in t for t in all_titles)
+assert response.objects[0].properties["draft"] is False
+# END BoostNegativeWeight
+
+
+# ==========================================
+# ===== Boost on hybrid search =====
+# ==========================================
+
+# START BoostOnHybrid
+# Hybrid keeps its own alpha-blend of BM25 + vector. The boost runs once
+# over the fused hybrid result — the sub-search legs don't see it.
+response = articles.query.hybrid(
+ query="transformer architectures",
+ alpha=0.75,
+ limit=5,
+ # highlight-start
+ boost=Boost.blend(
+ Boost.filter(Filter.by_property("category").equal("research"), weight=1.0),
+ Boost.filter(Filter.by_property("draft").equal(True), weight=-2.0),
+ weight=0.3,
+ ),
+ # highlight-end
+ return_properties=["title", "category", "draft"],
+)
+# END BoostOnHybrid
+
+# Top result is research and not a draft — both boost legs worked.
+assert response.objects[0].properties["category"] == "research"
+assert response.objects[0].properties["draft"] is False
+
+
+client.collections.delete("Articles")
+client.close()
diff --git a/_includes/code/howto/search.filters.nested.py b/_includes/code/howto/search.filters.nested.py
new file mode 100644
index 000000000..1db3c2a22
--- /dev/null
+++ b/_includes/code/howto/search.filters.nested.py
@@ -0,0 +1,162 @@
+# Howto: Search -> Filters on nested object properties - Python examples.
+#
+# Preview feature: requires Weaviate v1.38+ with
+# `WEAVIATE_PREVIEW_NESTED_FILTERING=on` set on the server. Released
+# Weaviate versions reject `cars.make`-style nested paths at the filter
+# parser. Not wired into pytest CI yet — promote at GA.
+
+import weaviate
+from weaviate.classes.config import Configure, Property, DataType, Tokenization
+from weaviate.classes.query import Filter
+
+client = weaviate.connect_to_local()
+
+client.collections.delete("Document")
+
+# Schema: Document.cars (object[]) -> tires (object[]).
+# Mirrors the path patterns used by the worked examples below
+# (cars.make, cars[0].make, cars.tires.width, ...).
+client.collections.create(
+ name="Document",
+ vector_config=Configure.Vectors.self_provided(),
+ inverted_index_config=Configure.inverted_index(index_null_state=True),
+ properties=[
+ Property(name="title", data_type=DataType.TEXT, tokenization=Tokenization.FIELD),
+ Property(
+ name="cars",
+ data_type=DataType.OBJECT_ARRAY,
+ nested_properties=[
+ Property(name="make", data_type=DataType.TEXT, tokenization=Tokenization.FIELD),
+ Property(name="color", data_type=DataType.TEXT, tokenization=Tokenization.FIELD),
+ Property(
+ name="tires",
+ data_type=DataType.OBJECT_ARRAY,
+ nested_properties=[
+ Property(name="brand", data_type=DataType.TEXT, tokenization=Tokenization.FIELD),
+ Property(name="width", data_type=DataType.INT),
+ ],
+ ),
+ ],
+ ),
+ ],
+)
+
+docs = client.collections.use("Document")
+docs.data.insert_many([
+ # Doc 1: two cars; (Toyota, red) + (Honda, blue)
+ {"title": "doc1", "cars": [
+ {"make": "Toyota", "color": "red",
+ "tires": [{"brand": "Bridgestone", "width": 215},
+ {"brand": "Bridgestone", "width": 215}]},
+ {"make": "Honda", "color": "blue",
+ "tires": [{"brand": "Pirelli", "width": 205},
+ {"brand": "Pirelli", "width": 205}]},
+ ]},
+ # Doc 2: one Toyota, no tires
+ {"title": "doc2", "cars": [
+ {"make": "Toyota", "color": "blue"},
+ ]},
+ # Doc 3: one Honda (red) with wide Michelin tires
+ {"title": "doc3", "cars": [
+ {"make": "Honda", "color": "red",
+ "tires": [{"brand": "Michelin", "width": 250},
+ {"brand": "Michelin", "width": 250}]},
+ ]},
+])
+
+
+# ==========================================
+# ===== Existential match (any element) =====
+# ==========================================
+
+# START NestedExistential
+# "any car has make = Toyota" — matches Doc 1 (first car) and Doc 2 (only car)
+response = docs.query.fetch_objects(
+ # highlight-start
+ filters=Filter.by_property("cars.make").equal("Toyota"),
+ # highlight-end
+ return_properties=["title"],
+)
+
+for o in response.objects:
+ print(o.properties)
+# END NestedExistential
+
+assert len(response.objects) == 2
+
+
+# ==========================================
+# ===== Positional match (cars[N]) =====
+# ==========================================
+
+# START NestedPositional
+# "the FIRST car has make = Toyota" — Doc 3's first car is Honda, so it's excluded
+response = docs.query.fetch_objects(
+ # highlight-start
+ filters=Filter.by_property("cars[0].make").equal("Toyota"),
+ # highlight-end
+ return_properties=["title"],
+)
+# END NestedPositional
+
+assert len(response.objects) == 2
+
+
+# ==========================================
+# ===== Same-element AND across leaves =====
+# ==========================================
+
+# START NestedSameElementAnd
+# "the SAME car is both Toyota AND red" — only Doc 1's first car qualifies.
+# Without same-element correlation a doc with separate (Toyota, blue) and
+# (Honda, red) cars would also match, which is wrong.
+response = docs.query.fetch_objects(
+ # highlight-start
+ filters=(
+ Filter.by_property("cars.make").equal("Toyota")
+ & Filter.by_property("cars.color").equal("red")
+ ),
+ # highlight-end
+ return_properties=["title"],
+)
+# END NestedSameElementAnd
+
+assert len(response.objects) == 1
+
+
+# ==========================================
+# ===== Recursive path (object[] inside object[]) =====
+# ==========================================
+
+# START NestedRecursive
+# "any tire on any car is wider than 200" — Doc 1 (215) and Doc 3 (250)
+response = docs.query.fetch_objects(
+ # highlight-start
+ filters=Filter.by_property("cars.tires.width").greater_than(200),
+ # highlight-end
+ return_properties=["title"],
+)
+# END NestedRecursive
+
+assert len(response.objects) == 2
+
+
+# ==========================================
+# ===== IsNull on an intermediate object =====
+# ==========================================
+
+# START NestedIsNull
+# "the first car has no tires" — only the Toyota in Doc 2
+response = docs.query.fetch_objects(
+ # highlight-start
+ filters=Filter.by_property("cars[0].tires").is_none(True),
+ # highlight-end
+ return_properties=["title"],
+)
+# END NestedIsNull
+
+assert len(response.objects) == 1
+
+
+client.collections.delete("Document")
+client.close()
diff --git a/_includes/code/java-v6/src/test/java/ManageCollectionsTest.java b/_includes/code/java-v6/src/test/java/ManageCollectionsTest.java
index f54dd8cef..62cf717d4 100644
--- a/_includes/code/java-v6/src/test/java/ManageCollectionsTest.java
+++ b/_includes/code/java-v6/src/test/java/ManageCollectionsTest.java
@@ -408,12 +408,13 @@ void testReplicationSettings() throws IOException {
@Test
void testAsyncRepair() throws IOException {
// START AsyncRepair
+ // Async replication runs by default when the replication factor is greater than 1
client.collections.create("Article", col -> col.replication(
- Replication.of(rep -> rep.replicationFactor(1).asyncEnabled(true))));
+ Replication.of(rep -> rep.replicationFactor(1))));
// END AsyncRepair
var config = client.collections.getConfig("Article").get();
- assertThat(config.replication().asyncEnabled()).isTrue();
+ assertThat(config.replication().replicationFactor()).isEqualTo(1);
}
@Test
@@ -421,7 +422,6 @@ void testAllReplicationSettings() throws IOException {
// START AllReplicationSettings
threeNodeClient.collections.create("Article",
col -> col.replication(Replication.of(rep -> rep.replicationFactor(3)
- .asyncEnabled(true)
.deletionStrategy(DeletionStrategy.TIME_BASED_RESOLUTION)
.asyncReplication(AsyncReplicationConfig.of(async -> async
.propagationConcurrency(5)
@@ -431,7 +431,6 @@ void testAllReplicationSettings() throws IOException {
var config = threeNodeClient.collections.getConfig("Article").get();
assertThat(config.replication().replicationFactor()).isEqualTo(3);
- assertThat(config.replication().asyncEnabled()).isTrue();
// START UpdateReplicationSettings
var collection = threeNodeClient.collections.use("Article");
diff --git a/_includes/collection-mutable-parameters.mdx b/_includes/collection-mutable-parameters.mdx
index 67fca22d9..9f177f235 100644
--- a/_includes/collection-mutable-parameters.mdx
+++ b/_includes/collection-mutable-parameters.mdx
@@ -14,7 +14,6 @@
- `autoTenantCreation` (introduced in `v1.25.0`)
- `autoTenantActivation` (introduced in `v1.25.2`)
- `replicationConfig`
- - `asyncEnabled` (introduced in `v1.26.0`)
- `factor` (not mutable in `v1.25` or higher)
- `deletionStrategy` (introduced in `v1.27.0`)
- `vectorIndexConfig`
diff --git a/_includes/feature-notes/async-config-collection.mdx b/_includes/feature-notes/async-config-collection.mdx
index fbee348d5..bf705bf63 100644
--- a/_includes/feature-notes/async-config-collection.mdx
+++ b/_includes/feature-notes/async-config-collection.mdx
@@ -1,3 +1,3 @@
:::info Added in `v1.36`
-These per-collection parameters override the corresponding [environment variables](/deploy/configuration/async-rep) at the collection level. If not set, the collection inherits the cluster-wide environment variable defaults.
+The corresponding cluster-wide [environment variables](/deploy/configuration/async-rep) override these per-collection parameters.
:::
diff --git a/_includes/feature-notes/boost.mdx b/_includes/feature-notes/boost.mdx
new file mode 100644
index 000000000..68167df1d
--- /dev/null
+++ b/_includes/feature-notes/boost.mdx
@@ -0,0 +1,3 @@
+:::caution Preview — added in `v1.38`
+This is a preview feature. The API may change in future releases.
+:::
diff --git a/_maintenance/removed-content-cleanup.md b/_maintenance/removed-content-cleanup.md
new file mode 100644
index 000000000..ecd50c81d
--- /dev/null
+++ b/_maintenance/removed-content-cleanup.md
@@ -0,0 +1,27 @@
+# Removed content — cleanup tracking
+
+This file tracks documentation content that describes **removed** Weaviate features,
+environment variables, or configuration fields. Rather than deleting such content the
+moment a feature is removed, we keep it in place with a short "Removed in `vX.Y`" note so
+that users upgrading from an older version can still find the entry and understand what
+happened to it. Once enough releases have passed that few users are upgrading across the
+removal boundary, the noted content should be deleted.
+
+## Policy
+
+- When a feature/env var/config field is removed from Weaviate, **mark** the corresponding
+ docs entry as `Removed in vX.Y` instead of deleting it immediately.
+- Add a row to the table below so the kept-but-stale content can be found and cleaned later.
+- **Suggested cleanup window:** keep the note for roughly three minor releases after the
+ removal, then delete the entry and remove its row here. (Adjust per the supported-version
+ policy at cleanup time — these are guidelines, not hard commitments.)
+
+## Tracked entries
+
+| Page / file | Removed item | Removed in | Suggested cleanup | Notes |
+| --- | --- | --- | --- | --- |
+| `docs/deploy/configuration/env-vars/index.md` | `ASYNC_REPLICATION_CLUSTER_MAX_WORKERS` (table row) | `v1.38` | `v1.41`+ | Replaced by `ASYNC_REPLICATION_SCHEDULER_WORKERS`. |
+| `docs/deploy/configuration/env-vars/index.md` | `ASYNC_REPLICATION_ALIVE_NODES_CHECKING_FREQUENCY` (table row) | `v1.38` | `v1.41`+ | Scheduler no longer polls alive nodes separately. |
+| `docs/deploy/configuration/env-vars/runtime-config.md` | `async_replication_cluster_max_workers` (override mapping row) | `v1.38` | `v1.41`+ | Runtime override removed alongside the env var. |
+| `docs/deploy/configuration/async-rep.md` | "Removed environment variables (v1.38)" ` Cluster Worker Limits
-#### `ASYNC_REPLICATION_CLUSTER_MAX_WORKERS`
-Sets the maximum number of concurrent async replication workers across the entire cluster.
+#### `ASYNC_REPLICATION_SCHEDULER_WORKERS`
+Sets the number of workers in the cluster-wide pool that the async replication scheduler uses to run hashbeat work across all shards and tenants.
-- Its default value is `30`.
-- **Use case**: Limits the total number of concurrent replication workers to prevent resource exhaustion in large clusters with many collections or tenants.
+- Its default value is `10`. The maximum is `100`.
+- **Use case**: A single bounded worker pool replaces the previous per-shard goroutines, so this is the main lever for capping async replication's total concurrency and preventing resource exhaustion on clusters with many collections or tenants.
- **Special Considerations**:
- - This is a cluster-wide cap. Individual collections can set their own `maxWorkers` via the per-collection [`asyncConfig`](/weaviate/config-refs/collections#async-config), but the total across all collections will not exceed this cluster limit.
+ - This is a cluster-wide setting. There is no per-collection worker count; collections share this single pool.
+
+:::note Changed in `v1.38`
+`ASYNC_REPLICATION_SCHEDULER_WORKERS` replaces the removed `ASYNC_REPLICATION_CLUSTER_MAX_WORKERS` environment variable, and the per-collection `maxWorkers` option has been removed.
+:::
+
+#### `ASYNC_REPLICATION_HASHTREE_INIT_CONCURRENCY`
+Sets how many shards may initialize (build) their hash tree concurrently when async replication starts up.
+
+- Its default value is `100`.
+- **Use case**: Bounds the burst of work when many shards begin async replication at once, for example after a node restart or when many replicated collections exist.
+
+ Removed environment variables (v1.38)
+
+These variables were removed when async replication moved to a centralized scheduler in `v1.38`. They are listed here for reference and are no longer read by Weaviate.
+
+#### `ASYNC_REPLICATION_CLUSTER_MAX_WORKERS`
+**Removed in `v1.38`.** Previously set the maximum number of concurrent async replication workers across the cluster (default `30`). Replaced by [`ASYNC_REPLICATION_SCHEDULER_WORKERS`](#async_replication_scheduler_workers).
+
+#### `ASYNC_REPLICATION_ALIVE_NODES_CHECKING_FREQUENCY`
+**Removed in `v1.38`.** Previously defined how often the background process checked for changes in node availability (default `5s`). The scheduler no longer uses a separate alive-nodes polling mechanism.
Node Status Monitoring
-
-#### `ASYNC_REPLICATION_ALIVE_NODES_CHECKING_FREQUENCY`
-Defines the frequency at which the system checks for changes in the availability of nodes within the cluster.
- - Its default value is `5s`. The value requires a time unit suffix (e.g. `5s`, `1m`).
- - **Use Case(s)**: When a node rejoins the cluster after a period of downtime, it is highly likely to be out of sync. This setting ensures that the replication process is initiated promptly.
-
-Timeout Management
diff --git a/docs/deploy/configuration/env-vars/index.md b/docs/deploy/configuration/env-vars/index.md
index 3db54c30e..1a07eca4f 100644
--- a/docs/deploy/configuration/env-vars/index.md
+++ b/docs/deploy/configuration/env-vars/index.md
@@ -67,8 +67,8 @@ import APITable from '@site/src/components/APITable';
| `MAXIMUM_CONCURRENT_BUCKET_LOADS` | Maximum number of buckets that can be loaded concurrently during startup. This is a safeguard to prevent overwhelming the operating system when loading large numbers of collections. Default: `100`
Added in `v1.31.22` | `string - number` | `50` |
| `MAXIMUM_CONCURRENT_SHARD_LOADS` | Maximum number of shards that can be loaded concurrently during startup. This is a safeguard to prevent overwhelming the operating system when loading large numbers of collections. Default: `100` | `string - number` | `50` |
| `MCP_SERVER_CONFIG_PATH` | Path to a YAML file for customizing MCP tool descriptions. Useful for prompt engineering the LLM's understanding of your specific data. If not provided or file malformed, the default descriptions will be used. Default: `""` (default tool descriptions from the [source code](https://github.com/weaviate/weaviate/tree/main/adapters/handlers/mcp) will be used)
Added in `v1.37.1` | `string` | `/etc/weaviate/mcp-config.yaml` |
-| `MCP_SERVER_ENABLED` | Enable the built-in MCP server. When enabled, the MCP endpoint is available at `/v1/mcp` on the REST API port. Default: `false`
Added in `v1.37.1` | `boolean` | `true` |
-| `MCP_SERVER_WRITE_ACCESS_ENABLED` | Enable write tools (`weaviate-objects-upsert`) on the MCP server. When `false`, only read and query tools are available. Default: `false`
Added in `v1.37.1` | `boolean` | `true` |
+| `MCP_SERVER_ENABLED` | Enable the built-in MCP server. When enabled, the MCP endpoint is available at `/v1/mcp` on the REST API port. Default: `false`
Added in `v1.37.1`. [Runtime-configurable](./runtime-config.md#mcp) from `v1.38`. | `boolean` | `true` |
+| `MCP_SERVER_WRITE_ACCESS_ENABLED` | Enable write tools (`weaviate-objects-upsert`) on the MCP server. When `false`, only read and query tools are available. Default: `false`
Added in `v1.37.1`. [Runtime-configurable](./runtime-config.md#mcp) from `v1.38`. | `boolean` | `true` |
| `MEMORY_READONLY_PERCENTAGE` | If memory usage is higher than the given percentage all shards on the affected node will be marked as `READONLY`, meaning all future write requests will fail. (Default: `0` - i.e. no limit) | `string - number` | `75` |
| `MEMORY_WARNING_PERCENTAGE` | If memory usage is higher than the given percentage a warning will be logged by all shards on the affected node's disk. (Default: `0` - i.e. no limit) | `string - number` | `85` |
| `MODULES_CLIENT_TIMEOUT` | Timeout for requests to Weaviate modules. Default: `50s` | `string - duration` | `5s`, `10m`, `1h` |
@@ -90,6 +90,7 @@ import APITable from '@site/src/components/APITable';
| `PERSISTENCE_LSM_MAX_SEGMENT_SIZE` | Maximum size of a segment in the [LSM store](/weaviate/concepts/storage.md#object-and-inverted-index-store). Set this to limit disk usage spikes during compaction to ~2x the segment size. Default: no limit | `string` | `4GiB` (IEC units), `4GB` (SI units), `4000000000` (bytes) |
| `PROMETHEUS_MONITORING_ENABLED` | If set, Weaviate collects [metrics in a Prometheus-compatible format](/deploy/configuration/monitoring.md) | `boolean` | `false` |
| `PROMETHEUS_MONITORING_GROUP` | If set, Weaviate groups metrics for the same class across all shards. | `boolean` | `true` |
+| `QUERY_BOOST_DEFAULT_DEPTH` | Default candidate-pool size used when a [Boost](/weaviate/search/boost.md) query does not set its own `depth`. The primary search retrieves this many candidates before the boost rescorer runs. Must be a positive integer and is hard-capped by `QUERY_MAXIMUM_RESULTS`. Default: `100`
Added in `v1.38` | `string - number` | `200` |
| `QUERY_CROSS_REFERENCE_DEPTH_LIMIT` | Sets the maximum depth of cross-references to be resolved in a query. Defaults to 5. | `string - number` | `3` |
| `QUERY_DEFAULTS_LIMIT` | Sets the default number of objects to be returned in a query. | `string - number` | `25`
Defaults to `10`|
| `QUERY_MAXIMUM_RESULTS` | Sets the maximum total number of objects that can be retrieved. | `string - number` | `10000` |
@@ -225,9 +226,7 @@ For more information on authentication and authorization, see the [Authenticatio
| `RAFT_BOOTSTRAP_EXPECT` | The number of voter notes at bootstrapping time | `string - number` | `1` |
| `RAFT_BOOTSTRAP_TIMEOUT` | The time in seconds to wait for the cluster to bootstrap | `string - number` | `90` |
| `RAFT_DRAIN_SLEEP` | Grace period before shutdown to allow ongoing operations to complete. (Default: `200ms`) | `string - number` | `2s` |
-| `RAFT_ENABLE_FQDN_RESOLVER` | If `true`, use DNS lookup instead of memberlist lookup for Raft. Removed in `v1.30`. ([Read more](/weaviate/concepts/cluster.md#node-discovery)) | `boolean` | `true` |
| `RAFT_ENABLE_ONE_NODE_RECOVERY` | Enable running the single node recovery routine on restart. This is useful if the default hostname has changed and a single node cluster believes there are supposed to be two nodes. | `boolean` | `false` |
-| `RAFT_FQDN_RESOLVER_TLD` | The top-level domain to use for DNS lookup, in `[node-id].[tld]` format. Removed in `v1.30`. ([Read more](/weaviate/concepts/cluster.md#node-discovery)) | `string` | `example.com` |
| `RAFT_GRPC_MESSAGE_MAX_SIZE` | The maximum internal raft gRPC message size in bytes. Defaults to 1073741824 | `string - number` | `1073741824` |
| `RAFT_JOIN` | Manually set Raft voter nodes. If set, RAFT_BOOTSTRAP_EXPECT needs to be adjusted manually to match the number of Raft voters. | `string` | `weaviate-0,weaviate-1` |
| `RAFT_METADATA_ONLY_VOTERS` | If `true`, voter nodes only handle the schema. They do not accept any data. | `boolean` | `false` |
@@ -250,12 +249,14 @@ For more information on authentication and authorization, see the [Authenticatio
| Variable | Description | Type | Example Value |
| --- | --- | --- | --- |
-| `ASYNC_REPLICATION_DISABLED` | Disable async replication. Default: `false` | `boolean` | `false` |
-| `ASYNC_REPLICATION_CLUSTER_MAX_WORKERS` | Maximum concurrent async replication workers across the cluster. Default: `30` | `string - number` | `10` |
+| `ASYNC_REPLICATION_DISABLED` | Disable async replication cluster-wide. When `false` (default), async replication runs automatically for any collection with a replication factor greater than `1`. Default: `false` | `boolean` | `false` |
+| `ASYNC_REPLICATION_SCHEDULER_WORKERS` | Number of workers in the cluster-wide pool that run async replication work across all shards and tenants. Added in `v1.38`, replacing `ASYNC_REPLICATION_CLUSTER_MAX_WORKERS`. Default: `10`, Max: `100`
[Read more.](/deploy/configuration/async-rep.md#async_replication_scheduler_workers) | `string - number` | `10` |
+| `ASYNC_REPLICATION_HASHTREE_INIT_CONCURRENCY` | Number of shards that may build their hash tree concurrently when async replication starts up. Added in `v1.38`. Default: `100`
[Read more.](/deploy/configuration/async-rep.md#async_replication_hashtree_init_concurrency) | `string - number` | `100` |
+| `ASYNC_REPLICATION_CLUSTER_MAX_WORKERS` | **Removed in `v1.38`.** Previously set the maximum number of concurrent async replication workers across the cluster. Replaced by `ASYNC_REPLICATION_SCHEDULER_WORKERS`. | `string - number` | `30` |
| `ASYNC_REPLICATION_HASHTREE_HEIGHT` | Height of the hash tree used for data comparison between nodes. If the height is `0` each node will store just one digest per shard. Default: `16` (single-tenant) / `10` (multi-tenant), Min: `0`, Max: `20`
[Read more about potentially increased memory consumption.](/weaviate/concepts/replication-architecture/consistency#memory-and-performance-considerations-for-async-replication) | `string - number` | `10` |
| `ASYNC_REPLICATION_FREQUENCY` | Frequency of periodic data comparison between nodes. Default: `30s` | `string - duration` | `60s` |
| `ASYNC_REPLICATION_FREQUENCY_WHILE_PROPAGATING` | Frequency of data comparison between nodes while propagation is active. Default: `3s` | `string - duration` | `5s` |
-| `ASYNC_REPLICATION_ALIVE_NODES_CHECKING_FREQUENCY` | Frequency of how often the background process checks for changes in the availability of nodes. Default: `5s` | `string - duration` | `20s` |
+| `ASYNC_REPLICATION_ALIVE_NODES_CHECKING_FREQUENCY` | **Removed in `v1.38`.** Previously set how often the background process checked for changes in node availability. No longer used by the async replication scheduler. | `string - duration` | `5s` |
| `ASYNC_REPLICATION_LOGGING_FREQUENCY` | Frequency of how often the background process logs any events. Default: `60s` | `string - duration` | `7s` |
| `ASYNC_REPLICATION_DIFF_BATCH_SIZE` | Specifies the batch size for comparing digest information between nodes. Default: `1000`, Min: `1`, Max: `10000` |`string - number` | `2000` |
| `ASYNC_REPLICATION_DIFF_PER_NODE_TIMEOUT` | Defines the time limit a node has to provide a comparison response. Default: `10s` | `string - duration` | `30s` |
diff --git a/docs/deploy/configuration/env-vars/runtime-config.md b/docs/deploy/configuration/env-vars/runtime-config.md
index f0c294cdf..69c118e55 100644
--- a/docs/deploy/configuration/env-vars/runtime-config.md
+++ b/docs/deploy/configuration/env-vars/runtime-config.md
@@ -57,7 +57,9 @@ The following overrides are currently supported:
| Runtime override name | Environment variable name |
| :----------------------------------------------- | :------------------------------------------- |
| `async_replication_disabled` | `ASYNC_REPLICATION_DISABLED` |
-| `async_replication_cluster_max_workers` | `ASYNC_REPLICATION_CLUSTER_MAX_WORKERS` |
+| `async_replication_scheduler_workers` | `ASYNC_REPLICATION_SCHEDULER_WORKERS` |
+| `async_replication_hashtree_init_concurrency` | `ASYNC_REPLICATION_HASHTREE_INIT_CONCURRENCY`|
+| `async_replication_cluster_max_workers` _(removed in `v1.38`)_ | `ASYNC_REPLICATION_CLUSTER_MAX_WORKERS` _(removed in `v1.38`)_ |
| `autoschema_enabled` | `AUTOSCHEMA_ENABLED` |
| `default_quantization` | `DEFAULT_QUANTIZATION` |
| `default_sharding_count` | `DEFAULT_SHARDING_COUNT` |
@@ -114,6 +116,17 @@ The following overrides are currently supported:
| `authentication_oidc_skip_client_id_check` | `AUTHENTICATION_OIDC_SKIP_CLIENT_ID_CHECK` |
| `authentication_oidc_username_claim` | `AUTHENTICATION_OIDC_USERNAME_CLAIM` |
+### MCP
+
+Added in `v1.38`. Toggling these at runtime does not require a cluster restart — the HTTP handlers stay registered and per-request checks pick up the new value. See [MCP server — Toggle without restart](/weaviate/configuration/mcp-server.mdx#toggle-without-restart) for behavior details.
+
+| Runtime override name | Environment variable name |
+| :-------------------------------- | :---------------------------------- |
+| `mcp_server_enabled` | `MCP_SERVER_ENABLED` |
+| `mcp_server_write_access_enabled` | `MCP_SERVER_WRITE_ACCESS_ENABLED` |
+
+`MCP_SERVER_CONFIG_PATH` is intentionally **not** runtime-configurable — tool descriptions are baked into the tool schemas at registration.
+
Refer to the [Environment variables](./index.md) page for descriptions on each configuration option
## Operation and monitoring
diff --git a/docs/deploy/configuration/horizontal-scaling.mdx b/docs/deploy/configuration/horizontal-scaling.mdx
index 2b6b9c44b..e1e8a64c5 100644
--- a/docs/deploy/configuration/horizontal-scaling.mdx
+++ b/docs/deploy/configuration/horizontal-scaling.mdx
@@ -338,7 +338,6 @@ curl \
],
"replicationConfig": {
"factor": 3,
- "asyncEnabled": true,
"deletionStrategy": "TimeBasedResolution"
}
}' \
diff --git a/docs/deploy/configuration/monitoring.md b/docs/deploy/configuration/monitoring.md
index a954c13f7..4ecf2dc15 100644
--- a/docs/deploy/configuration/monitoring.md
+++ b/docs/deploy/configuration/monitoring.md
@@ -64,7 +64,8 @@ Be aware that metrics do not follow the semantic versioning guidelines of other
:::
-The list of metrics that are obtainable through Weaviate's metric system is constantly being expanded. The complete list of metrics can be found in the source code files:
+The list of metrics that are obtainable through Weaviate's metric system is constantly being expanded. The complete list of metrics can be found in the source code files:
+
- [`usecases/monitoring/prometheus.go`](https://github.com/weaviate/weaviate/blob/main/usecases/monitoring/prometheus.go)
- [`usecases/replica/metrics.go`](https://github.com/weaviate/weaviate/blob/main/usecases/replica/metrics.go)
- [`adapters/repos/db/metrics.go`](https://github.com/weaviate/weaviate/blob/main/adapters/repos/db/metrics.go)
@@ -270,7 +271,7 @@ These metrics track Write-Ahead Log (WAL) recovery operations during startup.
| `lsm_bucket_wal_recovery_failure_count` | Number of failed LSM bucket WAL recoveries | `strategy` | `Counter` |
| `lsm_bucket_wal_recovery_duration_seconds` | Duration of LSM bucket WAL recovery in seconds | `strategy` | `Histogram` |
-### Schema & cluster consensus
+### Schema & cluster consensus
#### Schema & RAFT consensus
@@ -356,17 +357,17 @@ These metrics track Write-Ahead Log (WAL) recovery operations during startup.
#### Backup & restore
-| Metric | Description | Labels | Type |
-| --------------------------------- | --------------------------------------------------------- | ---------------------------- | --------- |
-| `backup_restore_ms` | Duration of a backup restore | `backend_name`, `class_name` | `Summary` |
-| `backup_restore_class_ms` | Duration restoring class | `class_name` | `Summary` |
-| `backup_restore_init_ms` | Startup phase of a backup restore | `backend_name`, `class_name` | `Summary` |
-| `backup_restore_from_backend_ms` | File transfer stage of a backup restore | `backend_name`, `class_name` | `Summary` |
-| `backup_store_to_backend_ms` | File transfer stage of a backup store | `backend_name`, `class_name` | `Summary` |
-| `bucket_pause_durations_ms` | Bucket pause durations | `bucket_dir` | `Summary` |
-| `backup_restore_data_transferred` | Total number of bytes transferred during a backup restore | `backend_name`, `class_name` | `Counter` |
-| `backup_store_data_transferred` | Total number of bytes transferred during a backup store | `backend_name`, `class_name` | `Counter` |
-| `weaviate_restore_phase_duration_seconds` | Duration of restore phases (prepare, object_storage_download, schema_apply) | `phase` | `Histogram` |
+| Metric | Description | Labels | Type |
+| ----------------------------------------- | --------------------------------------------------------------------------- | ---------------------------- | ----------- |
+| `backup_restore_ms` | Duration of a backup restore | `backend_name`, `class_name` | `Summary` |
+| `backup_restore_class_ms` | Duration restoring class | `class_name` | `Summary` |
+| `backup_restore_init_ms` | Startup phase of a backup restore | `backend_name`, `class_name` | `Summary` |
+| `backup_restore_from_backend_ms` | File transfer stage of a backup restore | `backend_name`, `class_name` | `Summary` |
+| `backup_store_to_backend_ms` | File transfer stage of a backup store | `backend_name`, `class_name` | `Summary` |
+| `bucket_pause_durations_ms` | Bucket pause durations | `bucket_dir` | `Summary` |
+| `backup_restore_data_transferred` | Total number of bytes transferred during a backup restore | `backend_name`, `class_name` | `Counter` |
+| `backup_store_data_transferred` | Total number of bytes transferred during a backup store | `backend_name`, `class_name` | `Counter` |
+| `weaviate_restore_phase_duration_seconds` | Duration of restore phases (prepare, object_storage_download, schema_apply) | `phase` | `Histogram` |
#### Shard management
@@ -438,9 +439,16 @@ These metrics track Write-Ahead Log (WAL) recovery operations during startup.
These metrics track asynchronous replication operations for maintaining data consistency across replicas.
+:::note Changed in `v1.38`
+As of Weaviate `v1.38`, async replication runs through a centralized scheduler with a bounded worker pool (replacing the previous per-shard goroutines). The `async_replication_scheduler_*` metrics below are new in `v1.38`, and the `async_replication_goroutines_running` metric has been removed.
+:::
+
| Metric | Description | Labels | Type |
| -------------------------------------------------------- | ------------------------------------------------------------------------ | ------------------------------------- | ----------- |
-| `async_replication_goroutines_running` | Number of currently running async replication goroutines | `type` (hashbeater, hashbeat_trigger) | `Gauge` |
+| `async_replication_scheduler_worker_pool_size` | Current target size of the scheduler worker pool | None | `Gauge` |
+| `async_replication_scheduler_workers_active` | Number of scheduler worker goroutines currently executing a hashbeat cycle | None | `Gauge` |
+| `async_replication_scheduler_shards_registered` | Number of shards currently registered with the async replication scheduler | None | `Gauge` |
+| `async_replication_scheduler_queue_depth` | Number of shards waiting in the scheduler heap (not in-flight) | None | `Gauge` |
| `async_replication_hashtree_init_count` | Count of async replication hashtree initializations | None | `Counter` |
| `async_replication_hashtree_init_running` | Number of currently running hashtree initializations | None | `Gauge` |
| `async_replication_hashtree_init_failure_count` | Count of async replication hashtree initialization failures | None | `Counter` |
@@ -451,10 +459,13 @@ These metrics track asynchronous replication operations for maintaining data con
| `async_replication_iteration_running` | Number of currently running async replication iterations | None | `Gauge` |
| `async_replication_hashtree_diff_duration_seconds` | Duration of async replication hashtree diff computation in seconds | None | `Histogram` |
| `async_replication_object_digests_diff_duration_seconds` | Duration of async replication object digests diff computation in seconds | None | `Histogram` |
+| `async_replication_objects_diff_total` | Total objects found in diff per hashbeat cycle (queued for propagation or locally deleted before propagation) | None | `Counter` |
| `async_replication_propagation_count` | Count of async replication propagation executions | None | `Counter` |
| `async_replication_propagation_failure_count` | Count of async replication propagation failures | None | `Counter` |
| `async_replication_propagation_object_count` | Count of objects propagated by async replication | None | `Counter` |
| `async_replication_propagation_duration_seconds` | Duration of async replication propagation in seconds | None | `Histogram` |
+| `async_replication_local_deletions_total` | Total local object deletions applied due to remote-deleted verdicts or deletion-conflict responses during async replication | None | `Counter` |
+| `async_replication_reconcile_failures_total` | Number of indices that failed to reconcile async replication with the global `ASYNC_REPLICATION_DISABLED` flag | None | `Counter` |
#### Replication coordinator
@@ -474,6 +485,26 @@ These metrics track the replication coordinator's read and write operations acro
| `replication_coordinator_reads_duration_seconds` | Duration in seconds of read operations from replicas | None | `Histogram` |
| `replication_read_repair_duration_seconds` | Duration in seconds of read repair operations | None | `Histogram` |
+### MCP server
+
+Added in `v1.38`. These metrics track tool traffic, latency, auth failures, and the live state of the runtime write-access flag for the built-in [Weaviate MCP server](/weaviate/configuration/mcp-server.mdx).
+
+| Metric | Description | Labels | Type |
+| ----------------------------------------- | ------------------------------------------------------------------------------------------------- | ---------------- | ----------- |
+| `weaviate_mcp_tool_calls_total` | Total MCP tool invocations. | `tool`, `status` | `Counter` |
+| `weaviate_mcp_tool_call_duration_seconds` | Latency of MCP tool calls (LatencyBuckets histogram). | `tool`, `status` | `Histogram` |
+| `weaviate_mcp_tool_calls_inflight` | In-flight MCP tool calls per tool. Catches one slow tool starving the rest. | `tool` | `Gauge` |
+| `weaviate_mcp_auth_failures_total` | MCP authentication and authorization failures. | `reason` | `Counter` |
+| `weaviate_mcp_tools_listed_total` | `tools/list` calls, labeled with whether the write tool was visible in the response. | `write_access` | `Counter` |
+| `weaviate_mcp_write_access_enabled` | Live state of `MCP_SERVER_WRITE_ACCESS_ENABLED`, polled at scrape time. Reflects runtime toggles. | None | `Gauge` |
+
+Label values:
+
+- **`tool`** — the MCP tool name (e.g. `weaviate-query-hybrid`, `weaviate-objects-upsert`).
+- **`status`** — `success` · `error` · `denied` · `write_disabled`. `denied` covers authorization failures classified via the `Forbidden` / `Unauthenticated` error families. `write_disabled` is emitted when a write call hits the runtime guard.
+- **`reason`** — `missing_token` · `invalid_token` · `forbidden` · `unauthenticated`. `missing_token` and `invalid_token` are detected at the principal-extraction step; `forbidden` and `unauthenticated` are detected at authorization time.
+- **`write_access`** — `enabled` / `disabled`, matching the live state of `MCP_SERVER_WRITE_ACCESS_ENABLED` at the time of the `tools/list` call.
+
---
diff --git a/docs/deploy/configuration/replication.md b/docs/deploy/configuration/replication.md
index c1cfca1ba..989a1ed99 100644
--- a/docs/deploy/configuration/replication.md
+++ b/docs/deploy/configuration/replication.md
@@ -48,7 +48,7 @@ When Weaviate detects inconsistent data across nodes, it attempts to repair the
Weaviate offers [async replication](/weaviate/concepts/replication-architecture/consistency.md#async-replication) to proactively detect inconsistencies. In earlier versions, Weaviate uses a [repair-on-read](/weaviate/concepts/replication-architecture/consistency.md#repair-on-read) strategy to repair inconsistencies at read time.
-Repair-on-read is automatic. To activate async replication, set `asyncEnabled` to true in the `replicationConfig` section of your collection definition.
+Repair-on-read is automatic. As of Weaviate `v1.38`, async replication is also **enabled by default** for any collection with a replication factor greater than `1` — there is no longer a per-collection flag to switch it on. To turn it off cluster-wide, set the [`ASYNC_REPLICATION_DISABLED`](/deploy/configuration/env-vars/index.md#async-replication) environment variable to `true`. The `replicationConfig` section is used to set the replication factor and to fine-tune async replication via `asyncConfig`:
import ReplicationConfigWithAsyncRepair from '/\_includes/code/configuration/replication-consistency.mdx';
@@ -64,8 +64,10 @@ Update the following [environment variables](/deploy/configuration/env-vars/inde
#### Worker limits
-- **Set the cluster-wide worker cap:** `ASYNC_REPLICATION_CLUSTER_MAX_WORKERS`
- Set the maximum number of concurrent async replication workers across the entire cluster. Default: `30`. Individual collections can set their own `maxWorkers` via `asyncConfig`, but the total across all collections will not exceed this cluster limit.
+- **Set the scheduler worker pool size:** `ASYNC_REPLICATION_SCHEDULER_WORKERS`
+ Set the number of workers in the cluster-wide pool that run async replication work across all shards and tenants. Default: `10`, maximum: `100`. As of `v1.38` this replaces the removed `ASYNC_REPLICATION_CLUSTER_MAX_WORKERS` variable and the per-collection `maxWorkers` option; collections share this single pool.
+- **Set hash tree init concurrency:** `ASYNC_REPLICATION_HASHTREE_INIT_CONCURRENCY`
+ Set how many shards may build their hash tree concurrently when async replication starts up. Default: `100`.
#### Logging
@@ -78,13 +80,15 @@ Update the following [environment variables](/deploy/configuration/env-vars/inde
Define how often each node compares its local data with other nodes.
- **Set comparison timeout:** `ASYNC_REPLICATION_DIFF_PER_NODE_TIMEOUT`
Optionally configure a timeout for how long to wait during comparison when a node is unresponsive.
-- **Monitor node availability:** `ASYNC_REPLICATION_ALIVE_NODES_CHECKING_FREQUENCY`
- Trigger comparisons whenever there’s a change in node availability.
- **Configure hash tree height:** `ASYNC_REPLICATION_HASHTREE_HEIGHT`
Specify the size of the hash tree, which helps narrow down data differences by comparing hash digests at multiple levels instead of scanning entire datasets. See [this page](/weaviate/concepts/replication-architecture/consistency.md#memory-and-performance-considerations-for-async-replication) for more information on the memory and performance considerations for async replication.
- **Batch size for digest comparison:** `ASYNC_REPLICATION_DIFF_BATCH_SIZE`
Define the number of objects whose digest (e.g., last update time) is compared between nodes before propagating actual objects.
+:::note Removed in `v1.38`
+The `ASYNC_REPLICATION_CLUSTER_MAX_WORKERS` (replaced by `ASYNC_REPLICATION_SCHEDULER_WORKERS`) and `ASYNC_REPLICATION_ALIVE_NODES_CHECKING_FREQUENCY` environment variables were removed in `v1.38` when async replication moved to a centralized scheduler.
+:::
+
#### Data synchronization
Once differences between nodes are detected, Weaviate propagates outdated or missing data. Configure synchronization as follows:
diff --git a/docs/weaviate/api/graphql/filters.md b/docs/weaviate/api/graphql/filters.md
index 855339515..7055aa676 100644
--- a/docs/weaviate/api/graphql/filters.md
+++ b/docs/weaviate/api/graphql/filters.md
@@ -149,7 +149,7 @@ Starting with `v1.12.0` you can configure your own [stopword lists for the inver
## Multiple operands
-You can set multiple operands or [nest conditions](../../search/filters.md#nested-filters).
+You can set multiple operands or [combine conditions with `And` / `Or`](../../search/filters.md#combine-filters-with-and-or-or).
:::tip
You can filter datetimes similarly to numbers, with the `valueDate` given as `string` in [RFC3339](https://datatracker.ietf.org/doc/rfc3339/) format.
@@ -472,6 +472,61 @@ import GraphQLFiltersWhereBeaconCount from '/_includes/code/graphql.filters.wher
FQDN for node discovery
-
-:::caution Removed in `v1.30`
-
-This was an experimental feature. Use with caution.
-
-:::
-
-There can be a situation where IP-address based node discovery is not optimal. In such cases, you can set `RAFT_ENABLE_FQDN_RESOLVER` and `RAFT_FQDN_RESOLVER_TLD` [environment variables](/deploy/configuration/env-vars/index.md#multi-node-instances) to enable fully qualified domain name (FQDN) based node discovery.
-
-If this feature is enabled, Weaviate uses the FQDN resolver to resolve the node name to the node IP address for metadata (e.g., Raft) communication.
-
-:::info FQDN: For metadata changes only
-This feature is only used for metadata changes which [use Raft as the consensus mechanism](./replication-architecture/cluster-architecture.md#metadata-replication-raft). It does not affect data read/write operations.
-:::
-
-#### Examples of when to use FQDN for node discovery
-
-The use of FQDN can resolve a situation where if IP addresses are re-used across different clusters, the nodes in one cluster could mistakenly discover nodes in another cluster.
-
-It can also be useful when using services (for example, Kubernetes) where the IP of the services is different from the actual node IP, but it proxies the connection to the node.
-
-#### Environment variables for FQDN node discovery
-
-`RAFT_ENABLE_FQDN_RESOLVER` is a Boolean flag. This flag enables the FQDN resolver. If set to `true`, Weaviate uses the FQDN resolver to resolve the node name to the node IP address. If set to `false`, Weaviate uses the memberlist lookup to resolve the node name to the node IP address. The default value is `false`.
-
-`RAFT_FQDN_RESOLVER_TLD` is a string that is appended in the format `[node-id].[tld]` when resolving a node-id to an IP address, where `[tld]` is the top-level domain.
-
-To use this feature, set `RAFT_ENABLE_FQDN_RESOLVER` to `true`.
-
-Pre-
-
-In versions `v1.8.0`-`v1.18.0`, users could not specify the node-affinity of a specific shard or replication shard.
-
-Shards were assigned to 'live' nodes in a round-robin fashion starting with a random node.
-
-v1.18.1 behavior
If you are a Kubernetes user, see the [`1.25 migration guide`](/deploy/migration/weaviate-1-25.md) before you upgrade. To upgrade, you have to delete your existing StatefulSet.
diff --git a/docs/weaviate/concepts/replication-architecture/cluster-architecture.md b/docs/weaviate/concepts/replication-architecture/cluster-architecture.md
index 53ac6f211..0a9b08d2d 100644
--- a/docs/weaviate/concepts/replication-architecture/cluster-architecture.md
+++ b/docs/weaviate/concepts/replication-architecture/cluster-architecture.md
@@ -17,33 +17,6 @@ By default, Weaviate nodes in a cluster use a gossip-like protocol through [Hash
Weaviate is optimized to run on Kubernetes, especially when operating as a cluster. The [Weaviate Helm chart](/deploy/installation-guides/k8s-installation.md#weaviate-helm-chart) makes use of a `StatefulSet` and a headless `Service` that automatically configures node discovery.
-FQDN for node discovery
-
-There can be a situation where IP-address based node discovery is not optimal. In such cases, you can set `RAFT_ENABLE_FQDN_RESOLVER` and `RAFT_FQDN_RESOLVER_TLD` [environment variables](/deploy/configuration/env-vars/index.md#multi-node-instances) to enable [fully qualified domain name (FQDN)](https://en.wikipedia.org/wiki/Fully_qualified_domain_name) based node discovery.
-
-If this feature is enabled, Weaviate uses the FQDN resolver to resolve the node name to the node IP address for metadata (e.g., Raft) communication.
-
-:::info FQDN: For metadata changes only
-This feature is only used for metadata changes which use Raft as the consensus mechanism. It does not affect data read/write operations.
-:::
-
-#### Examples of when to use FQDN for node discovery
-
-The use of FQDN can resolve a situation where if IP addresses are re-used across different clusters, the nodes in one cluster could mistakenly discover nodes in another cluster.
-
-It can also be useful when using services (for example, Kubernetes) where the IP of the services is different from the actual node IP, but it proxies the connection to the node.
-
-#### Environment variables for FQDN node discovery
-
-`RAFT_ENABLE_FQDN_RESOLVER` is a Boolean flag. This flag enables the FQDN resolver. If set to `true`, Weaviate uses the FQDN resolver to resolve the node name to the node IP address. If set to `false`, Weaviate uses the memberlist lookup to resolve the node name to the node IP address. The default value is `false`.
-
-`RAFT_FQDN_RESOLVER_TLD` is a string that is appended in the format `[node-id].[tld]` when resolving a node-id to an IP address, where `[tld]` is the top-level domain.
-
-To use this feature, set `RAFT_ENABLE_FQDN_RESOLVER` to `true`.
-
-
