Skip to content

Conversation

@Pranjali-2501
Copy link
Contributor

@Pranjali-2501 Pranjali-2501 commented Dec 6, 2025

This PR updates the internal xdsresource.Endpoint struct to contain a resolver.Endpoint instead of a []string to store the list of addresses associated with the endpoint gRFC A81. This change standardizes how backend information is stored and ensures that attributes (such as Hostname) are correctly associated with the endpoint hierarchy.

Key Changes:

Struct Update:

  • xdsresource.Endpoint now includes a ResolverEndpoint field (of type resolver.Endpoint) to store addresses and attributes. Remove the existing Address field (of type []string) and store address as a resolver.Endpoint field.

Attribute Handling:

  • Added SetHostname and GetHostname helpers to manage hostname metadata within resolver.Endpoint.Attributes.

Parsing Logic:

  • Updated parseEndpoints in unmarshal_eds.go to correctly populate the resolver.Endpoint object.

RELEASE NOTES: N/A

@Pranjali-2501 Pranjali-2501 added this to the 1.78 Release milestone Dec 6, 2025
@Pranjali-2501 Pranjali-2501 added Type: Feature New features or improvements in behavior Area: xDS Includes everything xDS related, including LB policies used with xDS. labels Dec 6, 2025
@codecov
Copy link

codecov bot commented Dec 6, 2025

Codecov Report

❌ Patch coverage is 85.00000% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 83.22%. Comparing base (4c27cc8) to head (578caf3).
⚠️ Report is 4 commits behind head on master.

Files with missing lines Patch % Lines
...nternal/xds/xdsclient/xdsresource/unmarshal_eds.go 82.35% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #8750      +/-   ##
==========================================
- Coverage   83.28%   83.22%   -0.07%     
==========================================
  Files         418      418              
  Lines       32367    32396      +29     
==========================================
+ Hits        26958    26961       +3     
- Misses       4034     4052      +18     
- Partials     1375     1383       +8     
Files with missing lines Coverage Δ
...rnal/xds/balancer/clusterresolver/configbuilder.go 94.11% <100.00%> (ø)
...nternal/xds/xdsclient/xdsresource/unmarshal_eds.go 92.76% <82.35%> (-1.57%) ⬇️

... and 26 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@Pranjali-2501 Pranjali-2501 self-assigned this Dec 6, 2025
@Pranjali-2501 Pranjali-2501 requested a review from easwars December 7, 2025 15:25
@Pranjali-2501 Pranjali-2501 changed the title xds: refactor xdsresource.Endpoint to embed resolver.Endpoint xds: refactor xdsresource.Endpoint to embed resolver.Endpoint (gRFC A81) Dec 8, 2025
@Pranjali-2501 Pranjali-2501 requested a review from mbissa December 8, 2025 10:09
// TestConcurrentChannels verifies that we can create multiple gRPC channels
// concurrently with a shared XDSClient, each of which will create a new LRS
// stream without any race.
func (s) TestConcurrentChannels(t *testing.T) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Please don't remove the (s) receiver.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

EndpointHealthStatusDegraded
)

// Endpoint contains information of an endpoint.
Copy link
Contributor

Choose a reason for hiding this comment

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

While you are here, could you also please file an issue and add a TODO here to capture the fact that we could completely get rid of this struct, and encode all the information associated with the endpoint in the resolver.Endpoint struct.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

Comment on lines 38 to 42
type hostnameType string

// hostnameKey is the key to store the hostname key attribute in
// a resolver.Endpoint attribute.
const hostnameKey = hostnameType("grpc.resolver.xdsresource.hostname")
Copy link
Contributor

Choose a reason for hiding this comment

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

Instead of a type and a const, this could be simplified as:

type hostnameKeyType struct{}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

// a resolver.Endpoint attribute.
const hostnameKey = hostnameType("grpc.resolver.xdsresource.hostname")

// SetHostname sets the hostname key for this resolver.Endpoint attribute.
Copy link
Contributor

Choose a reason for hiding this comment

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

How about:

// SetHostname returns a copy of the given endpoint with hostname added as an attribute.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

Comment on lines +46 to +48
if hostname == "" {
return endpoint
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Why would someone call this function with an empty hostname? And why is it important to not include the empty hostname in the attributes. If there are valid reasons for doing this, it should be mentioned in the docstring.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The hostname should be non-empty for :authority rewriting.
Added a comment.

Comment on lines 154 to 156
endpoint := resolver.Endpoint{
Addresses: address,
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: Literal structs with a single field could be initialized on a single line.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

Comment on lines 49 to 51
resolverEndpoint := resolver.Endpoint{
Addresses: address,
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: Literal structs with a single field could be initialized on a single line.

Copy link
Contributor 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/protobuf/types/known/wrapperspb"
)

func getResolverEndpoint(addr []string, hostname string) resolver.Endpoint {
Copy link
Contributor

Choose a reason for hiding this comment

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

The get prefix here makes it look like this is a getter. Please rename it to buildResolverEndpoint or makeResolverEndpoint.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

Comment on lines 553 to 555
ResolverEndpoint: resolver.Endpoint{
Addresses: []resolver.Address{{Addr: "addr-1-1"}},
},
Copy link
Contributor

Choose a reason for hiding this comment

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

Here and elsewhere in this test file, you could make such lines be one a one-liner since only a single field is being set in the resolver.Endpoint.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done.

Comment on lines 254 to 255
newEP := e
newEP.Addresses = make([]resolver.Address, len(e.Addresses))
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we really need this newEndpoints and newEP local variables. Can we make the modifications to the individual endpoints in the endpoints slice, because that is what was happening earlier.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In the previous implementation, xdsresource.Endpoint stored raw address strings, so we were forced to construct a brand new resolver.Endpoint instance in configbuilder.priorityLocalitiesToClusterImpl().

With this refactor, the endpoints slice contains resolver.Endpoint objects that come directly from the xdsClient's cache. If we modify endpoints or its Addresses slice directly in clusterresolver, we are modifying the shared underlying memory which causes a race.

I confirmed this behavior during testing—without the explicit copy, TestConcurrentChannels fails with a data race. The newEndpoints/newEP variables ensure we are modifying a private copy.

But I have again changed the implementation. Instead of creating newEndpoints/newEP variables in clusterresolver, I'm creating a new resovler.Endpoint in configbuilder.priorityLocalitiesToClusterImpl().

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area: xDS Includes everything xDS related, including LB policies used with xDS. Type: Feature New features or improvements in behavior

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants