• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

grpc / grpc-java / #19705

20 Feb 2025 04:25AM UTC coverage: 88.616% (+0.03%) from 88.589%
#19705

push

github

web-flow
xds: explicitly set request hash key for the ring hash LB policy

Implements [gRFC A76: explicitly setting the request hash key for the
ring hash LB policy][A76]
* Explictly setting the request hash key is guarded by the
  `GRPC_EXPERIMENTAL_RING_HASH_SET_REQUEST_HASH_KEY` environment
  variable until API stabilized. 

Tested:
* Verified end-to-end by spinning up multiple gRPC servers and a gRPC
  client that injects a custom service (load balancing) config with
  `ring_hash_experimental` and a custom `request_hash_header` (with
  NO associated value in the metadata headers) which generates a random
  hash for each request to the ring hash LB. Verified picks/RPCs are
  split evenly/uniformly across all backends.
* Ran affected unit tests with thread sanitizer and 1000 iterations to
  prevent data races.

[A76]: https://github.com/grpc/proposal/blob/master/A76-ring-hash-improvements.md#explicitly-setting-the-request-hash-key

34305 of 38712 relevant lines covered (88.62%)

0.89 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.88
/../xds/src/main/java/io/grpc/xds/RingHashLoadBalancerProvider.java
1
/*
2
 * Copyright 2021 The gRPC Authors
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *     http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16

17
package io.grpc.xds;
18

19
import com.google.common.annotations.VisibleForTesting;
20
import com.google.common.base.Strings;
21
import io.grpc.Internal;
22
import io.grpc.LoadBalancer;
23
import io.grpc.LoadBalancer.Helper;
24
import io.grpc.LoadBalancerProvider;
25
import io.grpc.NameResolver.ConfigOrError;
26
import io.grpc.Status;
27
import io.grpc.internal.GrpcUtil;
28
import io.grpc.internal.JsonUtil;
29
import io.grpc.xds.RingHashLoadBalancer.RingHashConfig;
30
import io.grpc.xds.RingHashOptions;
31
import java.util.Map;
32

33
/**
34
 * The provider for the "ring_hash_experimental" balancing policy.
35
 */
36
@Internal
37
public final class RingHashLoadBalancerProvider extends LoadBalancerProvider {
1✔
38

39
  // Same as ClientXdsClient.DEFAULT_RING_HASH_LB_POLICY_MIN_RING_SIZE
40
  @VisibleForTesting
41
  static final long DEFAULT_MIN_RING_SIZE = 1024L;
42
  // Same as ClientXdsClient.DEFAULT_RING_HASH_LB_POLICY_MAX_RING_SIZE
43
  @VisibleForTesting
44
  static final long DEFAULT_MAX_RING_SIZE = 4 * 1024L;
45

46
  private static final boolean enableRingHash =
1✔
47
      Strings.isNullOrEmpty(System.getenv("GRPC_XDS_EXPERIMENTAL_ENABLE_RING_HASH"))
1✔
48
          || Boolean.parseBoolean(System.getenv("GRPC_XDS_EXPERIMENTAL_ENABLE_RING_HASH"));
1✔
49

50
  @Override
51
  public LoadBalancer newLoadBalancer(Helper helper) {
52
    return new RingHashLoadBalancer(helper);
1✔
53
  }
54

55
  @Override
56
  public boolean isAvailable() {
57
    return enableRingHash;
1✔
58
  }
59

60
  @Override
61
  public int getPriority() {
62
    return 5;
1✔
63
  }
64

65
  @Override
66
  public String getPolicyName() {
67
    return "ring_hash_experimental";
1✔
68
  }
69

70
  @Override
71
  public ConfigOrError parseLoadBalancingPolicyConfig(Map<String, ?> rawLoadBalancingPolicyConfig) {
72
    try {
73
      return parseLoadBalancingPolicyConfigInternal(rawLoadBalancingPolicyConfig);
1✔
74
    } catch (RuntimeException e) {
×
75
      return ConfigOrError.fromError(
×
76
          Status.UNAVAILABLE.withCause(e).withDescription(
×
77
              "Failed parsing configuration for " + getPolicyName()));
×
78
    }
79
  }
80

81
  private ConfigOrError parseLoadBalancingPolicyConfigInternal(
82
      Map<String, ?> rawLoadBalancingPolicyConfig) {
83
    Long minRingSize = JsonUtil.getNumberAsLong(rawLoadBalancingPolicyConfig, "minRingSize");
1✔
84
    Long maxRingSize = JsonUtil.getNumberAsLong(rawLoadBalancingPolicyConfig, "maxRingSize");
1✔
85
    String requestHashHeader = "";
1✔
86
    if (GrpcUtil.getFlag("GRPC_EXPERIMENTAL_RING_HASH_SET_REQUEST_HASH_KEY", false)) {
1✔
87
      requestHashHeader = JsonUtil.getString(rawLoadBalancingPolicyConfig, "requestHashHeader");
1✔
88
    }
89
    long maxRingSizeCap = RingHashOptions.getRingSizeCap();
1✔
90
    if (minRingSize == null) {
1✔
91
      minRingSize = DEFAULT_MIN_RING_SIZE;
1✔
92
    }
93
    if (maxRingSize == null) {
1✔
94
      maxRingSize = DEFAULT_MAX_RING_SIZE;
1✔
95
    }
96
    if (requestHashHeader == null) {
1✔
97
      requestHashHeader = "";
1✔
98
    }
99
    if (minRingSize > maxRingSizeCap) {
1✔
100
      minRingSize = maxRingSizeCap;
1✔
101
    }
102
    if (maxRingSize > maxRingSizeCap) {
1✔
103
      maxRingSize = maxRingSizeCap;
1✔
104
    }
105
    if (minRingSize <= 0 || maxRingSize <= 0 || minRingSize > maxRingSize) {
1✔
106
      return ConfigOrError.fromError(Status.UNAVAILABLE.withDescription(
1✔
107
          "Invalid 'mingRingSize'/'maxRingSize'"));
108
    }
109
    return ConfigOrError.fromConfig(
1✔
110
        new RingHashConfig(minRingSize, maxRingSize, requestHashHeader));
1✔
111
  }
112
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc