Autosharding LB implementation - #11
Open
shivaspeaks wants to merge 8 commits into
Open
Conversation
Owner
Author
|
/gemini review |
4 similar comments
Owner
Author
|
/gemini review |
Owner
Author
|
/gemini review |
Owner
Author
|
/gemini review |
Owner
Author
|
/gemini review |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements Autosharding LB in grpc-java: https://github.com/easwars/proposal/blob/slicer/A119-autosharding-lb-policy.md
This implementation does not contain xds integration.
Abstract
Add support for an auto-sharding load balancing policy, that communicates with
an external sharding service to receive resource assignments. This policy should
be supported in both xDS and non-xDS based deployments.
Background
An auto-sharding service enables client-side load balancing through the
following process:
fluctuating load
Within this framework, application-defined keys generally consist of arbitrary
byte sequences, such as:
The targets for traffic distribution, or resources, frequently include:
Implementing a load balancing policy in gRPC that uses an auto-sharding service
has applications in various scenarios, such as:
Related Proposals
Proposal
Add the
autosharding_experimentalLB policy in gRPC that contains thefollowing functionality:
sharding service and processing assignments from that service.
distinct, non-overlapping key-ranges or slices, each associated with a set
of server endpoints.
application-defined keyspace.
it.
the sharding service are unusable.
Crucially, the LB policy receives its configuration and endpoint data from the
Name Resolver and not from the sharding service.
LB Policy Architecture
The LB policy receives the following information from the Name Resolver apart
from its configuration:
attribute. If this attribute is missing, the first address associated with the
endpoint shall serve as the hostname. The endpoint hostname attribute
described in [gRFC A81][A81] will be used here.
sharding service, given an opaque string specified in the configuration
EndpointMap
The endpoints are stored in a map where the key is the hostname of the endpoint,
and the value is the state associated with the endpoint. This state includes a
child
pick_firstLB policy that is created lazily, and the most recentconnectivity state and picker returned by that policy. We'll call this map the
EndpointMapgoing forward. This could look something like this:The LB policy must create a new
EndpointMapwhenever it receives new endpointsfrom the Name Resolver.
The LB policy should update the existing
EndpointMapwhen it receives anupdate from the child policy. This means that the
EndpointMapcannot be sharedwith the picker without synchronizing access to it. Instead, we propose creating
a new data structure that contains only the fields from
EndpointStatethat thePickerneeds access to.Assignment
The LB policy must use the injected "Channel Factory" to create a gRPC channel
to the sharding service, and must create a
Shardstream on it. The shardingservice will send assignments on this stream. These will be stored internally in
a data structure named
Assignment, and will contain key-ranges and theirassociated endpoint names. This could look something like this:
SliceMap
To route each RPC, the picker will essentially need to use the
Assignmenttodetermine which
Sliceto use, choose an endpoint name from within thatSlice, and then look up that endpoint name in theEndpointMap. Forperformance reasons, we want to avoid having to look up the endpoint name in a
map, so we will create a new data structure called a
SliceMapthat isoptimized for lookups. Given a key, it returns a matching key-range. The
SliceMapmust be immutable, allowing the Picker to access it without anyexplicit synchronization with the LB policy. The
SliceMapis meant to be usedby the
Pickerin conjunction with a list ofPickerEndpoints such that thelist can be swapped out, as long as the number and order of endpoints don't
change.
Because assignments are pre-validated to have no gaps and cover the full key
range, and since
SliceMap.slicesis sorted bystart_key, the implementationof
SliceMap.lookupboils down to a binary search to find the smallest indexiwhereSliceMap.slices[i].start_key > key. Once we havei, indexi - 1is what we are actually looking for. Here is a psuedo-code for it:
Building the SliceMap
The
SliceMapis generated from theEndpointMapandAssignmentwhen eitherof them change. Here is the pseudo-code for the logic to build the
SliceMap:Fallback Mechanism
The LB policy must support a fallback mechanism that utilizes all endpoints
provided by the Name Resolver. There are two types of fallback:
but all endpoints in the matching
SliceEntryfor an RPC are inTRANSIENT_FAILURE.Key considerations here:
policy configuration.
not employ any sort of subsetting.
the sharding service, if it subsequently receives a bad one or if the
connection to the sharding service fails.
Fallback at Startup
Whenever the LB policy creates a new gRPC Channel to the sharding service, it
must start a timer for the duration specified by the
initial_assignment_timeoutfield in the LB policy configuration. There are twopossible scenarios here:
must continue using them until it receives one from the new gRPC Channel or
the timer expires. While the timer is pending and the LB policy is using the
existing assignment, it must continue to process endpoint updates from the
Name Resolver and state updates from the child LB policies as normal.
receives one from the new gRPC Channel or the timer expires.
When the policy receives a valid assignment from the sharding server or the
timer expires, it must build a
SliceMapand update the parent gRPC Channelwith a new
Picker, which then retries any queued RPCs:Pickerwill this assignment for the retried RPCs.
by the Name Resolver.
While RPCs are queued waiting for one of the above events to happen, the
Pickermust setdelay_typeto "autosharding_assignment_pending". See [WIPgRFC A121][A121].
Supported modes of operation
The LB policy must support two primary modes of operation:
localities and shards requests accordingly. This is similar to how the
ring_hashLB policy, specified in [gRFC A42][A42], works.LB policy, sharding requests across a flat list of endpoints provided by the
Name Resolver.
weighted_target_experimentalthat handles locality picking, while eachautosharding_experimentalchild policy instance only handles endpointpicking within its specific locality.
The LB policy must maintain consistent behavior across both modes and must not
require explicit knowledge of its operational context. Notably, we do not
support using this LB policy solely for locality picking with delegation to a
separate endpoint-picking policy. This configuration lacks identified use cases
and introduces significant implementation complexity.
Load Balancing Configuration
The
autosharding_experimentalLB policy's configuration will be as follows:Key considerations regarding the LB policy configuration that must be handled by
the "Channel Factory".
communication with the sharding service. This decision prevents potential
privilege-escalation vulnerabilities resulting from a compromised control
plane, following the security framework established in [gRFC A102][A102].
configuration.
Handling updates from the Name Resolver
When the LB policy receives a configuration update, it must do the following:
channel_factory_keyfield has changed (or if this is thefirst configuration update), use the “Channel Factory” to create a new gRPC
channel to this target URI.
If a new gRPC channel is created:
Shardstream on the newly created gRPC channel, and,slicing_targetfield has changed, create a newShardstream becausethe
slicing_targetcontrols the assignments sent by the sharding service.When the LB policy receives endpoints from the Name Resolver, it must do the
following:
pick_firstchild, lazily, for every endpoint. The latter will createsubchannels for the addresses within the endpoints. See this
section for more details.
EndpointMapaccordingly.SliceMapunless the initial assignment timer is active. Seesection Building the SliceMap for more information.
Pickerthat uses the aboveSliceMap.If the LB policy receives an empty set of endpoints from the Name Resolver, it
must set the connectivity state of the gRPC channel to
TRANSIENT_FAILUREandfail all subsequent RPCs until an update with a non-empty set of endpoints is
received.
Handling updates from child policies
When the LB policy receives a state update from one of its child policies,
containing the new connectivity state and picker for the child policy, the LB
policy must perform the following:
EndpointStatestored in theEndpointMapcorresponding to thechild policy.
Pickerwith the existingSliceMapand updatedEndpointMap.update, the
Pickerwill be able to build a newlist[PickerEndpoint]anduse it with the existing
SliceMap.Picker.Creating a gRPC Channel to the Sharding Service
The LB policy will be injected with a “Channel Factory” via attributes,
alongside its configuration. This utility will help create a fully functional
gRPC Channel given the
channel_factory_keyin the LB policy configuration.Implementations must ensure the key uniquely encodes all parameters necessary
for channel creation. For example, credentials need only be included in the key
if the factory supports creating channels with different credentials.
In xDS-based deployments, this “Channel Factory” will be injected by the
cds_experimentalLB policy. Refer to section Changes to CDS LBpolicy for more details. For non-xDS environments,
users will have the capability to inject this utility via a dedicated channel
option.
A
DialOptionwill be added to allow the user to inject a “Channel Factory”.gRPC will take care of plumbing this down to the LB policies.
We will also have APIs to set and get this factory from the
resolver.Statestruct that is sent to the LB policy as part of a resolver update.
C++
TBD
JAVA
TBD
Communicating with the sharding service
The LB policy communicates with an external sharding service using the OSS
DynamicSharding gRPC protocol. As described earlier, the LB policy creates a
gRPC channel to the sharding service using the “Channel Factory” provided to it,
whenever the
channel_factory_keyin its configuration changes. It willthen create a
Shardstream on that channel.Sending the first message
The LB policy sends an
Initmessage on the stream to kick things off. Thismessage currently contains three fields:
target: The value for this field is derived from theslicing_targetfieldof the LB policy configuration. If a
%stokens is present in this string, itis replaced with the “Locality” value passed to the LB policy as attributes
in the resolver update (similar to how the “Channel Factory” is passed).
weighted_target_experimentalLB policy as a resolver state attribute, andis available to all LB policies that sit underneath it.
autosharding_experimentalLB policy is used for endpointpicking alone, it will sit underneath the
weighted_target_experimentalLB policy, and therefore will have access to this resolver attribute. See
[gRFC A78][A78] for more details.
autosharding_experimentalLB policy is used for both localityand endpoint picking, the “Locality” value will not be part of the slicing
target as the policy will handle endpoints from all localities.
slicing_targetto notcontain
%stokens. But if they do, it is the responsibility of the user toensure that this attribute is populated by the Name Resolver. If this
attribute is not available, the LB policy will replace the
%stoken withan empty string.
client_uuid: The LB policy generates a UUID at creation time and must reusethe same value across stream restarts.
current_generation: The LB policy must store the generation number of themost recent good assignment received from the sharding service and use that
value here.
assignment in the case of a stream failure.
Handling responses from the sharding server
Responses received from the sharding server in a
ShardingResponsemessage canone of the following:
AssignmentChunk: This contains one chunk of a logical assignment from thesharding server. The LB policy must cache chunks until it receives an
AssignmentMetadatamessage.AssignmentMetadata: This indicates the end of a logical assignment from thesharding server. The LB policy must attempt to combine previously received
chunks into one single logical assignment.
LoadReportingConfig: This contains configuration for how load needs to beaggregated and sent to the sharding server. The LB policy must ignore this
message for the time being.
See section on Handling assignments from the sharding
server for more information.
Backoff on stream and connectivity failures
When a
Shardstream fails without receiving at least one good logicalassignment, the LB policy must use exponential backoff before each successive
attempt to re-establish the stream. The algorithm should be similar to what gRPC
uses for connection attempts. The backoff state will be reset when a
Shardstream finally receives a good logical assignment from the server.
Implementations should use the
wait_for_readyoption on theShardstream tohelp recover faster from connectivity failures instead of applying a backoff
when stream creation fails.
Handling assignments from the sharding server
The sharding server implementing the OSS DynamicSharding gRPC protocol will
distribute (chunked) complete assignments to its clients, instead of deltas.
From the LB policy’s point of view, this will look as follows:
ShardingResponsemessagesShardingResponsemessage contains either anAssignmentChunkmessageor an
AssignmentMetadatamessage.AssignmentChunkmessage contains a list ofSliceAssignmentmessagesand a list of
EndpointStatemessages:SliceAssignmentmessage contains aSlicethat contains a[start_key, end_key)and a list of endpoint indices into the combinedendpoint list over all chunks (in chunk order).
EndpointStatemessage contains a single endpoint name.AssignmentMetadatamessage indicates that the sharding server hascompleted sending all chunks for the current assignment and contains a
generation number for the logical assignment.
Visually, we can represent this as follows:
The LB policy must cache the
AssignmentChunkmessages locally until it sees anAssignmentMetadatamessage. This is because eachChunkcontains severalendpoint names and each
Slicewithin a chunk contains an index into thecomplete set of endpoint names, combined in chunk order. So, until all chunks
are received, the LB policy cannot meaningfully use any of them.
Once the
AssignmentMetadatamessage is received, the LB policy must validatethe assignment as follows:
Slices.Slices are valid once theendpoint names are combined.
If validation fails, the LB policy must terminate the stream to the sharding
service, and attempt to re-establish it.
Upon successful validation, the LB policy must build a new
SliceMapand createa new picker with the newly built
SliceMapand send an update to the gRPCchannel.
The Picker
The LB policy must create a new
Pickerevery time a newSliceMapis built,which happens every time the LB policy receives new endpoints from the Name
Resolver or new assignments from the sharding service. The LB policy must create
a new
Pickerwhen it receives a state update from one of its child policies aswell, but in this case, the existing
SliceMapcan be reused.Here is the pseudo-code for the
Pickermethod of the picker:Interactions with
pick_firstThe LB policy will not proactively connect to endpoints given to it by the Name
Resolver. Instead, connections are triggerred from the picker as described above
in the picker pseudo-code.
autosharding_experimentalmust create apick_firstchild for every endpoint given to it by the Name Resolver.pick_firststarts connecting as soon as it is given its endpoint. So,autosharding_experimentalmust make sure that the childpick_firstpolicy iscreated lazily, when a connection to that endpoint needs to be established. This
may be accomplished by wrapping
pick_firstin a parent policy that createspick_firstonly when asked to establish a connection.The
autosharding_experimentalLB policy also relies on the sticky-TF behavior(specified in [gRFC A62][A62]) implemented by the
pick_firstpolicy, thatensures endpoints in
TRANSIENT_FAILUREstay in that state and continuously tryto reconnect with exponential backoff until they become
READY.Aggregated Connectivity State
The LB policy will use the same rules used by the
ring_hashLB policy, asdescribed in [gRFC A42][A42] to determine the aggregated connectivity state of
the gRPC Channel. The complete connectivity state aggregation rules are as
follows:
READYstate, reportREADY.TRANSIENT_FAILUREstate, reportTRANSIENT_FAILURE.CONNECTINGstate, reportCONNECTING.TRANSIENT_FAILUREand there is more thanone subchannel, report state
CONNECTING.IDLEstate, reportIDLE.TRANSIENT_FAILURE.Unlike most other LB policies, which start off in
CONNECTING, this policystarts off in
IDLEbecause it establishes connections lazily in response toRPCs. It uses a heuristic and reports
TRANSIENT_FAILUREwhen at least twosubchannels are in
TRANSIENT_FAILUREand none of the subchannels areREADY.This heuristic is an attempt to to balance the need to allow the
prioritypolicy (which would be an ancestor to this LB policy in the tree of LB policies
used in xDS use-cases) to quickly failover to the next priority and the desire
to avoid reporting the entire policy as having failed when the problem is just
one individual subchannel that happens to be unreachable.
The specific behavior that will enable this LB policy to stop reporting
TRANSIENT_FAILUREeven when it is not receiving picks will be that wheneverthis policy receives a subchannel connectivity state update or a resolver
update, if the aggregated connectivity state is
TRANSIENT_FAILUREorCONNECTINGand there are no endpoints inCONNECTINGstate, the policy willchoose one of the endpoints in
IDLEstate (if any) to trigger a connectionattempt on. It does not matter which
IDLEendpoint is chosen; that is left upto the implementation to determine. An efficient way to choose an
IDLEendpoint is to keep track of the first
IDLEendpoint while iterating throughall endpoints to determine the aggregated connectivity state and use it directly.
An alternative approach is shown in the following pseudo-code: