Skip to content
Open
73 changes: 73 additions & 0 deletions balancer/hostname/hostname.go
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Please move this to experimental/balancer/hostname package. We are trying to house all newly added experimental APIs to the top-level experimental directory.

Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
*
* Copyright 2026 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.
*
*/

// Package hostname contains utilities for the endpoint hostname attribute
// (used for per-endpoint :authority / SNI override).
//
// # Experimental
//
// Notice: This API is EXPERIMENTAL and may be changed or removed in a
// later release.
Comment on lines +24 to +25
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Please indicate in the note that all APIs in this package are experimental here. That way, every single API does not have to carry that same warning.

package hostname

import "google.golang.org/grpc/resolver"

type hostnameKey struct{}

// Set returns a copy of the given endpoint with the hostname attribute set.
// If hostname is empty the endpoint is returned unmodified.
//
// # Experimental
//
// Notice: This API is EXPERIMENTAL and may be changed or removed in a
// later release.
func Set(endpoint resolver.Endpoint, hostname string) resolver.Endpoint {
if hostname == "" {
return endpoint
}
endpoint.Attributes = endpoint.Attributes.WithValue(hostnameKey{}, hostname)
return endpoint
}

// Hostname returns the hostname attribute of endpoint. If this attribute is
// not set, it returns the empty string.
//
// # Experimental
//
// Notice: This API is EXPERIMENTAL and may be changed or removed in a
// later release.
func Hostname(endpoint resolver.Endpoint) string {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think it will be better to rename it as FromEndpoint.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

done

h, _ := endpoint.Attributes.Value(hostnameKey{}).(string)
return h
}

// FromAddress returns the hostname attribute from a legacy
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We don't need to export the function to retrieve hostname from address.attributes and address.BalancerAttributes.
Please remove this.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

done

// resolver.Address. It checks both the modern Attributes field and the
// deprecated BalancerAttributes field for compatibility.
//
// # Experimental
//
// Notice: This API is EXPERIMENTAL and may be changed or removed in a
// later release.
func FromAddress(addr resolver.Address) string {
if h, ok := addr.Attributes.Value(hostnameKey{}).(string); ok && h != "" {
return h
}
h, _ := addr.BalancerAttributes.Value(hostnameKey{}).(string)
return h
}
Comment on lines +59 to +69
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Most LB policies should not need access to attributes stores in a resolver.Address. Would the policy that you are planning to write need this one? Unless we have a documented use-case for this, we should not be adding this.

44 changes: 44 additions & 0 deletions balancer/hostname/hostname_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
*
* Copyright 2026 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.
*
*/

package hostname_test

import (
"testing"

"google.golang.org/grpc/balancer/hostname"
"google.golang.org/grpc/resolver"
)

