This is an automated email from the ASF dual-hosted git repository.
Alanxtl pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/dubbo-go.git
The following commit(s) were added to refs/heads/develop by this push:
new 422a66ada fix(polaris): resolve nil pointer panic on startup and route
miss (#3303) (#3697)
422a66ada is described below
commit 422a66ada0b99b6e3adca22f8d86c8ea6a92983c
Author: hysu <[email protected]>
AuthorDate: Mon Aug 24 00:03:20 2026 -0700
fix(polaris): resolve nil pointer panic on startup and route miss (#3303)
(#3697)
* fix(polaris): resolve nil pointer panic on startup and route miss (#3303)
- Disable route filtering during directory initialization.
- Fall back to all instances in polarisRouter.Route on route miss.
- Add nil guards to failfast/failsafe/failback cluster
Signed-off-by: hysu <[email protected]>
* refactor: replace perrors with fmt.Errorf per code review
Signed-off-by: hysu <[email protected]>
* refactor: replace fmt.Errorf with errors.New
Signed-off-by: hysu <[email protected]>
---------
Signed-off-by: hysu <[email protected]>
---
cluster/cluster/failback/cluster_invoker.go | 3 +++
cluster/cluster/failfast/cluster_invoker.go | 4 ++++
cluster/cluster/failfast/cluster_test.go | 24 ++++++++++++++++++++++++
cluster/cluster/failsafe/cluster_invoker.go | 4 ++++
cluster/router/polaris/router.go | 6 ++++++
registry/polaris/registry.go | 4 ++++
6 files changed, 45 insertions(+)
diff --git a/cluster/cluster/failback/cluster_invoker.go
b/cluster/cluster/failback/cluster_invoker.go
index 6cb18eac9..09871dc42 100644
--- a/cluster/cluster/failback/cluster_invoker.go
+++ b/cluster/cluster/failback/cluster_invoker.go
@@ -190,6 +190,9 @@ func (invoker *failbackClusterInvoker) Invoke(ctx
context.Context, invocation pr
invoked := make([]protocolbase.Invoker, 0, len(invokers))
ivk := invoker.DoSelect(loadBalance, invocation, invokers, invoked)
// DO INVOKE
+ if ivk == nil {
+ return &result.RPCResult{Err: errors.New("invoker is nil")}
+ }
res := ivk.Invoke(ctx, invocation)
if res.Error() != nil {
timerTask := newRetryTimerTask(loadBalance, invocation,
invokers, ivk, invoker)
diff --git a/cluster/cluster/failfast/cluster_invoker.go
b/cluster/cluster/failfast/cluster_invoker.go
index 4d04695ec..e475174ad 100644
--- a/cluster/cluster/failfast/cluster_invoker.go
+++ b/cluster/cluster/failfast/cluster_invoker.go
@@ -19,6 +19,7 @@ package failfast
import (
"context"
+ "errors"
)
import (
@@ -54,5 +55,8 @@ func (invoker *failfastClusterInvoker) Invoke(ctx
context.Context, invocation pr
}
ivk := invoker.DoSelect(loadbalance, invocation, invokers, nil)
+ if ivk == nil {
+ return &result.RPCResult{Err: errors.New("invoker is nil")}
+ }
return ivk.Invoke(ctx, invocation)
}
diff --git a/cluster/cluster/failfast/cluster_test.go
b/cluster/cluster/failfast/cluster_test.go
index 6e1fc7976..ff83d1a56 100644
--- a/cluster/cluster/failfast/cluster_test.go
+++ b/cluster/cluster/failfast/cluster_test.go
@@ -105,3 +105,27 @@ func TestFailfastInvokeFail(t *testing.T) {
assert.Equal(t, "error", result.Error().Error())
assert.Nil(t, result.Result())
}
+
+// TestFailfastInvokeWithNoAvailableProvider verifies that invoking with
+// no available providers returns an error instead of panicking.
+func TestFailfastInvokeWithNoAvailableProvider(t *testing.T) {
+ ctrl := gomock.NewController(t)
+ defer ctrl.Finish()
+
+ extension.SetLoadbalance("random", random.NewRandomLoadBalance)
+
+ // Simulate unavailable invoker causing DoSelect to return nil.
+ invoker := mock.NewMockInvoker(ctrl)
+ invoker.EXPECT().IsAvailable().Return(false).AnyTimes()
+ invoker.EXPECT().GetURL().Return(failfastUrl).AnyTimes()
+
+ staticDir := static.NewDirectory([]base.Invoker{invoker})
+ clusterInvoker := newFailfastCluster().Join(staticDir)
+
+ require.NotPanics(t, func() {
+ res := clusterInvoker.Invoke(context.Background(),
&invocation.RPCInvocation{})
+ // Must return an error, not panic
+ require.NotNil(t, res)
+ require.Error(t, res.Error())
+ })
+}
diff --git a/cluster/cluster/failsafe/cluster_invoker.go
b/cluster/cluster/failsafe/cluster_invoker.go
index 537690726..912f46212 100644
--- a/cluster/cluster/failsafe/cluster_invoker.go
+++ b/cluster/cluster/failsafe/cluster_invoker.go
@@ -19,6 +19,7 @@ package failsafe
import (
"context"
+ "errors"
)
import (
@@ -74,6 +75,9 @@ func (invoker *failsafeClusterInvoker) Invoke(ctx
context.Context, invocation pr
ivk := invoker.DoSelect(loadbalance, invocation, invokers, invoked)
// DO INVOKE
+ if ivk == nil {
+ return &result.RPCResult{Err: errors.New("invoker is nil")}
+ }
res = ivk.Invoke(ctx, invocation)
if res.Error() != nil {
// ignore
diff --git a/cluster/router/polaris/router.go b/cluster/router/polaris/router.go
index aa3bc32f9..21bb74773 100644
--- a/cluster/router/polaris/router.go
+++ b/cluster/router/polaris/router.go
@@ -178,6 +178,7 @@ func (p *polarisRouter) Route(invokers []base.Invoker, url
*common.URL,
resp, err := p.routerAPI.ProcessRouters(&req)
if err != nil {
+ logger.Warnf("[Router][Polaris] route miss, fallback: %+v", err)
return invokers
}
@@ -188,6 +189,11 @@ func (p *polarisRouter) Route(invokers []base.Invoker, url
*common.URL,
}
}
+ if len(ret) == 0 {
+ logger.Warn("[Router][Polaris] route rule yielded no invokers,
fallback")
+ return invokers
+ }
+
return ret
}
diff --git a/registry/polaris/registry.go b/registry/polaris/registry.go
index b23bf67ce..a903980d2 100644
--- a/registry/polaris/registry.go
+++ b/registry/polaris/registry.go
@@ -270,6 +270,10 @@ func (pr *polarisRegistry) LoadSubscribeInstances(url
*common.URL, notify regist
GetInstancesRequest: model.GetInstancesRequest{
Service: serviceName,
Namespace: pr.namespace,
+ // Polaris evaluates routing rules during GetInstances
by default. At subscribe
+ // time there is no uid label, so the filter rejects
all instances. Skip it here;
+ // polarisRouter.Route handles per-call routing instead.
+ SkipRouteFilter: true,
},
})
if err != nil {