diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index adf5fd76..4dedeaeb 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "2.4.1"
+ ".": "2.5.0"
}
\ No newline at end of file
diff --git a/.stats.yml b/.stats.yml
index 7ec9737b..dd607793 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
configured_endpoints: 14
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/turbopuffer-benesch/turbopuffer-c47147eabda459ce775ffbab42a531bb3f2778c4cce15f88e05a4eed97013a21.yml
-openapi_spec_hash: a981d1b889452ec8c0108ee0ec69dc6d
-config_hash: 068c4c3b2277de182691cc65c1e50a7c
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/turbopuffer-benesch/turbopuffer-45f4429a37dcc469269006f05ea38ee3973087f22456b1cdb60b682902997372.yml
+openapi_spec_hash: a606206d2ac6886fe31c3392a1f590ea
+config_hash: d3fc3b93270f7ee8ae68224dafcfeb6d
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d50fa76e..aceb03aa 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,19 @@
# Changelog
+## 2.5.0 (2026-07-03)
+
+Full Changelog: [v2.4.1...v2.5.0](https://github.com/turbopuffer/turbopuffer-java/compare/v2.4.1...v2.5.0)
+
+### Features
+
+* spec: add sharding config to the openapi spec ([80ab39d](https://github.com/turbopuffer/turbopuffer-java/commit/80ab39dc6139b94e054f2863f610a7a13aa58b8e))
+* support late_interaction parameter in sdks ([3ad0e51](https://github.com/turbopuffer/turbopuffer-java/commit/3ad0e51302ae70de600465b800ac9398bb055b62))
+
+
+### Chores
+
+* fix import ordering in generated tests ([#247](https://github.com/turbopuffer/turbopuffer-java/issues/247)) ([1e75734](https://github.com/turbopuffer/turbopuffer-java/commit/1e75734881ca2b8042608ace48450880a5dd03fd))
+
## 2.4.1 (2026-06-25)
Full Changelog: [v2.4.0...v2.4.1](https://github.com/turbopuffer/turbopuffer-java/compare/v2.4.0...v2.4.1)
diff --git a/README.md b/README.md
index 02484ea7..f139bca3 100644
--- a/README.md
+++ b/README.md
@@ -2,8 +2,8 @@
-[](https://central.sonatype.com/artifact/com.turbopuffer/turbopuffer-java/2.4.1)
-[](https://javadoc.io/doc/com.turbopuffer/turbopuffer-java/2.4.1)
+[](https://central.sonatype.com/artifact/com.turbopuffer/turbopuffer-java/2.5.0)
+[](https://javadoc.io/doc/com.turbopuffer/turbopuffer-java/2.5.0)
@@ -13,7 +13,7 @@ It is generated with [Stainless](https://www.stainless.com/).
-The HTTP API documentation can be found at [turbopuffer.com/docs/api-overview](https://turbopuffer.com/docs/api-overview). Javadocs are available on [javadoc.io](https://javadoc.io/doc/com.turbopuffer/turbopuffer-java/2.4.1).
+The HTTP API documentation can be found at [turbopuffer.com/docs/api-overview](https://turbopuffer.com/docs/api-overview). Javadocs are available on [javadoc.io](https://javadoc.io/doc/com.turbopuffer/turbopuffer-java/2.5.0).
@@ -24,7 +24,7 @@ The HTTP API documentation can be found at [turbopuffer.com/docs/api-overview](h
### Gradle
```kotlin
-implementation("com.turbopuffer:turbopuffer-java:2.4.1")
+implementation("com.turbopuffer:turbopuffer-java:2.5.0")
```
### Maven
@@ -33,7 +33,7 @@ implementation("com.turbopuffer:turbopuffer-java:2.4.1")
com.turbopuffer
turbopuffer-java
- 2.4.1
+ 2.5.0
```
diff --git a/build.gradle.kts b/build.gradle.kts
index 86e833f5..2bd71d69 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -8,7 +8,7 @@ repositories {
allprojects {
group = "com.turbopuffer"
- version = "2.4.1" // x-release-please-version
+ version = "2.5.0" // x-release-please-version
}
subprojects {
diff --git a/turbopuffer-java-core/src/main/kotlin/com/turbopuffer/models/namespaces/AttributeSchemaConfig.kt b/turbopuffer-java-core/src/main/kotlin/com/turbopuffer/models/namespaces/AttributeSchemaConfig.kt
index 1503b678..14ba1aa7 100644
--- a/turbopuffer-java-core/src/main/kotlin/com/turbopuffer/models/namespaces/AttributeSchemaConfig.kt
+++ b/turbopuffer-java-core/src/main/kotlin/com/turbopuffer/models/namespaces/AttributeSchemaConfig.kt
@@ -729,6 +729,7 @@ private constructor(
@JsonCreator(mode = JsonCreator.Mode.DISABLED)
private constructor(
private val distanceMetric: JsonField,
+ private val lateInteraction: JsonField,
private val additionalProperties: MutableMap,
) {
@@ -736,8 +737,11 @@ private constructor(
private constructor(
@JsonProperty("distance_metric")
@ExcludeMissing
- distanceMetric: JsonField = JsonMissing.of()
- ) : this(distanceMetric, mutableMapOf())
+ distanceMetric: JsonField = JsonMissing.of(),
+ @JsonProperty("late_interaction")
+ @ExcludeMissing
+ lateInteraction: JsonField = JsonMissing.of(),
+ ) : this(distanceMetric, lateInteraction, mutableMapOf())
/**
* A function used to calculate vector similarity.
@@ -748,6 +752,17 @@ private constructor(
fun distanceMetric(): Optional =
distanceMetric.getOptional("distance_metric")
+ /**
+ * Opt in to late-interaction (MUVERA) indexing. Only valid on fixed-dim `[][N]f32`
+ * vector array attributes, and is required to enable an ANN index on such attributes.
+ * Defaults to `false`.
+ *
+ * @throws TurbopufferInvalidDataException if the JSON field has an unexpected type
+ * (e.g. if the server responded with an unexpected value).
+ */
+ fun lateInteraction(): Optional =
+ lateInteraction.getOptional("late_interaction")
+
/**
* Returns the raw JSON value of [distanceMetric].
*
@@ -758,6 +773,16 @@ private constructor(
@ExcludeMissing
fun _distanceMetric(): JsonField = distanceMetric
+ /**
+ * Returns the raw JSON value of [lateInteraction].
+ *
+ * Unlike [lateInteraction], this method doesn't throw if the JSON field has an
+ * unexpected type.
+ */
+ @JsonProperty("late_interaction")
+ @ExcludeMissing
+ fun _lateInteraction(): JsonField = lateInteraction
+
@JsonAnySetter
private fun putAdditionalProperty(key: String, value: JsonValue) {
additionalProperties.put(key, value)
@@ -780,11 +805,13 @@ private constructor(
class Builder internal constructor() {
private var distanceMetric: JsonField = JsonMissing.of()
+ private var lateInteraction: JsonField = JsonMissing.of()
private var additionalProperties: MutableMap = mutableMapOf()
@JvmSynthetic
internal fun from(annConfig: AnnConfig) = apply {
distanceMetric = annConfig.distanceMetric
+ lateInteraction = annConfig.lateInteraction
additionalProperties = annConfig.additionalProperties.toMutableMap()
}
@@ -803,6 +830,25 @@ private constructor(
this.distanceMetric = distanceMetric
}
+ /**
+ * Opt in to late-interaction (MUVERA) indexing. Only valid on fixed-dim `[][N]f32`
+ * vector array attributes, and is required to enable an ANN index on such
+ * attributes. Defaults to `false`.
+ */
+ fun lateInteraction(lateInteraction: Boolean) =
+ lateInteraction(JsonField.of(lateInteraction))
+
+ /**
+ * Sets [Builder.lateInteraction] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.lateInteraction] with a well-typed [Boolean]
+ * value instead. This method is primarily for setting the field to an undocumented
+ * or not yet supported value.
+ */
+ fun lateInteraction(lateInteraction: JsonField) = apply {
+ this.lateInteraction = lateInteraction
+ }
+
fun additionalProperties(additionalProperties: Map) = apply {
this.additionalProperties.clear()
putAllAdditionalProperties(additionalProperties)
@@ -831,7 +877,7 @@ private constructor(
* Further updates to this [Builder] will not mutate the returned instance.
*/
fun build(): AnnConfig =
- AnnConfig(distanceMetric, additionalProperties.toMutableMap())
+ AnnConfig(distanceMetric, lateInteraction, additionalProperties.toMutableMap())
}
private var validated: Boolean = false
@@ -852,6 +898,7 @@ private constructor(
}
distanceMetric().ifPresent { it.validate() }
+ lateInteraction()
validated = true
}
@@ -870,7 +917,9 @@ private constructor(
* Used for best match union deserialization.
*/
@JvmSynthetic
- internal fun validity(): Int = (distanceMetric.asKnown().getOrNull()?.validity() ?: 0)
+ internal fun validity(): Int =
+ (distanceMetric.asKnown().getOrNull()?.validity() ?: 0) +
+ (if (lateInteraction.asKnown().isPresent) 1 else 0)
override fun equals(other: Any?): Boolean {
if (this === other) {
@@ -879,15 +928,18 @@ private constructor(
return other is AnnConfig &&
distanceMetric == other.distanceMetric &&
+ lateInteraction == other.lateInteraction &&
additionalProperties == other.additionalProperties
}
- private val hashCode: Int by lazy { Objects.hash(distanceMetric, additionalProperties) }
+ private val hashCode: Int by lazy {
+ Objects.hash(distanceMetric, lateInteraction, additionalProperties)
+ }
override fun hashCode(): Int = hashCode
override fun toString() =
- "AnnConfig{distanceMetric=$distanceMetric, additionalProperties=$additionalProperties}"
+ "AnnConfig{distanceMetric=$distanceMetric, lateInteraction=$lateInteraction, additionalProperties=$additionalProperties}"
}
}
diff --git a/turbopuffer-java-core/src/main/kotlin/com/turbopuffer/models/namespaces/CustomTypes.kt b/turbopuffer-java-core/src/main/kotlin/com/turbopuffer/models/namespaces/CustomTypes.kt
index 3e459744..638a7ade 100644
--- a/turbopuffer-java-core/src/main/kotlin/com/turbopuffer/models/namespaces/CustomTypes.kt
+++ b/turbopuffer-java-core/src/main/kotlin/com/turbopuffer/models/namespaces/CustomTypes.kt
@@ -1106,6 +1106,10 @@ sealed class RankBy() {
@JvmStatic
public fun knn(attr: String, value: List): RankByKnn = RankByKnn.create(attr, value)
+ @JvmStatic
+ public fun knn(attr: String, value: List>): RankByKnnMulti =
+ RankByKnnMulti.create(attr, value)
+
@JvmStatic
public fun knn(attr: String, expr: Expr): RankByKnnExpr = RankByKnnExpr.create(attr, expr)
@@ -1264,6 +1268,25 @@ class RankByKnnExpr private constructor(attr: String, expr: Expr) : RankBy() {
}
}
+@JsonAutoDetect(fieldVisibility = Visibility.ANY)
+@JsonFormat(shape = JsonFormat.Shape.ARRAY)
+@JsonPropertyOrder("attr", "f0", "value")
+class RankByKnnMulti private constructor(attr: String, value: List>) : RankBy() {
+ private val attr: String = attr
+ private val f0: String = "kNN"
+ private val value: List> = value
+
+ override fun toString(): String {
+ return jsonMapper.writeValueAsString(this)
+ }
+
+ companion object {
+ @JvmSynthetic
+ internal fun create(attr: String, value: List>): RankByKnnMulti =
+ RankByKnnMulti(attr, value)
+ }
+}
+
@JsonAutoDetect(fieldVisibility = Visibility.ANY)
@JsonFormat(shape = JsonFormat.Shape.ARRAY)
@JsonPropertyOrder("attr", "f0", "value")
diff --git a/turbopuffer-java-core/src/main/kotlin/com/turbopuffer/models/namespaces/NamespaceMetadata.kt b/turbopuffer-java-core/src/main/kotlin/com/turbopuffer/models/namespaces/NamespaceMetadata.kt
index 2c46d3c6..36bbdff0 100644
--- a/turbopuffer-java-core/src/main/kotlin/com/turbopuffer/models/namespaces/NamespaceMetadata.kt
+++ b/turbopuffer-java-core/src/main/kotlin/com/turbopuffer/models/namespaces/NamespaceMetadata.kt
@@ -42,6 +42,7 @@ private constructor(
private val schema: JsonField,
private val updatedAt: JsonField,
private val pinning: JsonField,
+ private val sharding: JsonField,
private val additionalProperties: MutableMap,
) {
@@ -65,6 +66,9 @@ private constructor(
@ExcludeMissing
updatedAt: JsonField = JsonMissing.of(),
@JsonProperty("pinning") @ExcludeMissing pinning: JsonField = JsonMissing.of(),
+ @JsonProperty("sharding")
+ @ExcludeMissing
+ sharding: JsonField = JsonMissing.of(),
) : this(
approxLogicalBytes,
approxRowCount,
@@ -74,6 +78,7 @@ private constructor(
schema,
updatedAt,
pinning,
+ sharding,
mutableMapOf(),
)
@@ -139,6 +144,17 @@ private constructor(
*/
fun pinning(): Optional = pinning.getOptional("pinning")
+ /**
+ * Configuration for namespace sharding, which partitions a namespace's documents across
+ * multiple internal shards to scale indexing and query throughput beyond a single machine.
+ * Sharding can only be configured on a namespace's inaugural write, and cannot be added to or
+ * changed on an existing namespace.
+ *
+ * @throws TurbopufferInvalidDataException if the JSON field has an unexpected type (e.g. if the
+ * server responded with an unexpected value).
+ */
+ fun sharding(): Optional = sharding.getOptional("sharding")
+
/**
* Returns the raw JSON value of [approxLogicalBytes].
*
@@ -206,6 +222,13 @@ private constructor(
*/
@JsonProperty("pinning") @ExcludeMissing fun _pinning(): JsonField = pinning
+ /**
+ * Returns the raw JSON value of [sharding].
+ *
+ * Unlike [sharding], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("sharding") @ExcludeMissing fun _sharding(): JsonField = sharding
+
@JsonAnySetter
private fun putAdditionalProperty(key: String, value: JsonValue) {
additionalProperties.put(key, value)
@@ -248,6 +271,7 @@ private constructor(
private var schema: JsonField? = null
private var updatedAt: JsonField? = null
private var pinning: JsonField = JsonMissing.of()
+ private var sharding: JsonField = JsonMissing.of()
private var additionalProperties: MutableMap = mutableMapOf()
@JvmSynthetic
@@ -260,6 +284,7 @@ private constructor(
schema = namespaceMetadata.schema
updatedAt = namespaceMetadata.updatedAt
pinning = namespaceMetadata.pinning
+ sharding = namespaceMetadata.sharding
additionalProperties = namespaceMetadata.additionalProperties.toMutableMap()
}
@@ -376,6 +401,23 @@ private constructor(
*/
fun pinning(pinning: JsonField) = apply { this.pinning = pinning }
+ /**
+ * Configuration for namespace sharding, which partitions a namespace's documents across
+ * multiple internal shards to scale indexing and query throughput beyond a single machine.
+ * Sharding can only be configured on a namespace's inaugural write, and cannot be added to
+ * or changed on an existing namespace.
+ */
+ fun sharding(sharding: ShardingConfig) = sharding(JsonField.of(sharding))
+
+ /**
+ * Sets [Builder.sharding] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.sharding] with a well-typed [ShardingConfig] value
+ * instead. This method is primarily for setting the field to an undocumented or not yet
+ * supported value.
+ */
+ fun sharding(sharding: JsonField) = apply { this.sharding = sharding }
+
fun additionalProperties(additionalProperties: Map) = apply {
this.additionalProperties.clear()
putAllAdditionalProperties(additionalProperties)
@@ -423,6 +465,7 @@ private constructor(
checkRequired("schema", schema),
checkRequired("updatedAt", updatedAt),
pinning,
+ sharding,
additionalProperties.toMutableMap(),
)
}
@@ -450,6 +493,7 @@ private constructor(
schema().validate()
updatedAt()
pinning().ifPresent { it.validate() }
+ sharding().ifPresent { it.validate() }
validated = true
}
@@ -475,7 +519,8 @@ private constructor(
(index.asKnown().getOrNull()?.validity() ?: 0) +
(schema.asKnown().getOrNull()?.validity() ?: 0) +
(if (updatedAt.asKnown().isPresent) 1 else 0) +
- (pinning.asKnown().getOrNull()?.validity() ?: 0)
+ (pinning.asKnown().getOrNull()?.validity() ?: 0) +
+ (sharding.asKnown().getOrNull()?.validity() ?: 0)
@JsonDeserialize(using = Index.Deserializer::class)
@JsonSerialize(using = Index.Serializer::class)
@@ -1515,6 +1560,7 @@ private constructor(
schema == other.schema &&
updatedAt == other.updatedAt &&
pinning == other.pinning &&
+ sharding == other.sharding &&
additionalProperties == other.additionalProperties
}
@@ -1528,6 +1574,7 @@ private constructor(
schema,
updatedAt,
pinning,
+ sharding,
additionalProperties,
)
}
@@ -1535,5 +1582,5 @@ private constructor(
override fun hashCode(): Int = hashCode
override fun toString() =
- "NamespaceMetadata{approxLogicalBytes=$approxLogicalBytes, approxRowCount=$approxRowCount, createdAt=$createdAt, encryption=$encryption, index=$index, schema=$schema, updatedAt=$updatedAt, pinning=$pinning, additionalProperties=$additionalProperties}"
+ "NamespaceMetadata{approxLogicalBytes=$approxLogicalBytes, approxRowCount=$approxRowCount, createdAt=$createdAt, encryption=$encryption, index=$index, schema=$schema, updatedAt=$updatedAt, pinning=$pinning, sharding=$sharding, additionalProperties=$additionalProperties}"
}
diff --git a/turbopuffer-java-core/src/main/kotlin/com/turbopuffer/models/namespaces/NamespaceWriteParams.kt b/turbopuffer-java-core/src/main/kotlin/com/turbopuffer/models/namespaces/NamespaceWriteParams.kt
index 01524df5..7ce5c104 100644
--- a/turbopuffer-java-core/src/main/kotlin/com/turbopuffer/models/namespaces/NamespaceWriteParams.kt
+++ b/turbopuffer-java-core/src/main/kotlin/com/turbopuffer/models/namespaces/NamespaceWriteParams.kt
@@ -160,6 +160,17 @@ private constructor(
*/
fun schema(): Optional = body.schema()
+ /**
+ * Configuration for namespace sharding, which partitions a namespace's documents across
+ * multiple internal shards to scale indexing and query throughput beyond a single machine.
+ * Sharding can only be configured on a namespace's inaugural write, and cannot be added to or
+ * changed on an existing namespace.
+ *
+ * @throws TurbopufferInvalidDataException if the JSON field has an unexpected type (e.g. if the
+ * server responded with an unexpected value).
+ */
+ fun sharding(): Optional = body.sharding()
+
/**
* A list of documents in columnar format. Each key is a column name, mapped to an array of
* values for that column.
@@ -296,6 +307,13 @@ private constructor(
*/
fun _schema(): JsonField = body._schema()
+ /**
+ * Returns the raw JSON value of [sharding].
+ *
+ * Unlike [sharding], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ fun _sharding(): JsonField = body._sharding()
+
/**
* Returns the raw JSON value of [upsertColumns].
*
@@ -660,6 +678,23 @@ private constructor(
*/
fun schema(schema: JsonField) = apply { body.schema(schema) }
+ /**
+ * Configuration for namespace sharding, which partitions a namespace's documents across
+ * multiple internal shards to scale indexing and query throughput beyond a single machine.
+ * Sharding can only be configured on a namespace's inaugural write, and cannot be added to
+ * or changed on an existing namespace.
+ */
+ fun sharding(sharding: ShardingConfig) = apply { body.sharding(sharding) }
+
+ /**
+ * Sets [Builder.sharding] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.sharding] with a well-typed [ShardingConfig] value
+ * instead. This method is primarily for setting the field to an undocumented or not yet
+ * supported value.
+ */
+ fun sharding(sharding: JsonField) = apply { body.sharding(sharding) }
+
/**
* A list of documents in columnar format. Each key is a column name, mapped to an array of
* values for that column.
@@ -877,6 +912,7 @@ private constructor(
private val patchRows: JsonField>,
private val returnAffectedIds: JsonField,
private val schema: JsonField,
+ private val sharding: JsonField,
private val upsertColumns: JsonField,
private val upsertCondition: JsonField,
private val upsertRows: JsonField>,
@@ -931,6 +967,9 @@ private constructor(
@ExcludeMissing
returnAffectedIds: JsonField = JsonMissing.of(),
@JsonProperty("schema") @ExcludeMissing schema: JsonField = JsonMissing.of(),
+ @JsonProperty("sharding")
+ @ExcludeMissing
+ sharding: JsonField = JsonMissing.of(),
@JsonProperty("upsert_columns")
@ExcludeMissing
upsertColumns: JsonField = JsonMissing.of(),
@@ -957,6 +996,7 @@ private constructor(
patchRows,
returnAffectedIds,
schema,
+ sharding,
upsertColumns,
upsertCondition,
upsertRows,
@@ -1098,6 +1138,17 @@ private constructor(
*/
fun schema(): Optional = schema.getOptional("schema")
+ /**
+ * Configuration for namespace sharding, which partitions a namespace's documents across
+ * multiple internal shards to scale indexing and query throughput beyond a single machine.
+ * Sharding can only be configured on a namespace's inaugural write, and cannot be added to
+ * or changed on an existing namespace.
+ *
+ * @throws TurbopufferInvalidDataException if the JSON field has an unexpected type (e.g. if
+ * the server responded with an unexpected value).
+ */
+ fun sharding(): Optional = sharding.getOptional("sharding")
+
/**
* A list of documents in columnar format. Each key is a column name, mapped to an array of
* values for that column.
@@ -1268,6 +1319,15 @@ private constructor(
*/
@JsonProperty("schema") @ExcludeMissing fun _schema(): JsonField = schema
+ /**
+ * Returns the raw JSON value of [sharding].
+ *
+ * Unlike [sharding], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("sharding")
+ @ExcludeMissing
+ fun _sharding(): JsonField = sharding
+
/**
* Returns the raw JSON value of [upsertColumns].
*
@@ -1334,6 +1394,7 @@ private constructor(
private var patchRows: JsonField>? = null
private var returnAffectedIds: JsonField = JsonMissing.of()
private var schema: JsonField = JsonMissing.of()
+ private var sharding: JsonField = JsonMissing.of()
private var upsertColumns: JsonField = JsonMissing.of()
private var upsertCondition: JsonField = JsonMissing.of()
private var upsertRows: JsonField>? = null
@@ -1357,6 +1418,7 @@ private constructor(
patchRows = body.patchRows.map { it.toMutableList() }
returnAffectedIds = body.returnAffectedIds
schema = body.schema
+ sharding = body.sharding
upsertColumns = body.upsertColumns
upsertCondition = body.upsertCondition
upsertRows = body.upsertRows.map { it.toMutableList() }
@@ -1676,6 +1738,23 @@ private constructor(
*/
fun schema(schema: JsonField) = apply { this.schema = schema }
+ /**
+ * Configuration for namespace sharding, which partitions a namespace's documents across
+ * multiple internal shards to scale indexing and query throughput beyond a single
+ * machine. Sharding can only be configured on a namespace's inaugural write, and cannot
+ * be added to or changed on an existing namespace.
+ */
+ fun sharding(sharding: ShardingConfig) = sharding(JsonField.of(sharding))
+
+ /**
+ * Sets [Builder.sharding] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.sharding] with a well-typed [ShardingConfig] value
+ * instead. This method is primarily for setting the field to an undocumented or not yet
+ * supported value.
+ */
+ fun sharding(sharding: JsonField) = apply { this.sharding = sharding }
+
/**
* A list of documents in columnar format. Each key is a column name, mapped to an array
* of values for that column.
@@ -1778,6 +1857,7 @@ private constructor(
(patchRows ?: JsonMissing.of()).map { it.toImmutable() },
returnAffectedIds,
schema,
+ sharding,
upsertColumns,
upsertCondition,
(upsertRows ?: JsonMissing.of()).map { it.toImmutable() },
@@ -1814,6 +1894,7 @@ private constructor(
patchRows()
returnAffectedIds()
schema().ifPresent { it.values.forEach { it.validate() } }
+ sharding().ifPresent { it.validate() }
upsertColumns()
upsertRows()
validated = true
@@ -1848,6 +1929,7 @@ private constructor(
(patchRows.asKnown().getOrNull()?.size ?: 0) +
(if (returnAffectedIds.asKnown().isPresent) 1 else 0) +
(schema.asKnown().getOrNull()?.values?.sumOf { it.validity() } ?: 0) +
+ (sharding.asKnown().getOrNull()?.validity() ?: 0) +
(upsertColumns.asKnown().getOrNull()?.values?.sumOf { it.size } ?: 0) +
(upsertRows.asKnown().getOrNull()?.size ?: 0)
@@ -1873,6 +1955,7 @@ private constructor(
patchRows == other.patchRows &&
returnAffectedIds == other.returnAffectedIds &&
schema == other.schema &&
+ sharding == other.sharding &&
upsertColumns == other.upsertColumns &&
upsertCondition == other.upsertCondition &&
upsertRows == other.upsertRows &&
@@ -1897,6 +1980,7 @@ private constructor(
patchRows,
returnAffectedIds,
schema,
+ sharding,
upsertColumns,
upsertCondition,
upsertRows,
@@ -1907,7 +1991,7 @@ private constructor(
override fun hashCode(): Int = hashCode
override fun toString() =
- "Body{branchFromNamespace=$branchFromNamespace, copyFromNamespace=$copyFromNamespace, deleteByFilter=$deleteByFilter, deleteByFilterAllowPartial=$deleteByFilterAllowPartial, deleteCondition=$deleteCondition, deletes=$deletes, disableBackpressure=$disableBackpressure, distanceMetric=$distanceMetric, encryption=$encryption, patchByFilter=$patchByFilter, patchByFilterAllowPartial=$patchByFilterAllowPartial, patchColumns=$patchColumns, patchCondition=$patchCondition, patchRows=$patchRows, returnAffectedIds=$returnAffectedIds, schema=$schema, upsertColumns=$upsertColumns, upsertCondition=$upsertCondition, upsertRows=$upsertRows, additionalProperties=$additionalProperties}"
+ "Body{branchFromNamespace=$branchFromNamespace, copyFromNamespace=$copyFromNamespace, deleteByFilter=$deleteByFilter, deleteByFilterAllowPartial=$deleteByFilterAllowPartial, deleteCondition=$deleteCondition, deletes=$deletes, disableBackpressure=$disableBackpressure, distanceMetric=$distanceMetric, encryption=$encryption, patchByFilter=$patchByFilter, patchByFilterAllowPartial=$patchByFilterAllowPartial, patchColumns=$patchColumns, patchCondition=$patchCondition, patchRows=$patchRows, returnAffectedIds=$returnAffectedIds, schema=$schema, sharding=$sharding, upsertColumns=$upsertColumns, upsertCondition=$upsertCondition, upsertRows=$upsertRows, additionalProperties=$additionalProperties}"
}
/** The patch and filter specifying which documents to patch. */
diff --git a/turbopuffer-java-core/src/main/kotlin/com/turbopuffer/models/namespaces/ShardingConfig.kt b/turbopuffer-java-core/src/main/kotlin/com/turbopuffer/models/namespaces/ShardingConfig.kt
new file mode 100644
index 00000000..624c8a52
--- /dev/null
+++ b/turbopuffer-java-core/src/main/kotlin/com/turbopuffer/models/namespaces/ShardingConfig.kt
@@ -0,0 +1,187 @@
+// File generated from our OpenAPI spec by Stainless.
+
+package com.turbopuffer.models.namespaces
+
+import com.fasterxml.jackson.annotation.JsonAnyGetter
+import com.fasterxml.jackson.annotation.JsonAnySetter
+import com.fasterxml.jackson.annotation.JsonCreator
+import com.fasterxml.jackson.annotation.JsonProperty
+import com.turbopuffer.core.ExcludeMissing
+import com.turbopuffer.core.JsonField
+import com.turbopuffer.core.JsonMissing
+import com.turbopuffer.core.JsonValue
+import com.turbopuffer.core.checkRequired
+import com.turbopuffer.errors.TurbopufferInvalidDataException
+import java.util.Collections
+import java.util.Objects
+
+/**
+ * Configuration for namespace sharding, which partitions a namespace's documents across multiple
+ * internal shards to scale indexing and query throughput beyond a single machine. Sharding can only
+ * be configured on a namespace's inaugural write, and cannot be added to or changed on an existing
+ * namespace.
+ */
+class ShardingConfig
+@JsonCreator(mode = JsonCreator.Mode.DISABLED)
+private constructor(
+ private val numShards: JsonField,
+ private val additionalProperties: MutableMap,
+) {
+
+ @JsonCreator
+ private constructor(
+ @JsonProperty("num_shards") @ExcludeMissing numShards: JsonField = JsonMissing.of()
+ ) : this(numShards, mutableMapOf())
+
+ /**
+ * The number of shards to partition the namespace into.
+ *
+ * @throws TurbopufferInvalidDataException if the JSON field has an unexpected type or is
+ * unexpectedly missing or null (e.g. if the server responded with an unexpected value).
+ */
+ fun numShards(): Int = numShards.getRequired("num_shards")
+
+ /**
+ * Returns the raw JSON value of [numShards].
+ *
+ * Unlike [numShards], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("num_shards") @ExcludeMissing fun _numShards(): JsonField = numShards
+
+ @JsonAnySetter
+ private fun putAdditionalProperty(key: String, value: JsonValue) {
+ additionalProperties.put(key, value)
+ }
+
+ @JsonAnyGetter
+ @ExcludeMissing
+ fun _additionalProperties(): Map =
+ Collections.unmodifiableMap(additionalProperties)
+
+ fun toBuilder() = Builder().from(this)
+
+ companion object {
+
+ /**
+ * Returns a mutable builder for constructing an instance of [ShardingConfig].
+ *
+ * The following fields are required:
+ * ```java
+ * .numShards()
+ * ```
+ */
+ @JvmStatic fun builder() = Builder()
+ }
+
+ /** A builder for [ShardingConfig]. */
+ class Builder internal constructor() {
+
+ private var numShards: JsonField? = null
+ private var additionalProperties: MutableMap = mutableMapOf()
+
+ @JvmSynthetic
+ internal fun from(shardingConfig: ShardingConfig) = apply {
+ numShards = shardingConfig.numShards
+ additionalProperties = shardingConfig.additionalProperties.toMutableMap()
+ }
+
+ /** The number of shards to partition the namespace into. */
+ fun numShards(numShards: Int) = numShards(JsonField.of(numShards))
+
+ /**
+ * Sets [Builder.numShards] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.numShards] with a well-typed [Int] value instead. This
+ * method is primarily for setting the field to an undocumented or not yet supported value.
+ */
+ fun numShards(numShards: JsonField) = apply { this.numShards = numShards }
+
+ fun additionalProperties(additionalProperties: Map) = apply {
+ this.additionalProperties.clear()
+ putAllAdditionalProperties(additionalProperties)
+ }
+
+ fun putAdditionalProperty(key: String, value: JsonValue) = apply {
+ additionalProperties.put(key, value)
+ }
+
+ fun putAllAdditionalProperties(additionalProperties: Map) = apply {
+ this.additionalProperties.putAll(additionalProperties)
+ }
+
+ fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) }
+
+ fun removeAllAdditionalProperties(keys: Set) = apply {
+ keys.forEach(::removeAdditionalProperty)
+ }
+
+ /**
+ * Returns an immutable instance of [ShardingConfig].
+ *
+ * Further updates to this [Builder] will not mutate the returned instance.
+ *
+ * The following fields are required:
+ * ```java
+ * .numShards()
+ * ```
+ *
+ * @throws IllegalStateException if any required field is unset.
+ */
+ fun build(): ShardingConfig =
+ ShardingConfig(
+ checkRequired("numShards", numShards),
+ additionalProperties.toMutableMap(),
+ )
+ }
+
+ private var validated: Boolean = false
+
+ /**
+ * Validates that the types of all values in this object match their expected types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws TurbopufferInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
+ fun validate(): ShardingConfig = apply {
+ if (validated) {
+ return@apply
+ }
+
+ numShards()
+ validated = true
+ }
+
+ fun isValid(): Boolean =
+ try {
+ validate()
+ true
+ } catch (e: TurbopufferInvalidDataException) {
+ false
+ }
+
+ /**
+ * Returns a score indicating how many valid values are contained in this object recursively.
+ *
+ * Used for best match union deserialization.
+ */
+ @JvmSynthetic internal fun validity(): Int = (if (numShards.asKnown().isPresent) 1 else 0)
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) {
+ return true
+ }
+
+ return other is ShardingConfig &&
+ numShards == other.numShards &&
+ additionalProperties == other.additionalProperties
+ }
+
+ private val hashCode: Int by lazy { Objects.hash(numShards, additionalProperties) }
+
+ override fun hashCode(): Int = hashCode
+
+ override fun toString() =
+ "ShardingConfig{numShards=$numShards, additionalProperties=$additionalProperties}"
+}
diff --git a/turbopuffer-java-core/src/test/kotlin/com/turbopuffer/models/namespaces/NamespaceMetadataTest.kt b/turbopuffer-java-core/src/test/kotlin/com/turbopuffer/models/namespaces/NamespaceMetadataTest.kt
index 1b6dea03..1c616dc3 100644
--- a/turbopuffer-java-core/src/test/kotlin/com/turbopuffer/models/namespaces/NamespaceMetadataTest.kt
+++ b/turbopuffer-java-core/src/test/kotlin/com/turbopuffer/models/namespaces/NamespaceMetadataTest.kt
@@ -53,6 +53,7 @@ internal class NamespaceMetadataTest {
)
.build()
)
+ .sharding(ShardingConfig.builder().numShards(1).build())
.build()
assertThat(namespaceMetadata.approxLogicalBytes()).isEqualTo(0L)
@@ -102,6 +103,8 @@ internal class NamespaceMetadataTest {
)
.build()
)
+ assertThat(namespaceMetadata.sharding())
+ .contains(ShardingConfig.builder().numShards(1).build())
}
@Test
@@ -147,6 +150,7 @@ internal class NamespaceMetadataTest {
)
.build()
)
+ .sharding(ShardingConfig.builder().numShards(1).build())
.build()
val roundtrippedNamespaceMetadata =
diff --git a/turbopuffer-java-core/src/test/kotlin/com/turbopuffer/models/namespaces/NamespaceWriteParamsTest.kt b/turbopuffer-java-core/src/test/kotlin/com/turbopuffer/models/namespaces/NamespaceWriteParamsTest.kt
index c7b6fdf9..b8c6da69 100644
--- a/turbopuffer-java-core/src/test/kotlin/com/turbopuffer/models/namespaces/NamespaceWriteParamsTest.kt
+++ b/turbopuffer-java-core/src/test/kotlin/com/turbopuffer/models/namespaces/NamespaceWriteParamsTest.kt
@@ -52,6 +52,7 @@ internal class NamespaceWriteParamsTest {
.put("foo", AttributeSchemaConfig.builder().type("string").build())
.build()
)
+ .sharding(ShardingConfig.builder().numShards(1).build())
.upsertColumns(
Columns.builder()
.addId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e")
@@ -121,6 +122,7 @@ internal class NamespaceWriteParamsTest {
.put("foo", AttributeSchemaConfig.builder().type("string").build())
.build()
)
+ .sharding(ShardingConfig.builder().numShards(1).build())
.upsertColumns(
Columns.builder()
.addId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e")
@@ -188,6 +190,7 @@ internal class NamespaceWriteParamsTest {
.put("foo", AttributeSchemaConfig.builder().type("string").build())
.build()
)
+ assertThat(body.sharding()).contains(ShardingConfig.builder().numShards(1).build())
assertThat(body.upsertColumns())
.contains(
Columns.builder()
diff --git a/turbopuffer-java-core/src/test/kotlin/com/turbopuffer/models/namespaces/ShardingConfigTest.kt b/turbopuffer-java-core/src/test/kotlin/com/turbopuffer/models/namespaces/ShardingConfigTest.kt
new file mode 100644
index 00000000..e893d221
--- /dev/null
+++ b/turbopuffer-java-core/src/test/kotlin/com/turbopuffer/models/namespaces/ShardingConfigTest.kt
@@ -0,0 +1,32 @@
+// File generated from our OpenAPI spec by Stainless.
+
+package com.turbopuffer.models.namespaces
+
+import com.fasterxml.jackson.module.kotlin.jacksonTypeRef
+import com.turbopuffer.core.jsonMapper
+import org.assertj.core.api.Assertions.assertThat
+import org.junit.jupiter.api.Test
+
+internal class ShardingConfigTest {
+
+ @Test
+ fun create() {
+ val shardingConfig = ShardingConfig.builder().numShards(1).build()
+
+ assertThat(shardingConfig.numShards()).isEqualTo(1)
+ }
+
+ @Test
+ fun roundtrip() {
+ val jsonMapper = jsonMapper()
+ val shardingConfig = ShardingConfig.builder().numShards(1).build()
+
+ val roundtrippedShardingConfig =
+ jsonMapper.readValue(
+ jsonMapper.writeValueAsString(shardingConfig),
+ jacksonTypeRef(),
+ )
+
+ assertThat(roundtrippedShardingConfig).isEqualTo(shardingConfig)
+ }
+}
diff --git a/turbopuffer-java-core/src/test/kotlin/com/turbopuffer/services/ServiceParamsTest.kt b/turbopuffer-java-core/src/test/kotlin/com/turbopuffer/services/ServiceParamsTest.kt
index bfbd6489..fc9bf95d 100644
--- a/turbopuffer-java-core/src/test/kotlin/com/turbopuffer/services/ServiceParamsTest.kt
+++ b/turbopuffer-java-core/src/test/kotlin/com/turbopuffer/services/ServiceParamsTest.kt
@@ -26,6 +26,7 @@ import com.turbopuffer.models.namespaces.Filter
import com.turbopuffer.models.namespaces.NamespaceWriteParams
import com.turbopuffer.models.namespaces.Row
import com.turbopuffer.models.namespaces.Schema
+import com.turbopuffer.models.namespaces.ShardingConfig
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test
@@ -115,6 +116,7 @@ internal class ServiceParamsTest {
.put("age", AttributeSchemaConfig.builder().type("uint").build())
.build()
)
+ .sharding(ShardingConfig.builder().numShards(1).build())
.upsertColumns(
Columns.builder()
.put("id", listOf("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"))
diff --git a/turbopuffer-java-core/src/test/kotlin/com/turbopuffer/services/async/NamespaceServiceAsyncTest.kt b/turbopuffer-java-core/src/test/kotlin/com/turbopuffer/services/async/NamespaceServiceAsyncTest.kt
index d0e5b649..3550df4e 100644
--- a/turbopuffer-java-core/src/test/kotlin/com/turbopuffer/services/async/NamespaceServiceAsyncTest.kt
+++ b/turbopuffer-java-core/src/test/kotlin/com/turbopuffer/services/async/NamespaceServiceAsyncTest.kt
@@ -25,6 +25,7 @@ import com.turbopuffer.models.namespaces.NamespaceUpdateSchemaParams
import com.turbopuffer.models.namespaces.NamespaceWriteParams
import com.turbopuffer.models.namespaces.Row
import com.turbopuffer.models.namespaces.Schema
+import com.turbopuffer.models.namespaces.ShardingConfig
import com.turbopuffer.models.namespaces.VectorEncoding
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test
@@ -325,6 +326,7 @@ internal class NamespaceServiceAsyncTest {
.put("age", AttributeSchemaConfig.builder().type("uint").build())
.build()
)
+ .sharding(ShardingConfig.builder().numShards(1).build())
.upsertColumns(
Columns.builder()
.put("id", listOf("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"))
diff --git a/turbopuffer-java-core/src/test/kotlin/com/turbopuffer/services/blocking/NamespaceServiceTest.kt b/turbopuffer-java-core/src/test/kotlin/com/turbopuffer/services/blocking/NamespaceServiceTest.kt
index 15ceae9b..65d6bc2e 100644
--- a/turbopuffer-java-core/src/test/kotlin/com/turbopuffer/services/blocking/NamespaceServiceTest.kt
+++ b/turbopuffer-java-core/src/test/kotlin/com/turbopuffer/services/blocking/NamespaceServiceTest.kt
@@ -25,6 +25,7 @@ import com.turbopuffer.models.namespaces.NamespaceUpdateSchemaParams
import com.turbopuffer.models.namespaces.NamespaceWriteParams
import com.turbopuffer.models.namespaces.Row
import com.turbopuffer.models.namespaces.Schema
+import com.turbopuffer.models.namespaces.ShardingConfig
import com.turbopuffer.models.namespaces.VectorEncoding
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test
@@ -307,6 +308,7 @@ internal class NamespaceServiceTest {
.put("age", AttributeSchemaConfig.builder().type("uint").build())
.build()
)
+ .sharding(ShardingConfig.builder().numShards(1).build())
.upsertColumns(
Columns.builder()
.put("id", listOf("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"))