Skip to content

fix(tenants): split tenant updates at the server's limit of 100 - #616

Open
dudanogueira wants to merge 3 commits into
mainfrom
fix/tenant-update-batching
Open

dudanogueira wants to merge 3 commits into
mainfrom
fix/tenant-update-batching

Conversation

@dudanogueira

@dudanogueira dudanogueira commented Aug 28, 2026 •

Copy link
Copy Markdown

Problem

PUT /schema/{class}/tenants rejects more than 100 tenants with HTTP 422, and the client sent the whole list in one request. Updating more than 100 tenants failed, and so did activate, deactivate and offload, which all go through update:

HTTP 422: maximum number of tenants allowed to be updated simultaneously is 100

Fix

  • update(List<Tenant>) sends the tenants in batches of 100, one after another, in both the sync and async clients (the async client chains them with thenCompose).
  • If a batch fails, the batches before it stay applied and the error is propagated. This matches the Python and TypeScript clients.
  • create is not split: the server does not cap the number of tenants added in one request.

Breaking changes

None.

Closes #615

PUT /schema/{class}/tenants rejects more than 100 tenants with HTTP 422,
so updating more than that failed outright -- and with it activate,
deactivate and offload, which all delegate to update. The Python and
TypeScript clients split the request internally, so the same code
written against either of them worked and the Java one did not.

update() now sends the tenants in batches of 100, on the sync and async
clients alike; the async one chains them so they reach the server one
after another rather than all at once. Adding tenants stays a single
request: the server validates it with allowOverHundred=true, so only
updates are capped.

More than one request means a partial update is now possible: if a batch
fails, the tenants of the preceding batches stay updated and the error
propagates. Python and TypeScript behave the same way; it is documented
on both update() methods.

MockRestTransport.performRequestAsync returned null, which no chained
caller could compose on; it returns a completed future now.

Closes #615

Claude-Session: https://claude.ai/code/session_01WmY5dAGWCccWDoqkKNC2JU
@dudanogueira
dudanogueira force-pushed the fix/tenant-update-batching branch from b2ff369 to f8d3db4 Compare August 28, 2026 21:15

@orca-security-eu orca-security-eu Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Orca Security Scan Summary

Status Check Issues by priority
Passed Passed Infrastructure as Code high 0   medium 0   low 0   info 0 View in Orca
Passed Passed SAST high 0   medium 0   low 0   info 0 View in Orca
Passed Passed Secrets high 0   medium 0   low 0   info 0 View in Orca
Passed Passed Vulnerabilities high 0   medium 0   low 0   info 0 View in Orca

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes tenant updates failing with HTTP 422 when more than 100 tenants are updated at once by batching PUT /schema/{class}/tenants requests at the server-enforced limit (100). The batching is centralized so that update, activate, deactivate, and offload all benefit, with both sync and async clients sending batches sequentially.

Changes:

  • Add batching logic (MAX_TENANTS_PER_REQUEST = 100 and batches(...)) to UpdateTenantsRequest.
  • Update sync and async tenants clients to send update batches sequentially, documenting partial-update behavior.
  • Add unit and integration tests covering batching behavior and a mock transport adjustment to enable async chaining tests.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/test/java/io/weaviate/testutil/transport/MockRestTransport.java Returns a completed future for async requests to enable thenCompose chaining in tests.
src/test/java/io/weaviate/client6/v1/api/collections/tenants/UpdateTenantsBatchingTest.java New unit tests validating batch splitting and request counts/bodies for sync + async clients.
src/main/java/io/weaviate/client6/v1/api/collections/tenants/WeaviateTenantsClientAsync.java Chains update batches sequentially via thenCompose; adds Javadoc about batching/partial updates.
src/main/java/io/weaviate/client6/v1/api/collections/tenants/WeaviateTenantsClient.java Sends update batches sequentially in the sync client; adds Javadoc about batching/partial updates.
src/main/java/io/weaviate/client6/v1/api/collections/tenants/UpdateTenantsRequest.java Encodes the server limit (100) and provides batching helper used by both clients.
src/it/java/io/weaviate/integration/TenantsITest.java Adds an integration test validating 250-tenant deactivate/activate works via batching.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The async MockRestTransport change currently drops BooleanEndpoint behavior (can return null where a boolean is expected) and there is a misleading @param Javadoc in the updated public method.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (1)

src/test/java/io/weaviate/testutil/transport/MockRestTransport.java:61

  • performRequestAsync always returns completedFuture(null) and ignores BooleanEndpoint, unlike performRequest and DefaultRestTransport which return a boolean result for boolean endpoints. This can cause async client tests using MockRestTransport (e.g., exists(...)) to see null instead of a boolean.
  @Override
  public <RequestT, ResponseT, ExceptionT> CompletableFuture<ResponseT> performRequestAsync(RequestT request,
      Endpoint<RequestT, ResponseT> endpoint) {
    requests.add(new Request<>(request, endpoint));
    // A completed future rather than null, so callers which chain requests
    // (thenCompose) can be tested against this transport.
    return CompletableFuture.completedFuture(null);
  }
  • Files reviewed: 6/6 changed files
  • Comments generated: 1
  • Review effort level: Lite

- Remove the comments and Javadoc added in the previous commit.
- Copy each batch so later changes to the caller's list cannot reach
  requests that are still in flight.
- Fix the @PARAM of update(List<Tenant>): it takes tenant configurations.
- MockRestTransport.performRequestAsync answers BooleanEndpoint with TRUE,
  like performRequest.

Claude-Session: https://claude.ai/code/session_01VCkN66JyKNd2RQpFE46nCE
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

v6: tenant updates are not split at the server's 100-tenant limit, so activate/deactivate fails above 100

2 participants