func TestHostname_SetAndGet(t *testing.T) {
Comment thread
Pranjali-2501 marked this conversation as resolved.
Outdated
ep := resolver.Endpoint{}
if h := hostname.Hostname(ep); h != "" {
t.Errorf("empty = %q", h)
}

ep2 := hostname.Set(ep, "myservice.example.com")
if h := hostname.Hostname(ep2); h != "myservice.example.com" {
t.Errorf("got %q", h)
}

// empty hostname returns same endpoint
ep3 := hostname.Set(ep2, "")
if hostname.Hostname(ep3) != "myservice.example.com" {
t.Error("empty should not overwrite")
}
}
2 changes: 1 addition & 1 deletion balancer/pickfirst/pickfirst.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,10 @@ import (

"google.golang.org/grpc/balancer"
"google.golang.org/grpc/balancer/pickfirst/internal"
"google.golang.org/grpc/balancer/weight"
"google.golang.org/grpc/connectivity"
expstats "google.golang.org/grpc/experimental/stats"
"google.golang.org/grpc/grpclog"
"google.golang.org/grpc/internal/balancer/weight"
"google.golang.org/grpc/internal/envconfig"
internalgrpclog "google.golang.org/grpc/internal/grpclog"
"google.golang.org/grpc/internal/pretty"
Expand Down
3 changes: 2 additions & 1 deletion balancer/pickfirst/pickfirst_ext_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,18 @@ import (
"time"

"github.com/google/go-cmp/cmp"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Remove this new line.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

done

"google.golang.org/grpc"
"google.golang.org/grpc/backoff"
"google.golang.org/grpc/balancer"
pfbalancer "google.golang.org/grpc/balancer/pickfirst"
pfinternal "google.golang.org/grpc/balancer/pickfirst/internal"
"google.golang.org/grpc/balancer/weight"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/connectivity"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/internal"
"google.golang.org/grpc/internal/balancer/stub"
"google.golang.org/grpc/internal/balancer/weight"
"google.golang.org/grpc/internal/channelz"
"google.golang.org/grpc/internal/envconfig"
"google.golang.org/grpc/internal/grpcsync"
Expand Down
3 changes: 2 additions & 1 deletion balancer/ringhash/ring_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ import (
"testing"

xxhash "github.com/cespare/xxhash/v2"
"google.golang.org/grpc/internal/balancer/weight"

"google.golang.org/grpc/balancer/weight"
"google.golang.org/grpc/resolver"
)

Expand Down
2 changes: 1 addition & 1 deletion balancer/ringhash/ringhash.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ import (
"google.golang.org/grpc/balancer/endpointsharding"
"google.golang.org/grpc/balancer/lazy"
"google.golang.org/grpc/balancer/pickfirst"
"google.golang.org/grpc/balancer/weight"
"google.golang.org/grpc/connectivity"
"google.golang.org/grpc/internal/balancer/weight"
"google.golang.org/grpc/internal/grpclog"
"google.golang.org/grpc/internal/pretty"
iringhash "google.golang.org/grpc/internal/ringhash"
Expand Down
2 changes: 1 addition & 1 deletion balancer/ringhash/ringhash_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ import (
"time"

"google.golang.org/grpc/balancer"
"google.golang.org/grpc/balancer/weight"
"google.golang.org/grpc/connectivity"
"google.golang.org/grpc/internal/balancer/weight"
"google.golang.org/grpc/internal/grpctest"
iringhash "google.golang.org/grpc/internal/ringhash"
"google.golang.org/grpc/internal/testutils"
Expand Down
81 changes: 81 additions & 0 deletions balancer/weight/weight.go
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Same here. Please move this to a directory inside of the top-level experimental directory.

Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
*
* Copyright 2026 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.
*
*/

// Package weight contains utilities to manage endpoint weights. Weights are
// used by LB policies such as ringhash to distribute load across multiple
// endpoints.
//
// # Experimental
//
// Notice: This API is EXPERIMENTAL and may be changed or removed in a
// later release.
Comment on lines +25 to +26
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Same as the other comment. Please mention that all APIs in this package are experimental here, and ignore the warnings on every single API.

package weight

import (
"fmt"

"google.golang.org/grpc/resolver"
)

// attributeKey is the type used as the key to store EndpointInfo in the
// Attributes field of resolver.Endpoint.
type attributeKey struct{}

// EndpointInfo will be stored in the Attributes field of Endpoints in order to
// use the ringhash balancer.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This comment needs to be updated, since this is no longer only pertinent to the ring_hash LB policy.

type EndpointInfo struct {
Weight uint32
}

// Equal allows the values to be compared by Attributes.Equal.
func (a EndpointInfo) Equal(o any) bool {
oa, ok := o.(EndpointInfo)
return ok && oa.Weight == a.Weight
}

// Set returns a copy of endpoint in which the Attributes field is updated with
// EndpointInfo.
//
// # Experimental
//
// Notice: This API is EXPERIMENTAL and may be changed or removed in a
// later release.
func Set(endpoint resolver.Endpoint, epInfo EndpointInfo) resolver.Endpoint {
endpoint.Attributes = endpoint.Attributes.WithValue(attributeKey{}, epInfo)
return endpoint
}

// String returns a human-readable representation of EndpointInfo.
// This method is intended for logging, testing, and debugging purposes only.
// Do not rely on the output format, as it is not guaranteed to remain stable.
func (a EndpointInfo) String() string {
return fmt.Sprintf("Weight: %d", a.Weight)
}
Comment on lines 63 to 68
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

There doesn't seem to be any calls to this method. Let's get rid of this.


// FromEndpoint returns the EndpointInfo stored in the Attributes field of an
// endpoint. It returns an empty EndpointInfo if attribute is not found.
//
// # Experimental
//
// Notice: This API is EXPERIMENTAL and may be changed or removed in a
// later release.
func FromEndpoint(endpoint resolver.Endpoint) EndpointInfo {
v := endpoint.Attributes.Value(attributeKey{})
ei, _ := v.(EndpointInfo)
return ei
}
84 changes: 84 additions & 0 deletions balancer/weight/weight_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
*
* Copyright 2026 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.
*
*/

package weight_test

import (
"testing"

"github.com/google/go-cmp/cmp"

"google.golang.org/grpc/attributes"
"google.golang.org/grpc/balancer/weight"
"google.golang.org/grpc/internal/grpctest"
"google.golang.org/grpc/resolver"
)

type s struct {
grpctest.Tester
}

func Test(t *testing.T) {
grpctest.RunSubTests(t, s{})
}

func (s) TestEndpointInfoToAndFromAttributes(t *testing.T) {
tests := []struct {
desc string
inputEndpointInfo weight.EndpointInfo
inputAttributes *attributes.Attributes
wantEndpointInfo weight.EndpointInfo
}{
{
desc: "empty_attributes",
inputEndpointInfo: weight.EndpointInfo{Weight: 100},
inputAttributes: nil,
wantEndpointInfo: weight.EndpointInfo{Weight: 100},
},
{
desc: "non-empty_attributes",
inputEndpointInfo: weight.EndpointInfo{Weight: 100},
inputAttributes: attributes.New("foo", "bar"),
wantEndpointInfo: weight.EndpointInfo{Weight: 100},
},
{
desc: "endpointInfo_not_present_in_empty_attributes",
inputEndpointInfo: weight.EndpointInfo{},
inputAttributes: nil,
wantEndpointInfo: weight.EndpointInfo{},
},
{
desc: "endpointInfo_not_present_in_non-empty_attributes",
inputEndpointInfo: weight.EndpointInfo{},
inputAttributes: attributes.New("foo", "bar"),
wantEndpointInfo: weight.EndpointInfo{},
},
}

for _, test := range tests {
t.Run(test.desc, func(t *testing.T) {
endpoint := resolver.Endpoint{Attributes: test.inputAttributes}
endpoint = weight.Set(endpoint, test.inputEndpointInfo)
gotEndpointInfo := weight.FromEndpoint(endpoint)
if !cmp.Equal(gotEndpointInfo, test.wantEndpointInfo) {
t.Errorf("gotEndpointInfo: %v, wantEndpointInfo: %v", gotEndpointInfo, test.wantEndpointInfo)
}

})
}
}
2 changes: 2 additions & 0 deletions internal/balancer/weight/weight.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
// Package weight contains utilities to manage endpoint weights. Weights are
// used by LB policies such as ringhash to distribute load across multiple
// endpoints.
//
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Instead of putting a deprecated message and creating new files in balancer/weight/, use git move to move the files from internal/balancer/weight/weight.go to balancer/weight/weight.go.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

done - i also added the "Experimental" comment because this is a newly-public API. is that fine?

// Deprecated: use google.golang.org/grpc/balancer/weight instead.
package weight

import (
Expand Down
2 changes: 2 additions & 0 deletions internal/balancer/weight/weight_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@
*
*/

// Deprecated: use google.golang.org/grpc/balancer/weight instead.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Same as above, use git move to move this file also.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

done

package weight_test

import (
"testing"

"github.com/google/go-cmp/cmp"

"google.golang.org/grpc/attributes"
"google.golang.org/grpc/internal/balancer/weight"
"google.golang.org/grpc/internal/grpctest"
Expand Down
2 changes: 1 addition & 1 deletion internal/xds/balancer/cdsbalancer/configbuilder.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import (
"maps"
"slices"

"google.golang.org/grpc/internal/balancer/weight"
"google.golang.org/grpc/balancer/weight"
"google.golang.org/grpc/internal/envconfig"
"google.golang.org/grpc/internal/hierarchy"
internalserviceconfig "google.golang.org/grpc/internal/serviceconfig"
Expand Down
3 changes: 2 additions & 1 deletion internal/xds/balancer/cdsbalancer/configbuilder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,13 @@ import (
"time"

"github.com/google/go-cmp/cmp"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Please remove this newline.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

done

"google.golang.org/grpc/attributes"
"google.golang.org/grpc/balancer"
"google.golang.org/grpc/balancer/pickfirst"
"google.golang.org/grpc/balancer/ringhash"
"google.golang.org/grpc/balancer/roundrobin"
"google.golang.org/grpc/internal/balancer/weight"
"google.golang.org/grpc/balancer/weight"
"google.golang.org/grpc/internal/envconfig"
"google.golang.org/grpc/internal/hierarchy"
iringhash "google.golang.org/grpc/internal/ringhash"
Expand Down
Loading
Loading