diff --git a/autosharding/build.gradle b/autosharding/build.gradle
new file mode 100644
index 00000000000..8c88f6d0e64
--- /dev/null
+++ b/autosharding/build.gradle
@@ -0,0 +1,71 @@
+plugins {
+ id "java-library"
+ id "maven-publish"
+ id "com.google.protobuf"
+ id "jacoco"
+ id "ru.vyarus.animalsniffer"
+}
+
+description = "gRPC: Autosharding Loadbalancing plugin"
+
+tasks.named("jar").configure {
+ manifest {
+ attributes('Automatic-Module-Name': 'io.grpc.autosharding')
+ }
+}
+
+sourceSets {
+ main {
+ proto {
+ srcDir 'third_party/autosharding/src/main/proto'
+ }
+ }
+}
+
+dependencies {
+ implementation project(':grpc-util'),
+ project(':grpc-core'),
+ project(':grpc-protobuf'),
+ project(':grpc-stub'),
+ libraries.auto.value.annotations,
+ libraries.guava
+ annotationProcessor libraries.auto.value
+ testImplementation libraries.truth,
+ project(':grpc-grpclb'),
+ project(':grpc-inprocess'),
+ project(':grpc-testing'),
+ project(':grpc-testing-proto'),
+ testFixtures(project(':grpc-api')),
+ testFixtures(project(':grpc-core'))
+ signature (libraries.signature.java) {
+ artifact {
+ extension = "signature"
+ }
+ }
+}
+
+tasks.named("compileJava").configure {
+ it.options.compilerArgs += [
+ "-Xlint:-processing",
+ ]
+ appendToProperty(
+ it.options.errorprone.excludedPaths,
+ ".*/build/generated/sources/annotationProcessor/java/.*",
+ "|")
+}
+
+tasks.named("javadoc").configure {
+ failOnError = false
+ exclude 'io/grpc/autosharding/v1/**'
+ exclude 'io/grpc/autosharding/*Provider.java'
+ exclude 'io/grpc/autosharding/internal/**'
+ exclude 'io/grpc/autosharding/Internal*'
+}
+
+tasks.named("jacocoTestReport").configure {
+ classDirectories.from = sourceSets.main.output.collect {
+ fileTree(dir: it, exclude: ['**/io/grpc/autosharding/v1/**'])
+ }
+}
+
+configureProtoCompilation()
diff --git a/autosharding/src/generated/main/grpc/com/google/cloud/autosharding/v1/AutoshardingServiceGrpc.java b/autosharding/src/generated/main/grpc/com/google/cloud/autosharding/v1/AutoshardingServiceGrpc.java
new file mode 100644
index 00000000000..908872d03da
--- /dev/null
+++ b/autosharding/src/generated/main/grpc/com/google/cloud/autosharding/v1/AutoshardingServiceGrpc.java
@@ -0,0 +1,473 @@
+package com.google.cloud.autosharding.v1;
+
+import static io.grpc.MethodDescriptor.generateFullMethodName;
+
+/**
+ *
+ * An auto-sharding service that assigns keys in an application's keyspace to
+ * abstract "endpoints", and which uses load information from clients to update
+ * that assignment over time. Assignments and load reports are scoped to an
+ * abstract "slicing target."
+ * Concrete examples of the concepts in this protocol:
+ * Endpoints:
+ * * Application servers
+ * * Pods in a Kubernetes cluster
+ * * Regions in a multi-regional service
+ * Keys:
+ * * User ids
+ * * Tenant ids in a multi-tenant system
+ * Load:
+ * * Request count
+ * * CPU cost of processing requests
+ *
+ */
+@io.grpc.stub.annotations.GrpcGenerated
+public final class AutoshardingServiceGrpc {
+
+ private AutoshardingServiceGrpc() {}
+
+ public static final java.lang.String SERVICE_NAME = "google.cloud.autosharding.v1.AutoshardingService";
+
+ // Static method descriptors that strictly reflect the proto.
+ private static volatile io.grpc.MethodDescriptor getWatchShardingAssignmentMethod;
+
+ @io.grpc.stub.annotations.RpcMethod(
+ fullMethodName = SERVICE_NAME + '/' + "WatchShardingAssignment",
+ requestType = com.google.cloud.autosharding.v1.WatchShardingAssignmentRequest.class,
+ responseType = com.google.cloud.autosharding.v1.WatchShardingAssignmentResponse.class,
+ methodType = io.grpc.MethodDescriptor.MethodType.BIDI_STREAMING)
+ public static io.grpc.MethodDescriptor getWatchShardingAssignmentMethod() {
+ io.grpc.MethodDescriptor getWatchShardingAssignmentMethod;
+ if ((getWatchShardingAssignmentMethod = AutoshardingServiceGrpc.getWatchShardingAssignmentMethod) == null) {
+ synchronized (AutoshardingServiceGrpc.class) {
+ if ((getWatchShardingAssignmentMethod = AutoshardingServiceGrpc.getWatchShardingAssignmentMethod) == null) {
+ AutoshardingServiceGrpc.getWatchShardingAssignmentMethod = getWatchShardingAssignmentMethod =
+ io.grpc.MethodDescriptor.newBuilder()
+ .setType(io.grpc.MethodDescriptor.MethodType.BIDI_STREAMING)
+ .setFullMethodName(generateFullMethodName(SERVICE_NAME, "WatchShardingAssignment"))
+ .setSampledToLocalTracing(true)
+ .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
+ com.google.cloud.autosharding.v1.WatchShardingAssignmentRequest.getDefaultInstance()))
+ .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
+ com.google.cloud.autosharding.v1.WatchShardingAssignmentResponse.getDefaultInstance()))
+ .setSchemaDescriptor(new AutoshardingServiceMethodDescriptorSupplier("WatchShardingAssignment"))
+ .build();
+ }
+ }
+ }
+ return getWatchShardingAssignmentMethod;
+ }
+
+ /**
+ * Creates a new async stub that supports all call types for the service
+ */
+ public static AutoshardingServiceStub newStub(io.grpc.Channel channel) {
+ io.grpc.stub.AbstractStub.StubFactory factory =
+ new io.grpc.stub.AbstractStub.StubFactory() {
+ @java.lang.Override
+ public AutoshardingServiceStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+ return new AutoshardingServiceStub(channel, callOptions);
+ }
+ };
+ return AutoshardingServiceStub.newStub(factory, channel);
+ }
+
+ /**
+ * Creates a new blocking-style stub that supports all types of calls on the service
+ */
+ public static AutoshardingServiceBlockingV2Stub newBlockingV2Stub(
+ io.grpc.Channel channel) {
+ io.grpc.stub.AbstractStub.StubFactory factory =
+ new io.grpc.stub.AbstractStub.StubFactory() {
+ @java.lang.Override
+ public AutoshardingServiceBlockingV2Stub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+ return new AutoshardingServiceBlockingV2Stub(channel, callOptions);
+ }
+ };
+ return AutoshardingServiceBlockingV2Stub.newStub(factory, channel);
+ }
+
+ /**
+ * Creates a new blocking-style stub that supports unary and streaming output calls on the service
+ */
+ public static AutoshardingServiceBlockingStub newBlockingStub(
+ io.grpc.Channel channel) {
+ io.grpc.stub.AbstractStub.StubFactory factory =
+ new io.grpc.stub.AbstractStub.StubFactory() {
+ @java.lang.Override
+ public AutoshardingServiceBlockingStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+ return new AutoshardingServiceBlockingStub(channel, callOptions);
+ }
+ };
+ return AutoshardingServiceBlockingStub.newStub(factory, channel);
+ }
+
+ /**
+ * Creates a new ListenableFuture-style stub that supports unary calls on the service
+ */
+ public static AutoshardingServiceFutureStub newFutureStub(
+ io.grpc.Channel channel) {
+ io.grpc.stub.AbstractStub.StubFactory factory =
+ new io.grpc.stub.AbstractStub.StubFactory() {
+ @java.lang.Override
+ public AutoshardingServiceFutureStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+ return new AutoshardingServiceFutureStub(channel, callOptions);
+ }
+ };
+ return AutoshardingServiceFutureStub.newStub(factory, channel);
+ }
+
+ /**
+ *
+ * An auto-sharding service that assigns keys in an application's keyspace to
+ * abstract "endpoints", and which uses load information from clients to update
+ * that assignment over time. Assignments and load reports are scoped to an
+ * abstract "slicing target."
+ * Concrete examples of the concepts in this protocol:
+ * Endpoints:
+ * * Application servers
+ * * Pods in a Kubernetes cluster
+ * * Regions in a multi-regional service
+ * Keys:
+ * * User ids
+ * * Tenant ids in a multi-tenant system
+ * Load:
+ * * Request count
+ * * CPU cost of processing requests
+ *
+ */
+ public interface AsyncService {
+
+ /**
+ *
+ * Opens a stream over which clients report load and the server delivers
+ * sharding assignments.
+ * A given client may use this stream just to receive assignments or also to
+ * report load, and it may opt in or out of reporting load at any time. (An
+ * implementation of the Autosharding service may impose restrictions on
+ * which clients are allowed to report load, and it may respond to clients
+ * improperly reporting load by ignoring their reports or terminating their
+ * streams with an error.)
+ * The client should keep this stream open at all times and reopen the stream
+ * after it closes, with backoff if the stream closed without delivering any
+ * data.
+ *
+ */
+ default io.grpc.stub.StreamObserver watchShardingAssignment(
+ io.grpc.stub.StreamObserver responseObserver) {
+ return io.grpc.stub.ServerCalls.asyncUnimplementedStreamingCall(getWatchShardingAssignmentMethod(), responseObserver);
+ }
+ }
+
+ /**
+ * Base class for the server implementation of the service AutoshardingService.
+ *
+ * An auto-sharding service that assigns keys in an application's keyspace to
+ * abstract "endpoints", and which uses load information from clients to update
+ * that assignment over time. Assignments and load reports are scoped to an
+ * abstract "slicing target."
+ * Concrete examples of the concepts in this protocol:
+ * Endpoints:
+ * * Application servers
+ * * Pods in a Kubernetes cluster
+ * * Regions in a multi-regional service
+ * Keys:
+ * * User ids
+ * * Tenant ids in a multi-tenant system
+ * Load:
+ * * Request count
+ * * CPU cost of processing requests
+ *
+ */
+ public static abstract class AutoshardingServiceImplBase
+ implements io.grpc.BindableService, AsyncService {
+
+ @java.lang.Override public final io.grpc.ServerServiceDefinition bindService() {
+ return AutoshardingServiceGrpc.bindService(this);
+ }
+ }
+
+ /**
+ * A stub to allow clients to do asynchronous rpc calls to service AutoshardingService.
+ *
+ * An auto-sharding service that assigns keys in an application's keyspace to
+ * abstract "endpoints", and which uses load information from clients to update
+ * that assignment over time. Assignments and load reports are scoped to an
+ * abstract "slicing target."
+ * Concrete examples of the concepts in this protocol:
+ * Endpoints:
+ * * Application servers
+ * * Pods in a Kubernetes cluster
+ * * Regions in a multi-regional service
+ * Keys:
+ * * User ids
+ * * Tenant ids in a multi-tenant system
+ * Load:
+ * * Request count
+ * * CPU cost of processing requests
+ *
+ */
+ public static final class AutoshardingServiceStub
+ extends io.grpc.stub.AbstractAsyncStub {
+ private AutoshardingServiceStub(
+ io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+ super(channel, callOptions);
+ }
+
+ @java.lang.Override
+ protected AutoshardingServiceStub build(
+ io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+ return new AutoshardingServiceStub(channel, callOptions);
+ }
+
+ /**
+ *
+ * Opens a stream over which clients report load and the server delivers
+ * sharding assignments.
+ * A given client may use this stream just to receive assignments or also to
+ * report load, and it may opt in or out of reporting load at any time. (An
+ * implementation of the Autosharding service may impose restrictions on
+ * which clients are allowed to report load, and it may respond to clients
+ * improperly reporting load by ignoring their reports or terminating their
+ * streams with an error.)
+ * The client should keep this stream open at all times and reopen the stream
+ * after it closes, with backoff if the stream closed without delivering any
+ * data.
+ *
+ */
+ public io.grpc.stub.StreamObserver watchShardingAssignment(
+ io.grpc.stub.StreamObserver responseObserver) {
+ return io.grpc.stub.ClientCalls.asyncBidiStreamingCall(
+ getChannel().newCall(getWatchShardingAssignmentMethod(), getCallOptions()), responseObserver);
+ }
+ }
+
+ /**
+ * A stub to allow clients to do synchronous rpc calls to service AutoshardingService.
+ *
+ * An auto-sharding service that assigns keys in an application's keyspace to
+ * abstract "endpoints", and which uses load information from clients to update
+ * that assignment over time. Assignments and load reports are scoped to an
+ * abstract "slicing target."
+ * Concrete examples of the concepts in this protocol:
+ * Endpoints:
+ * * Application servers
+ * * Pods in a Kubernetes cluster
+ * * Regions in a multi-regional service
+ * Keys:
+ * * User ids
+ * * Tenant ids in a multi-tenant system
+ * Load:
+ * * Request count
+ * * CPU cost of processing requests
+ *
+ */
+ public static final class AutoshardingServiceBlockingV2Stub
+ extends io.grpc.stub.AbstractBlockingStub {
+ private AutoshardingServiceBlockingV2Stub(
+ io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+ super(channel, callOptions);
+ }
+
+ @java.lang.Override
+ protected AutoshardingServiceBlockingV2Stub build(
+ io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+ return new AutoshardingServiceBlockingV2Stub(channel, callOptions);
+ }
+
+ /**
+ *
+ * Opens a stream over which clients report load and the server delivers
+ * sharding assignments.
+ * A given client may use this stream just to receive assignments or also to
+ * report load, and it may opt in or out of reporting load at any time. (An
+ * implementation of the Autosharding service may impose restrictions on
+ * which clients are allowed to report load, and it may respond to clients
+ * improperly reporting load by ignoring their reports or terminating their
+ * streams with an error.)
+ * The client should keep this stream open at all times and reopen the stream
+ * after it closes, with backoff if the stream closed without delivering any
+ * data.
+ *
+ */
+ @io.grpc.ExperimentalApi("https://github.com/grpc/grpc-java/issues/10918")
+ public io.grpc.stub.BlockingClientCall
+ watchShardingAssignment() {
+ return io.grpc.stub.ClientCalls.blockingBidiStreamingCall(
+ getChannel(), getWatchShardingAssignmentMethod(), getCallOptions());
+ }
+ }
+
+ /**
+ * A stub to allow clients to do limited synchronous rpc calls to service AutoshardingService.
+ *
+ * An auto-sharding service that assigns keys in an application's keyspace to
+ * abstract "endpoints", and which uses load information from clients to update
+ * that assignment over time. Assignments and load reports are scoped to an
+ * abstract "slicing target."
+ * Concrete examples of the concepts in this protocol:
+ * Endpoints:
+ * * Application servers
+ * * Pods in a Kubernetes cluster
+ * * Regions in a multi-regional service
+ * Keys:
+ * * User ids
+ * * Tenant ids in a multi-tenant system
+ * Load:
+ * * Request count
+ * * CPU cost of processing requests
+ *
+ */
+ public static final class AutoshardingServiceBlockingStub
+ extends io.grpc.stub.AbstractBlockingStub {
+ private AutoshardingServiceBlockingStub(
+ io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+ super(channel, callOptions);
+ }
+
+ @java.lang.Override
+ protected AutoshardingServiceBlockingStub build(
+ io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+ return new AutoshardingServiceBlockingStub(channel, callOptions);
+ }
+ }
+
+ /**
+ * A stub to allow clients to do ListenableFuture-style rpc calls to service AutoshardingService.
+ *
+ * An auto-sharding service that assigns keys in an application's keyspace to
+ * abstract "endpoints", and which uses load information from clients to update
+ * that assignment over time. Assignments and load reports are scoped to an
+ * abstract "slicing target."
+ * Concrete examples of the concepts in this protocol:
+ * Endpoints:
+ * * Application servers
+ * * Pods in a Kubernetes cluster
+ * * Regions in a multi-regional service
+ * Keys:
+ * * User ids
+ * * Tenant ids in a multi-tenant system
+ * Load:
+ * * Request count
+ * * CPU cost of processing requests
+ *
+ */
+ public static final class AutoshardingServiceFutureStub
+ extends io.grpc.stub.AbstractFutureStub {
+ private AutoshardingServiceFutureStub(
+ io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+ super(channel, callOptions);
+ }
+
+ @java.lang.Override
+ protected AutoshardingServiceFutureStub build(
+ io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+ return new AutoshardingServiceFutureStub(channel, callOptions);
+ }
+ }
+
+ private static final int METHODID_WATCH_SHARDING_ASSIGNMENT = 0;
+
+ private static final class MethodHandlers implements
+ io.grpc.stub.ServerCalls.UnaryMethod,
+ io.grpc.stub.ServerCalls.ServerStreamingMethod,
+ io.grpc.stub.ServerCalls.ClientStreamingMethod,
+ io.grpc.stub.ServerCalls.BidiStreamingMethod {
+ private final AsyncService serviceImpl;
+ private final int methodId;
+
+ MethodHandlers(AsyncService serviceImpl, int methodId) {
+ this.serviceImpl = serviceImpl;
+ this.methodId = methodId;
+ }
+
+ @java.lang.Override
+ @java.lang.SuppressWarnings("unchecked")
+ public void invoke(Req request, io.grpc.stub.StreamObserver responseObserver) {
+ switch (methodId) {
+ default:
+ throw new AssertionError();
+ }
+ }
+
+ @java.lang.Override
+ @java.lang.SuppressWarnings("unchecked")
+ public io.grpc.stub.StreamObserver invoke(
+ io.grpc.stub.StreamObserver responseObserver) {
+ switch (methodId) {
+ case METHODID_WATCH_SHARDING_ASSIGNMENT:
+ return (io.grpc.stub.StreamObserver) serviceImpl.watchShardingAssignment(
+ (io.grpc.stub.StreamObserver) responseObserver);
+ default:
+ throw new AssertionError();
+ }
+ }
+ }
+
+ public static final io.grpc.ServerServiceDefinition bindService(AsyncService service) {
+ return io.grpc.ServerServiceDefinition.builder(getServiceDescriptor())
+ .addMethod(
+ getWatchShardingAssignmentMethod(),
+ io.grpc.stub.ServerCalls.asyncBidiStreamingCall(
+ new MethodHandlers<
+ com.google.cloud.autosharding.v1.WatchShardingAssignmentRequest,
+ com.google.cloud.autosharding.v1.WatchShardingAssignmentResponse>(
+ service, METHODID_WATCH_SHARDING_ASSIGNMENT)))
+ .build();
+ }
+
+ private static abstract class AutoshardingServiceBaseDescriptorSupplier
+ implements io.grpc.protobuf.ProtoFileDescriptorSupplier, io.grpc.protobuf.ProtoServiceDescriptorSupplier {
+ AutoshardingServiceBaseDescriptorSupplier() {}
+
+ @java.lang.Override
+ public com.google.protobuf.Descriptors.FileDescriptor getFileDescriptor() {
+ return com.google.cloud.autosharding.v1.AutoshardingProto.getDescriptor();
+ }
+
+ @java.lang.Override
+ public com.google.protobuf.Descriptors.ServiceDescriptor getServiceDescriptor() {
+ return getFileDescriptor().findServiceByName("AutoshardingService");
+ }
+ }
+
+ private static final class AutoshardingServiceFileDescriptorSupplier
+ extends AutoshardingServiceBaseDescriptorSupplier {
+ AutoshardingServiceFileDescriptorSupplier() {}
+ }
+
+ private static final class AutoshardingServiceMethodDescriptorSupplier
+ extends AutoshardingServiceBaseDescriptorSupplier
+ implements io.grpc.protobuf.ProtoMethodDescriptorSupplier {
+ private final java.lang.String methodName;
+
+ AutoshardingServiceMethodDescriptorSupplier(java.lang.String methodName) {
+ this.methodName = methodName;
+ }
+
+ @java.lang.Override
+ public com.google.protobuf.Descriptors.MethodDescriptor getMethodDescriptor() {
+ return getServiceDescriptor().findMethodByName(methodName);
+ }
+ }
+
+ private static volatile io.grpc.ServiceDescriptor serviceDescriptor;
+
+ public static io.grpc.ServiceDescriptor getServiceDescriptor() {
+ io.grpc.ServiceDescriptor result = serviceDescriptor;
+ if (result == null) {
+ synchronized (AutoshardingServiceGrpc.class) {
+ result = serviceDescriptor;
+ if (result == null) {
+ serviceDescriptor = result = io.grpc.ServiceDescriptor.newBuilder(SERVICE_NAME)
+ .setSchemaDescriptor(new AutoshardingServiceFileDescriptorSupplier())
+ .addMethod(getWatchShardingAssignmentMethod())
+ .build();
+ }
+ }
+ }
+ return result;
+ }
+}
diff --git a/autosharding/third_party/autosharding/LICENSE b/autosharding/third_party/autosharding/LICENSE
new file mode 100644
index 00000000000..7260c1bae0d
--- /dev/null
+++ b/autosharding/third_party/autosharding/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright 2017, Google Research.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/autosharding/third_party/autosharding/import.sh b/autosharding/third_party/autosharding/import.sh
new file mode 100755
index 00000000000..d9e8fb1c886
--- /dev/null
+++ b/autosharding/third_party/autosharding/import.sh
@@ -0,0 +1,58 @@
+#!/bin/bash
+# Copyright 2026 The gRPC Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Update VERSION then execute this script
+
+set -e
+VERSION=3a21f69fe5516fe340af29ef68e327381473ecc7
+DOWNLOAD_URL="https://github.com/GoogleCloudPlatform/autosharding/archive/${VERSION}.tar.gz"
+DOWNLOAD_BASE_DIR="autosharding-${VERSION}"
+SOURCE_PROTO_BASE_DIR="${DOWNLOAD_BASE_DIR}/proto"
+TARGET_PROTO_BASE_DIR="src/main/proto"
+# Sorted alphabetically.
+FILES=(
+autosharding/v1/autosharding.proto
+)
+
+pushd `git rev-parse --show-toplevel`/autosharding/third_party/autosharding > /dev/null
+
+# put the repo in a tmp directory
+tmpdir="$(mktemp -d)"
+trap "rm -rf ${tmpdir}" EXIT
+curl -Ls "${DOWNLOAD_URL}" | tar xz -C "${tmpdir}"
+
+cp -p "${tmpdir}/${DOWNLOAD_BASE_DIR}/LICENSE" LICENSE
+
+rm -rf "${TARGET_PROTO_BASE_DIR}"
+mkdir -p "${TARGET_PROTO_BASE_DIR}"
+pushd "${TARGET_PROTO_BASE_DIR}" > /dev/null
+
+# copy proto files to project directory
+TOTAL=${#FILES[@]}
+COPIED=0
+for file in "${FILES[@]}"
+do
+ mkdir -p "$(dirname "${file}")"
+ cp -p "${tmpdir}/${SOURCE_PROTO_BASE_DIR}/${file}" "${file}" && (( ++COPIED ))
+done
+popd > /dev/null
+
+popd > /dev/null
+
+echo "Imported ${COPIED} files."
+if (( COPIED != TOTAL )); then
+ echo "Failed importing $(( TOTAL - COPIED )) files." 1>&2
+ exit 1
+fi
diff --git a/autosharding/third_party/autosharding/src/main/proto/autosharding/v1/autosharding.proto b/autosharding/third_party/autosharding/src/main/proto/autosharding/v1/autosharding.proto
new file mode 100644
index 00000000000..c14a4b8921a
--- /dev/null
+++ b/autosharding/third_party/autosharding/src/main/proto/autosharding/v1/autosharding.proto
@@ -0,0 +1,327 @@
+syntax = "proto3";
+
+package google.cloud.autosharding.v1;
+
+import "google/api/field_behavior.proto";
+import "google/protobuf/duration.proto";
+import "google/protobuf/timestamp.proto";
+
+option go_package = "github.com/GoogleCloudPlatform/autosharding/apiv1;autoshardingpb";
+option java_package = "com.google.cloud.autosharding.v1";
+option java_multiple_files = true;
+option java_outer_classname = "AutoshardingProto";
+
+// An auto-sharding service that assigns keys in an application's keyspace to
+// abstract "endpoints", and which uses load information from clients to update
+// that assignment over time. Assignments and load reports are scoped to an
+// abstract "slicing target."
+//
+// Concrete examples of the concepts in this protocol:
+//
+// Endpoints:
+// * Application servers
+// * Pods in a Kubernetes cluster
+// * Regions in a multi-regional service
+//
+// Keys:
+// * User ids
+// * Tenant ids in a multi-tenant system
+//
+// Load:
+// * Request count
+// * CPU cost of processing requests
+service AutoshardingService {
+ // Opens a stream over which clients report load and the server delivers
+ // sharding assignments.
+ //
+ // A given client may use this stream just to receive assignments or also to
+ // report load, and it may opt in or out of reporting load at any time. (An
+ // implementation of the Autosharding service may impose restrictions on
+ // which clients are allowed to report load, and it may respond to clients
+ // improperly reporting load by ignoring their reports or terminating their
+ // streams with an error.)
+ //
+ // The client should keep this stream open at all times and reopen the stream
+ // after it closes, with backoff if the stream closed without delivering any
+ // data.
+ rpc WatchShardingAssignment(stream WatchShardingAssignmentRequest)
+ returns (stream WatchShardingAssignmentResponse);
+}
+
+// In each instance of this message, the client MUST set exactly one field.
+message WatchShardingAssignmentRequest {
+ // Initial client-side configuration necessary to set up the stream. This
+ // field MUST be set in the first message sent, and it MUST NOT be set in
+ // subsequent messages.
+ InitialClientConfig initial_client_config = 1
+ [(google.api.field_behavior) = OPTIONAL];
+
+ // An aggregated snapshot of load data collected by this client.
+ //
+ // The client MUST include all load data it has collected since the last
+ // report in this report (irrespective of whether the last report was sent
+ // on this or a prior physical stream), and it MUST NOT include that data in
+ // any subsequent report.
+ //
+ // This field MUST NOT be set in the first message sent.
+ LoadReport load_report = 2 [(google.api.field_behavior) = OPTIONAL];
+
+ // The client's acknowledgment of the last received assignment.
+ //
+ // The client MUST respond with this message to every received
+ // `AssignmentMetadata` message from the server.
+ AssignmentAck assignment_ack = 3 [(google.api.field_behavior) = OPTIONAL];
+}
+
+// In each instance of this message, the server will set exactly one field.
+message WatchShardingAssignmentResponse {
+ // Configuration dictating how the client should aggregate and send load
+ // reports.
+ //
+ // The client MUST wait to send load reports until it has received the first
+ // version of this config from the server (on this physical stream).
+ LoadReportingConfig config = 1;
+
+ // A chunk of a logical assignment.
+ //
+ // The server delivers an assignment as a sequence of one or more
+ // `AssignmentChunk` messages followed by a single `AssignmentMetadata`
+ // message. Clients MUST wait for an `AssignmentMetadata` message and combine
+ // the prior chunks to form a logical assignment before using it.
+ AssignmentChunk chunk = 2;
+
+ // Metadata associated with the most recent chunked assignment. Acts as a
+ // trailer indicating that the server has finished delivering all chunks for
+ // that assignment.
+ AssignmentMetadata metadata = 3;
+}
+
+// Initial client-side configuration necessary to set up the stream.
+message InitialClientConfig {
+ // The slicing target to which this client's load reports and assignments
+ // apply.
+ //
+ // If this target is unknown to the server, the server will terminate the
+ // stream with a NOT_FOUND error.
+ //
+ // If this client does not have permission to connect to this target, the
+ // server will terminate the stream with a PERMISSION_DENIED error.
+ string target = 1 [(google.api.field_behavior) = REQUIRED];
+
+ // A UUID generated by this client upon instantiation. The client MUST reuse
+ // the same UUID across Autosharding stream reconnections. It is
+ // permissible but not expected for the client to use the same UUID across
+ // process restarts.
+ string client_uuid = 2 [(google.api.field_behavior) = REQUIRED];
+
+ // The latest assignment generation that this client has received. If set, the
+ // server will only send newer assignments. If unset (= zero), the server
+ // will send the latest assignment immediately.
+ int64 latest_generation = 3 [(google.api.field_behavior) = OPTIONAL];
+}
+
+// Server-specified configuration dictating how the client should report load.
+message LoadReportingConfig {
+ // The maximum fraction of total load in a `Load` message that appears in a
+ // single `RangeLoad` message, if the range is not a single-key range. When
+ // aggregating load data into a load report, the client MUST honor the latest
+ // received value of this field as of when it started aggregating. (See the
+ // `RangeLoad` documentation for a description of how the client should
+ // aggregate load data into load reports.)
+ //
+ // This field will always be in the range [0, 1].
+ double load_quantum_fraction = 1;
+
+ // The maximum length of slice keys in load reports. When aggregating load
+ // data into a load report, the client MUST honor the latest received value
+ // of this field as of when it started aggregating. (See the `RangeLoad`
+ // documentation for a description of how the client should aggregate load
+ // data into load reports.)
+ int32 max_load_balancing_resolution_bytes = 2;
+
+ // The interval at which this client should send `LoadReport` messages. After
+ // it sends a given load report to the server, the client MUST wait for
+ // roughly the interval specified by the latest received value of this field
+ // before it sends its next load report.
+ google.protobuf.Duration load_reporting_interval = 3;
+
+ // The lower bound on a load report's `start_time`, relative to the
+ // client's current time when it starts computing the load report. Clients
+ // should discard load data before this threshold.
+ google.protobuf.Duration load_report_start_threshold = 4;
+}
+
+// A load report consisting of per-metric load snapshots.
+message LoadReport {
+ // Snapshots of load in the keyspace associated with one or more metrics.
+ //
+ // A key in this map is a named load metric to which the load applies. This
+ // metric MUST be a "cost" metric, i.e., one where the total load for a given
+ // application key over a time interval is the sum of the individual load
+ // values recorded for that key over the time interval, and where the number
+ // of endpoints assigned a given application key does not affect the total
+ // load for that key.
+ map metric_loads = 1 [(google.api.field_behavior) = REQUIRED];
+
+ // The inclusive start timestamp of the time range to which this load report
+ // applies.
+ google.protobuf.Timestamp start_time = 2
+ [(google.api.field_behavior) = REQUIRED];
+
+ // The exclusive end timestamp of the time range to which this load report
+ // applies.
+ //
+ // This end time MUST be roughly equal to the client's current time, i.e.,
+ // the client must send this load report to the server immediately after
+ // computing it.
+ //
+ // To ensure good load-balancing behavior, the clock used to set these
+ // timestamps MUST be synchronized to within ~one second of the clocks of
+ // other clients and the Autosharding service. Implementations of the
+ // Autosharding service should have some mechanism for informing client
+ // owners when they detect significantly delayed client load reports (which
+ // suggests an actionable issue related to either network delays or clock
+ // skew).
+ google.protobuf.Timestamp end_time = 3
+ [(google.api.field_behavior) = REQUIRED];
+}
+
+// A per-key-range load snapshot.
+message Load {
+ // An ordered set of per-key-range load. Each range's inclusive start is
+ // implicitly the exclusive end of the previous range in the ordered set.
+ //
+ // Entries here MUST have lexicographically increasing start keys, and two
+ // entries MUST NOT have the same start key.
+ //
+ // Start keys in these entries MUST be at most
+ // `max_load_balancing_resolution_bytes` bytes (using the latest received
+ // value as of when the client started aggregating this load report).
+ //
+ // Clients MUST aggregate observed load values for a given key during this
+ // report's time interval into a single value for the key by summing the
+ // values, and clients MUST aggregate per-key load values into a single load
+ // value for a key range by summing the values.
+ //
+ // A single range MUST contain at most `load_quantum_fraction` of the total
+ // load across all ranges, unless it is a single-key range.
+ //
+ // A range is a single-key range if the next range's start key is the
+ // immediate lexicographic successor of this range's start key in the space of
+ // bytestrings of length `max_load_balancing_resolution_bytes` or if the start
+ // key is the largest allowed key.
+ //
+ // Subject to the above constraints, clients MUST aggregate this load data
+ // into as few key ranges as possible.
+ //
+ // Clients under memory pressure may report "inexact" load according to a
+ // strategy specified by the Autosharding service owner, e.g., by
+ // sampling observed load data rather than retaining each data point.
+ //
+ // Implementation sketch for aggregating load into a load report: maintain a
+ // hash map from key to total load observed since the last report. When it is
+ // time to send a new report, truncate the keys to
+ // `max_load_balancing_resolution_bytes`, then sort the load values
+ // lexicographically by key. Walk the keyspace starting from the min key,
+ // maintaining a current "open range" and its total load. If adding the
+ // current key to the current open range would push its total load over
+ // `load_quantum_fraction`, then "close" the range and start a new open range.
+ // Otherwise, add the current key to the current open range.
+ repeated RangeLoad range_loads = 1;
+}
+
+// Load associated with a key range (whose end is implicitly defined by the
+// next range's start).
+message RangeLoad {
+ // The key range's start (inclusive).
+ bytes start_key = 1;
+
+ // The aggregate load associated with this key range observed by this client
+ // during the time interval in this load report.
+ double load = 2;
+}
+
+// The client's acknowledgment of the last received assignment.
+message AssignmentAck {
+ // The generation of the assignment this acknowledgment applies to.
+ int64 generation = 1 [(google.api.field_behavior) = REQUIRED];
+
+ // Whether the client accepted the assignment.
+ //
+ // If the client did not accept the assignment, it MUST set this field to
+ // false and continue using the previous assignment, and it MUST set the
+ // `error_message` field to explain the reason for rejection.
+ bool accepted = 2 [(google.api.field_behavior) = REQUIRED];
+
+ // A textual error message from the client.
+ //
+ // This field MUST be set if and only if the client detected issues with the
+ // assignment, explaining those issues. The client may set this field even if
+ // it accepted the assignment, in which case the issues are not blocking from
+ // using the assignment.
+ //
+ // Service owners may use this information for fixing problems.
+ //
+ // The length of this field MUST NOT exceed 512 characters (Unicode code
+ // points, see https://google.aip.dev/210).
+ string error_message = 3 [
+ (google.api.field_behavior) = OPTIONAL
+ ];
+}
+
+// Metadata associated with an assignment.
+message AssignmentMetadata {
+ // The assignment's unique and monotonically-increasing version number.
+ int64 generation = 1;
+}
+
+// A portion of an assignment of slices to endpoints. The individual fields'
+// documentation describes how to reassemble a set of chunks into a complete
+// assignment.
+message AssignmentChunk {
+ // A chunk of the set of assignments for individual slices.
+ //
+ // Clients MUST reassemble the complete `slice_assignments` list by
+ // concatenating the contents of this field across chunks (in any order).
+ repeated SliceAssignment slice_assignments = 1;
+
+ // A chunk of the list of all endpoints present in this assignment.
+ //
+ // Clients MUST reassemble the complete `endpoints` list by concatenating the
+ // contents of this field across chunks, in chunk order.
+ repeated EndpointState endpoints = 2;
+}
+
+// An endpoint and any associated metadata.
+message EndpointState {
+ // The endpoint's name.
+ string endpoint = 1;
+}
+
+// The assignment for an individual slice.
+message SliceAssignment {
+ // The slice to which endpoints are assigned.
+ Slice slice = 1;
+
+ // The endpoints assigned to this slice.
+ repeated PerSliceEndpointState endpoints = 2;
+}
+
+// A single slice, i.e., a continuous range of keys.
+message Slice {
+ // The slice's start (inclusive). Will be at most 512 bytes.
+ bytes start_key = 1;
+
+ // The slice's end (exclusive). Will be at most 512 bytes. If this field is
+ // unset, this slice contains the largest allowed key (the 512-byte string
+ // 0xFFFF...).
+ optional bytes end_key = 2;
+}
+
+// An endpoint assigned to a slice, along with any associated metadata about
+// the endpoint in the context of that slice.
+message PerSliceEndpointState {
+ // An endpoint, specified as an index into the complete assignment's
+ // reassembled `endpoints` list.
+ int32 endpoint_index = 1;
+}
diff --git a/settings.gradle b/settings.gradle
index abd30a63956..0322a9a1fbd 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -95,6 +95,7 @@ include ":grpc-inprocess"
include ":grpc-util"
include ":grpc-opentelemetry"
include ":grpc-context-override-opentelemetry"
+include ":grpc-autosharding"
project(':grpc-api').projectDir = "$rootDir/api" as File
project(':grpc-core').projectDir = "$rootDir/core" as File
@@ -132,6 +133,7 @@ project(':grpc-inprocess').projectDir = "$rootDir/inprocess" as File
project(':grpc-util').projectDir = "$rootDir/util" as File
project(':grpc-opentelemetry').projectDir = "$rootDir/opentelemetry" as File
project(':grpc-context-override-opentelemetry').projectDir = "$rootDir/contextstorage" as File
+project(':grpc-autosharding').projectDir = "$rootDir/autosharding" as File
if (settings.hasProperty('skipCodegen') && skipCodegen.toBoolean()) {
println '*** Skipping the build of codegen and compilation of proto files because skipCodegen=true'