This is an automated email from the ASF dual-hosted git repository.

Similarityoung pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/dubbo-go-pixiu.git


The following commit(s) were added to refs/heads/develop by this push:
     new 106afed39 refactor(loadbalancer): gate snapshot fast paths behind an 
internal opt-in token (#971)
106afed39 is described below

commit 106afed39e3447d64b5f63f6c537e6dbbd4cabc6
Author: 承潜 <[email protected]>
AuthorDate: Sat Aug 15 14:27:01 2026 +0800

    refactor(loadbalancer): gate snapshot fast paths behind an internal opt-in 
token (#971)
    
    * refactor(loadbalancer): gate snapshot fast paths behind an internal 
opt-in token
    
    The zero-copy and healthy-only snapshot fast paths were opt-in via two
    exported marker interfaces, ZeroCopySnapshotLoadBalancer and
    HealthyOnlySnapshotLoadBalancer. Because Go interface satisfaction is
    structural, any external load-balancer plugin could implement those method
    names and silently opt into contracts that require never mutating or
    retaining snapshot-owned endpoints — risking request-path mutation, stale
    pointer retention, or data races on the shared snapshot.
    
    Replace the two markers with a single internal opt-in: balancers expose
    SnapshotOptIn() returning snapshotopt.Token, a struct in a new
    internal/snapshotopt package. Only balancers rooted at
    pkg/cluster/loadbalancer can import that package and name the return type,
    so only trusted in-tree balancers can opt in. External plugins cannot
    construct a Token, cannot satisfy the opt-in interface, and therefore
    always fall through to the safe default: full snapshot, defensively copied.
    
    The public extension surface (SnapshotLoadBalancer, PickContext) is
    unchanged; bundled balancers (RoundRobin, Rand, WeightRandom, Maglev,
    RingHash) keep their fast path via the new token. Behavior is otherwise
    identical.
    
    A new test (TestExternalLikeBalancerCannotOptIntoFastPaths) reproduces an
    out-of-tree plugin carrying the old method names and asserts it is treated
    as untrusted: full snapshot, defensive copy, mutation does not escape.
    
    Closes #941
    
    * fix(loadbalancer): keep deprecated snapshot markers compatible
    
    * fix(loadbalancer): close embedding bypass and drop unreleased markers
    
    The internal-token opt-in stops out-of-tree code from declaring
    SnapshotOptIn, but an external plugin could still embed an in-tree
    balancer and gain the method through promotion, silently re-entering the
    zero-copy / healthy-only fast paths. snapshotOptIn now also verifies the
    balancer's concrete type lives under pkg/cluster/loadbalancer, so a
    promoted method from an embedded balancer no longer grants trust.
    
    Remove the HealthyOnlySnapshotLoadBalancer and ZeroCopySnapshotLoadBalancer
    marker interfaces along with the Use* methods and var _ assertions on the
    bundled balancers. The markers were introduced in #932 and never shipped
    in a release, so there is no external caller to stay compatible with;
    keeping them contradicted the PR goal and coupled the balancers to
    deprecated, runtime-ignored APIs.
    
    Add an external-package regression (load_balancer_external_test.go) that
    embeds an in-tree balancer and asserts it still receives the full
    snapshot and a defensive copy.
---
 pkg/cluster/cluster.go                             |  8 +-
 .../internal/snapshotopt/snapshotopt.go            | 38 ++++++++++
 pkg/cluster/loadbalancer/load_balancer.go          | 62 +++++++++++----
 .../loadbalancer/load_balancer_external_test.go    | 87 ++++++++++++++++++++++
 pkg/cluster/loadbalancer/load_balancer_test.go     | 84 ++++++++++++++++++++-
 pkg/cluster/loadbalancer/maglev/maglev_hash.go     |  9 +--
 .../loadbalancer/rand/load_balancer_rand.go        |  9 +--
 pkg/cluster/loadbalancer/ringhash/ring_hash.go     |  9 +--
 pkg/cluster/loadbalancer/roundrobin/round_robin.go |  9 +--
 .../loadbalancer/weightrandom/weight_random.go     |  9 +--
 10 files changed, 273 insertions(+), 51 deletions(-)

diff --git a/pkg/cluster/cluster.go b/pkg/cluster/cluster.go
index 6608e546b..a0adefbbe 100644
--- a/pkg/cluster/cluster.go
+++ b/pkg/cluster/cluster.go
@@ -222,10 +222,10 @@ type EndpointSnapshot struct {
 //     only because *Endpoint and the underlying maps are treated as
 //     read-only after publication.
 //     Any code path that mutates them in place will leak state across
-//     all snapshots alive at the time of the mutation. The
-//     ZeroCopySnapshotLoadBalancer marker on load balancers exists
-//     precisely to opt into this contract; do not introduce new
-//     in-place mutation on snapshot-owned objects.
+//     all snapshots alive at the time of the mutation. The zero-copy
+//     opt-in on load balancers (snapshotopt.Token.ZeroCopy, set via
+//     SnapshotOptIn) exists precisely to opt into this contract; do not
+//     introduce new in-place mutation on snapshot-owned objects.
 func newEndpointSnapshot(config *model.ClusterConfig, previous 
*EndpointSnapshot, inheritRuntimeHealth bool) *EndpointSnapshot {
        var endpoints []*model.Endpoint
        clusterName := ""
diff --git a/pkg/cluster/loadbalancer/internal/snapshotopt/snapshotopt.go 
b/pkg/cluster/loadbalancer/internal/snapshotopt/snapshotopt.go
new file mode 100644
index 000000000..0d4a4ba1f
--- /dev/null
+++ b/pkg/cluster/loadbalancer/internal/snapshotopt/snapshotopt.go
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 snapshotopt carries the opt-in token for snapshot load-balancer fast
+// paths. It lives under internal/ on purpose: only in-tree balancers (packages
+// rooted at pkg/cluster/loadbalancer) can import it and therefore name Token,
+// so only trusted balancers can declare the SnapshotOptIn method that hands
+// these flags to the runtime. External plugins cannot construct a Token, so
+// they cannot opt into zero-copy or healthy-only access and always receive
+// defensively copied snapshot endpoints.
+package snapshotopt
+
+// Token tells the snapshot pick path which fast paths a trusted balancer opts
+// into. The zero value (both false) is the safe default: full snapshot,
+// defensively copied.
+type Token struct {
+       // ZeroCopy is true when the balancer never mutates or retains snapshot
+       // endpoints, so the runtime may hand it the snapshot-owned slices 
directly
+       // instead of defensive copies.
+       ZeroCopy bool
+       // HealthyOnly is true when the balancer never reads 
PickContext.AllEndpoints,
+       // so the runtime can skip populating the full (including unhealthy) 
set.
+       HealthyOnly bool
+}
diff --git a/pkg/cluster/loadbalancer/load_balancer.go 
b/pkg/cluster/loadbalancer/load_balancer.go
index 295de13c7..4b577eab8 100644
--- a/pkg/cluster/loadbalancer/load_balancer.go
+++ b/pkg/cluster/loadbalancer/load_balancer.go
@@ -18,11 +18,14 @@
 package loadbalancer
 
 import (
+       "reflect"
+       "strings"
        "sync"
        "sync/atomic"
 )
 
 import (
+       
"github.com/apache/dubbo-go-pixiu/pkg/cluster/loadbalancer/internal/snapshotopt"
        "github.com/apache/dubbo-go-pixiu/pkg/model"
 )
 
@@ -77,18 +80,48 @@ type SnapshotLoadBalancer interface {
        HandlerWithSnapshot(c PickContext, policy model.LbPolicy) 
*model.Endpoint
 }
 
-// HealthyOnlySnapshotLoadBalancer marks snapshot-aware balancers that do not
-// need PickContext.AllEndpoints. Unmarked snapshot balancers keep receiving
-// the full snapshot for compatibility with custom implementations.
-type HealthyOnlySnapshotLoadBalancer interface {
-       UseHealthyEndpointsOnly() bool
+// snapshotOptInBalancer is the internal opt-in surface for trusted, in-tree
+// snapshot balancers. The method returns snapshotopt.Token, whose type lives 
in
+// an internal package, so only balancers under pkg/cluster/loadbalancer can
+// implement this interface. External plugins cannot name the return type and
+// therefore always fall through to the safe default (full snapshot, 
defensively
+// copied). This is the trust boundary described in issue #941.
+type snapshotOptInBalancer interface {
+       SnapshotOptIn() snapshotopt.Token
 }
 
-// ZeroCopySnapshotLoadBalancer marks trusted balancers that never mutate or
-// retain snapshot endpoints. Other snapshot balancers receive defensive
-// copies.
-type ZeroCopySnapshotLoadBalancer interface {
-       UseZeroCopySnapshot() bool
+// inTreeLoadBalancerPkg is the package prefix that scopes trusted balancers.
+const inTreeLoadBalancerPkg = 
"github.com/apache/dubbo-go-pixiu/pkg/cluster/loadbalancer"
+
+// snapshotOptIn resolves a balancer's opt-in flags. Balancers that do not opt
+// in (including every external plugin, which cannot construct a Token) get the
+// zero value: no zero-copy, full snapshot.
+//
+// The internal return type already keeps out-of-tree code from declaring
+// SnapshotOptIn directly. The trust check additionally rejects external types
+// that gain the method via embedding an in-tree balancer (method promotion
+// would otherwise let them satisfy snapshotOptInBalancer).
+func snapshotOptIn(balancer LoadBalancer) snapshotopt.Token {
+       optIn, ok := balancer.(snapshotOptInBalancer)
+       if !ok || !isInTreeBalancer(balancer) {
+               return snapshotopt.Token{}
+       }
+       return optIn.SnapshotOptIn()
+}
+
+// isInTreeBalancer reports whether the balancer's concrete type is defined
+// under the in-tree load-balancer package tree. An external plugin that embeds
+// an in-tree balancer keeps its own package path here, so it is rejected.
+func isInTreeBalancer(balancer LoadBalancer) bool {
+       t := reflect.TypeOf(balancer)
+       for t != nil && t.Kind() == reflect.Ptr {
+               t = t.Elem()
+       }
+       if t == nil {
+               return false
+       }
+       pkg := t.PkgPath()
+       return pkg == inTreeLoadBalancerPkg || strings.HasPrefix(pkg, 
inTreeLoadBalancerPkg+"/")
 }
 
 // LoadBalancerStrategy load balancer strategy mode
@@ -114,10 +147,10 @@ func PickEndpoint(balancer LoadBalancer, context 
PickContext, policy model.LbPol
 }
 
 // NeedsAllEndpoints reports whether a snapshot-aware balancer should receive
-// PickContext.AllEndpoints on the request path.
+// PickContext.AllEndpoints on the request path. Only trusted in-tree balancers
+// can opt out (HealthyOnly); external plugins always receive the full 
snapshot.
 func NeedsAllEndpoints(balancer LoadBalancer) bool {
-       healthyOnly, ok := balancer.(HealthyOnlySnapshotLoadBalancer)
-       return !ok || !healthyOnly.UseHealthyEndpointsOnly()
+       return !snapshotOptIn(balancer).HealthyOnly
 }
 
 // ConsistentHashForHealthyEndpoints returns a consistent hash view that only
@@ -143,8 +176,7 @@ func pickEndpoint(balancer LoadBalancer, context 
PickContext, policy model.LbPol
        }
        if snapshotBalancer, ok := balancer.(SnapshotLoadBalancer); ok {
                snapshotContext := context
-               zeroCopy, ok := balancer.(ZeroCopySnapshotLoadBalancer)
-               if !ok || !zeroCopy.UseZeroCopySnapshot() {
+               if !snapshotOptIn(balancer).ZeroCopy {
                        snapshotContext = defensiveSnapshotPickContext(context)
                }
                endpoint := 
snapshotBalancer.HandlerWithSnapshot(snapshotContext, policy)
diff --git a/pkg/cluster/loadbalancer/load_balancer_external_test.go 
b/pkg/cluster/loadbalancer/load_balancer_external_test.go
new file mode 100644
index 000000000..a66caac35
--- /dev/null
+++ b/pkg/cluster/loadbalancer/load_balancer_external_test.go
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 loadbalancer_test
+
+import (
+       "testing"
+)
+
+import (
+       "github.com/stretchr/testify/assert"
+)
+
+import (
+       "github.com/apache/dubbo-go-pixiu/pkg/cluster/loadbalancer"
+       "github.com/apache/dubbo-go-pixiu/pkg/cluster/loadbalancer/roundrobin"
+       "github.com/apache/dubbo-go-pixiu/pkg/model"
+)
+
+// embeddingPlugin is an out-of-tree balancer that embeds an in-tree balancer.
+// Method promotion gives it SnapshotOptIn, so structural interface 
satisfaction
+// alone would wrongly grant it the snapshot fast paths. The runtime trust 
check
+// keys on the concrete type's package, which is this external test package, so
+// the plugin must still be treated as untrusted.
+type embeddingPlugin struct {
+       roundrobin.RoundRobin
+       seenHealthyEndpoints []*model.Endpoint
+}
+
+func (p *embeddingPlugin) HandlerWithSnapshot(c loadbalancer.PickContext, _ 
model.LbPolicy) *model.Endpoint {
+       p.seenHealthyEndpoints = c.HealthyEndpoints
+       if len(c.HealthyEndpoints) == 0 {
+               return nil
+       }
+       c.HealthyEndpoints[0].Metadata["weight"] = "mutated"
+       return c.HealthyEndpoints[0]
+}
+
+// TestEmbeddingInTreeBalancerCannotOptIntoFastPaths is the trust-boundary
+// regression for the embedding bypass: an external type that promotes
+// SnapshotOptIn from an embedded in-tree balancer must not receive the
+// healthy-only or zero-copy fast paths.
+func TestEmbeddingInTreeBalancerCannotOptIntoFastPaths(t *testing.T) {
+       plugin := &embeddingPlugin{}
+
+       // HealthyOnly fast path must be denied: external embedder still gets 
the
+       // full snapshot.
+       assert.True(t, loadbalancer.NeedsAllEndpoints(plugin),
+               "embedding an in-tree balancer must not promote the 
healthy-only fast path")
+
+       healthy := &model.Endpoint{ID: "healthy", Metadata: 
map[string]string{"weight": "1"}}
+       cluster := &model.ClusterConfig{
+               Name:      "embedding-trust-boundary",
+               Endpoints: []*model.Endpoint{healthy},
+       }
+
+       got := loadbalancer.PickEndpoint(plugin, loadbalancer.PickContext{
+               AllEndpoints:     []*model.Endpoint{healthy},
+               Config:           cluster,
+               HealthyEndpoints: []*model.Endpoint{healthy},
+       }, nil)
+
+       // ZeroCopy fast path must be denied: the plugin mutates what it 
receives,
+       // and that mutation must not escape to the snapshot-owned endpoint.
+       if assert.NotNil(t, got) {
+               assert.NotSame(t, healthy, got, "embedder must receive a 
defensive copy")
+       }
+       if assert.Len(t, plugin.seenHealthyEndpoints, 1) {
+               assert.NotSame(t, healthy, plugin.seenHealthyEndpoints[0])
+       }
+       assert.Equal(t, map[string]string{"weight": "1"}, healthy.Metadata,
+               "embedder mutation must not escape to the snapshot-owned 
endpoint")
+}
diff --git a/pkg/cluster/loadbalancer/load_balancer_test.go 
b/pkg/cluster/loadbalancer/load_balancer_test.go
index 88b0eb4f9..a1fa9ed1b 100644
--- a/pkg/cluster/loadbalancer/load_balancer_test.go
+++ b/pkg/cluster/loadbalancer/load_balancer_test.go
@@ -30,6 +30,7 @@ import (
 )
 
 import (
+       
"github.com/apache/dubbo-go-pixiu/pkg/cluster/loadbalancer/internal/snapshotopt"
        "github.com/apache/dubbo-go-pixiu/pkg/model"
 )
 
@@ -44,6 +45,37 @@ type mutatingSnapshotLoadBalancer struct{}
 type unhealthySnapshotLoadBalancer struct{}
 type healthyOnlySnapshotLoadBalancer struct{}
 
+// externalLikeSnapshotLoadBalancer mimics an out-of-tree plugin that copied 
the
+// pre-#941 marker method names (UseZeroCopySnapshot / 
UseHealthyEndpointsOnly).
+// Because the opt-in now flows through SnapshotOptIn returning an 
internal-only
+// token, these methods grant no fast path: the balancer must be treated as
+// untrusted (defensive copy, full snapshot). It also mutates the endpoints it
+// receives so the test can prove it got a copy.
+type externalLikeSnapshotLoadBalancer struct {
+       seenAllEndpoints     []*model.Endpoint
+       seenHealthyEndpoints []*model.Endpoint
+}
+
+func (*externalLikeSnapshotLoadBalancer) UseZeroCopySnapshot() bool     { 
return true }
+func (*externalLikeSnapshotLoadBalancer) UseHealthyEndpointsOnly() bool { 
return true }
+
+func (*externalLikeSnapshotLoadBalancer) Handler(_ *model.ClusterConfig, _ 
model.LbPolicy) *model.Endpoint {
+       return nil
+}
+
+func (b *externalLikeSnapshotLoadBalancer) HandlerWithSnapshot(c PickContext, 
_ model.LbPolicy) *model.Endpoint {
+       b.seenAllEndpoints = c.AllEndpoints
+       b.seenHealthyEndpoints = c.HealthyEndpoints
+       if len(c.HealthyEndpoints) == 0 {
+               return nil
+       }
+       c.HealthyEndpoints[0].Metadata["weight"] = "mutated"
+       if len(c.AllEndpoints) > 1 {
+               c.AllEndpoints[1].Metadata["weight"] = "mutated-unhealthy"
+       }
+       return c.HealthyEndpoints[0]
+}
+
 var _ LoadBalancer = (*legacyLoadBalancer)(nil)
 
 // healthyByIDIndex is a test double for the snapshot's O(1) healthy-by-ID
@@ -153,8 +185,8 @@ func (healthyOnlySnapshotLoadBalancer) 
HandlerWithSnapshot(_ PickContext, _ mode
        return nil
 }
 
-func (healthyOnlySnapshotLoadBalancer) UseHealthyEndpointsOnly() bool {
-       return true
+func (healthyOnlySnapshotLoadBalancer) SnapshotOptIn() snapshotopt.Token {
+       return snapshotopt.Token{HealthyOnly: true}
 }
 
 func (b *blockingLegacyLoadBalancer) Handler(c *model.ClusterConfig, _ 
model.LbPolicy) *model.Endpoint {
@@ -357,6 +389,54 @@ func 
TestNeedsAllEndpointsKeepsCompatibilityForUnmarkedSnapshotLoadBalancer(t *t
        assert.False(t, NeedsAllEndpoints(healthyOnlySnapshotLoadBalancer{}))
 }
 
+// TestExternalLikeBalancerCannotOptIntoFastPaths is the trust-boundary
+// regression for issue #941. A balancer that reproduces the old marker method
+// names but cannot produce the internal opt-in token must be treated as
+// untrusted: it receives the full snapshot (NeedsAllEndpoints true) and a
+// defensive copy (its mutation must not escape to the caller's endpoints).
+func TestExternalLikeBalancerCannotOptIntoFastPaths(t *testing.T) {
+       balancer := &externalLikeSnapshotLoadBalancer{}
+       assert.True(t, NeedsAllEndpoints(balancer),
+               "external-like balancer must not opt out of AllEndpoints via 
the old method name")
+
+       healthy := &model.Endpoint{
+               ID:       "healthy",
+               Metadata: map[string]string{"weight": "1"},
+       }
+       unhealthy := &model.Endpoint{
+               ID:        "unhealthy",
+               Metadata:  map[string]string{"weight": "2"},
+               UnHealthy: true,
+       }
+       cluster := &model.ClusterConfig{
+               Name:      "external-like-trust-boundary",
+               Endpoints: []*model.Endpoint{healthy, unhealthy},
+       }
+
+       got := PickEndpoint(balancer, PickContext{
+               AllEndpoints:     []*model.Endpoint{healthy, unhealthy},
+               Config:           cluster,
+               HealthyEndpoints: []*model.Endpoint{healthy},
+       }, nil)
+
+       if assert.NotNil(t, got) {
+               assert.NotSame(t, healthy, got, "untrusted balancer must 
receive a defensive copy")
+       }
+       if assert.Len(t, balancer.seenAllEndpoints, 2, "untrusted balancer must 
receive the full snapshot") {
+               assert.NotSame(t, healthy, balancer.seenAllEndpoints[0])
+               assert.NotSame(t, unhealthy, balancer.seenAllEndpoints[1])
+               assert.Equal(t, "healthy", balancer.seenAllEndpoints[0].ID)
+               assert.Equal(t, "unhealthy", balancer.seenAllEndpoints[1].ID)
+       }
+       if assert.Len(t, balancer.seenHealthyEndpoints, 1) {
+               assert.NotSame(t, healthy, balancer.seenHealthyEndpoints[0])
+       }
+       assert.Equal(t, map[string]string{"weight": "1"}, healthy.Metadata,
+               "untrusted balancer mutation must not escape to the 
snapshot-owned endpoint")
+       assert.Equal(t, map[string]string{"weight": "2"}, unhealthy.Metadata,
+               "untrusted balancer mutation must not escape to the full 
snapshot endpoint")
+}
+
 func TestDefensiveSnapshotPickContextClearsHealthyByID(t *testing.T) {
        healthy := []*model.Endpoint{
                {ID: "ep-1", Address: model.SocketAddress{Address: "127.0.0.1", 
Port: 8080}},
diff --git a/pkg/cluster/loadbalancer/maglev/maglev_hash.go 
b/pkg/cluster/loadbalancer/maglev/maglev_hash.go
index 6eca2d903..6cf7383f6 100644
--- a/pkg/cluster/loadbalancer/maglev/maglev_hash.go
+++ b/pkg/cluster/loadbalancer/maglev/maglev_hash.go
@@ -19,6 +19,7 @@ package maglev
 
 import (
        "github.com/apache/dubbo-go-pixiu/pkg/cluster/loadbalancer"
+       
"github.com/apache/dubbo-go-pixiu/pkg/cluster/loadbalancer/internal/snapshotopt"
        "github.com/apache/dubbo-go-pixiu/pkg/cluster/loadbalancer/ringhash"
        "github.com/apache/dubbo-go-pixiu/pkg/logger"
        "github.com/apache/dubbo-go-pixiu/pkg/model"
@@ -50,12 +51,8 @@ func NewMaglevHash(config model.ConsistentHash, endpoints 
[]*model.Endpoint) mod
 
 type MaglevHash struct{}
 
-func (MaglevHash) UseHealthyEndpointsOnly() bool {
-       return true
-}
-
-func (MaglevHash) UseZeroCopySnapshot() bool {
-       return true
+func (MaglevHash) SnapshotOptIn() snapshotopt.Token {
+       return snapshotopt.Token{ZeroCopy: true, HealthyOnly: true}
 }
 
 func (m MaglevHash) Handler(c *model.ClusterConfig, policy model.LbPolicy) 
*model.Endpoint {
diff --git a/pkg/cluster/loadbalancer/rand/load_balancer_rand.go 
b/pkg/cluster/loadbalancer/rand/load_balancer_rand.go
index 6335828ce..98d48f56d 100644
--- a/pkg/cluster/loadbalancer/rand/load_balancer_rand.go
+++ b/pkg/cluster/loadbalancer/rand/load_balancer_rand.go
@@ -23,6 +23,7 @@ import (
 
 import (
        "github.com/apache/dubbo-go-pixiu/pkg/cluster/loadbalancer"
+       
"github.com/apache/dubbo-go-pixiu/pkg/cluster/loadbalancer/internal/snapshotopt"
        "github.com/apache/dubbo-go-pixiu/pkg/model"
 )
 
@@ -32,12 +33,8 @@ func init() {
 
 type Rand struct{}
 
-func (Rand) UseHealthyEndpointsOnly() bool {
-       return true
-}
-
-func (Rand) UseZeroCopySnapshot() bool {
-       return true
+func (Rand) SnapshotOptIn() snapshotopt.Token {
+       return snapshotopt.Token{ZeroCopy: true, HealthyOnly: true}
 }
 
 // randIntn lets tests replace randomness with deterministic choices.
diff --git a/pkg/cluster/loadbalancer/ringhash/ring_hash.go 
b/pkg/cluster/loadbalancer/ringhash/ring_hash.go
index fcd62d019..d9bdbe839 100644
--- a/pkg/cluster/loadbalancer/ringhash/ring_hash.go
+++ b/pkg/cluster/loadbalancer/ringhash/ring_hash.go
@@ -27,6 +27,7 @@ import (
 
 import (
        "github.com/apache/dubbo-go-pixiu/pkg/cluster/loadbalancer"
+       
"github.com/apache/dubbo-go-pixiu/pkg/cluster/loadbalancer/internal/snapshotopt"
        "github.com/apache/dubbo-go-pixiu/pkg/logger"
        "github.com/apache/dubbo-go-pixiu/pkg/model"
 )
@@ -58,12 +59,8 @@ func NewRingHash(config model.ConsistentHash, endpoints 
[]*model.Endpoint) model
 
 type RingHashing struct{}
 
-func (RingHashing) UseHealthyEndpointsOnly() bool {
-       return true
-}
-
-func (RingHashing) UseZeroCopySnapshot() bool {
-       return true
+func (RingHashing) SnapshotOptIn() snapshotopt.Token {
+       return snapshotopt.Token{ZeroCopy: true, HealthyOnly: true}
 }
 
 func (r RingHashing) Handler(c *model.ClusterConfig, policy model.LbPolicy) 
*model.Endpoint {
diff --git a/pkg/cluster/loadbalancer/roundrobin/round_robin.go 
b/pkg/cluster/loadbalancer/roundrobin/round_robin.go
index 6680a2412..ecd787ddb 100644
--- a/pkg/cluster/loadbalancer/roundrobin/round_robin.go
+++ b/pkg/cluster/loadbalancer/roundrobin/round_robin.go
@@ -23,6 +23,7 @@ import (
 
 import (
        "github.com/apache/dubbo-go-pixiu/pkg/cluster/loadbalancer"
+       
"github.com/apache/dubbo-go-pixiu/pkg/cluster/loadbalancer/internal/snapshotopt"
        "github.com/apache/dubbo-go-pixiu/pkg/model"
 )
 
@@ -32,12 +33,8 @@ func init() {
 
 type RoundRobin struct{}
 
-func (RoundRobin) UseHealthyEndpointsOnly() bool {
-       return true
-}
-
-func (RoundRobin) UseZeroCopySnapshot() bool {
-       return true
+func (RoundRobin) SnapshotOptIn() snapshotopt.Token {
+       return snapshotopt.Token{ZeroCopy: true, HealthyOnly: true}
 }
 
 func (r RoundRobin) Handler(c *model.ClusterConfig, policy model.LbPolicy) 
*model.Endpoint {
diff --git a/pkg/cluster/loadbalancer/weightrandom/weight_random.go 
b/pkg/cluster/loadbalancer/weightrandom/weight_random.go
index e81785bdc..2e8d2b949 100644
--- a/pkg/cluster/loadbalancer/weightrandom/weight_random.go
+++ b/pkg/cluster/loadbalancer/weightrandom/weight_random.go
@@ -24,6 +24,7 @@ import (
 
 import (
        "github.com/apache/dubbo-go-pixiu/pkg/cluster/loadbalancer"
+       
"github.com/apache/dubbo-go-pixiu/pkg/cluster/loadbalancer/internal/snapshotopt"
        "github.com/apache/dubbo-go-pixiu/pkg/model"
 )
 
@@ -40,12 +41,8 @@ type weightedEndpoint struct {
 // It assigns weights to endpoints and uses these weights to influence the 
probability of selection.
 type WeightRandom struct{}
 
-func (WeightRandom) UseHealthyEndpointsOnly() bool {
-       return true
-}
-
-func (WeightRandom) UseZeroCopySnapshot() bool {
-       return true
+func (WeightRandom) SnapshotOptIn() snapshotopt.Token {
+       return snapshotopt.Token{ZeroCopy: true, HealthyOnly: true}
 }
 
 func (w WeightRandom) Handler(c *model.ClusterConfig, policy model.LbPolicy) 
*model.Endpoint {

Reply via email to