You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dubbo.apache.org by xu...@apache.org on 2025/11/16 12:32:08 UTC

(dubbo-go) branch develop updated: fix: nil pointer bug (#3071)

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

xuetaoli 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 58805d6cf fix: nil pointer bug (#3071)
58805d6cf is described below

commit 58805d6cfe5d0ad607708ed35e625a3959f493fb
Author: Xuetao Li <m1...@163.com>
AuthorDate: Sun Nov 16 20:31:58 2025 +0800

    fix: nil pointer bug (#3071)
    
    * fix: nil pointer bug
---
 cluster/cluster/failover/cluster_invoker.go      |   5 +-
 cluster/cluster/failover/cluster_invoker_test.go | 176 +++++++++++++++++++++++
 2 files changed, 179 insertions(+), 2 deletions(-)

diff --git a/cluster/cluster/failover/cluster_invoker.go b/cluster/cluster/failover/cluster_invoker.go
index c1248a3d4..6b54d7290 100644
--- a/cluster/cluster/failover/cluster_invoker.go
+++ b/cluster/cluster/failover/cluster_invoker.go
@@ -85,9 +85,10 @@ func (invoker *failoverClusterInvoker) Invoke(ctx context.Context, invocation pr
 		}
 		invoked = append(invoked, ivk)
 		// DO INVOKE
+		ivkURL := ivk.GetURL().Key()
 		res = ivk.Invoke(ctx, invocation)
 		if res.Error() != nil && !isBizError(res.Error()) {
-			providers = append(providers, ivk.GetURL().Key())
+			providers = append(providers, ivkURL)
 			continue
 		}
 		return res
@@ -104,7 +105,7 @@ func (invoker *failoverClusterInvoker) Invoke(ctx context.Context, invocation pr
 	}
 
 	logger.Errorf(fmt.Sprintf("Failed to invoke the method %v in the service %v. "+
-		"Tried %v times of the providers %v (%v/%v)from the registry %v on the consumer %v using the dubbo version %v. "+
+		"Tried %v times of the providers %v (%v/%v) from the registry %v on the consumer %v using the dubbo version %v. "+
 		"Last error is %+v.", methodName, invokerSvc, retries, providers, len(providers), len(invokers),
 		invokerUrl, ip, constant.Version, res.Error().Error()))
 
diff --git a/cluster/cluster/failover/cluster_invoker_test.go b/cluster/cluster/failover/cluster_invoker_test.go
new file mode 100644
index 000000000..35584cdd4
--- /dev/null
+++ b/cluster/cluster/failover/cluster_invoker_test.go
@@ -0,0 +1,176 @@
+/*
+ * 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 failover
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"testing"
+)
+
+import (
+	"github.com/golang/mock/gomock"
+
+	"github.com/stretchr/testify/assert"
+)
+
+import (
+	clusterpkg "dubbo.apache.org/dubbo-go/v3/cluster/cluster"
+	"dubbo.apache.org/dubbo-go/v3/cluster/directory/static"
+	"dubbo.apache.org/dubbo-go/v3/cluster/loadbalance/random"
+	"dubbo.apache.org/dubbo-go/v3/common"
+	"dubbo.apache.org/dubbo-go/v3/common/constant"
+	"dubbo.apache.org/dubbo-go/v3/common/extension"
+	"dubbo.apache.org/dubbo-go/v3/protocol/base"
+	"dubbo.apache.org/dubbo-go/v3/protocol/invocation"
+	"dubbo.apache.org/dubbo-go/v3/protocol/mock"
+	"dubbo.apache.org/dubbo-go/v3/protocol/result"
+)
+
+var broadcastUrl, _ = common.NewURL(
+	fmt.Sprintf("dubbo://%s:%d/com.ikurento.user.UserProvider", constant.LocalHostValue, constant.DefaultPort))
+
+func registerFailover(mockInvokers ...*mock.MockInvoker) base.Invoker {
+	extension.SetLoadbalance("random", random.NewRandomLoadBalance)
+
+	invokers := []base.Invoker{}
+	for _, ivk := range mockInvokers {
+		invokers = append(invokers, ivk)
+		ivk.EXPECT().GetURL().Return(broadcastUrl).AnyTimes()
+	}
+	staticDir := static.NewDirectory(invokers)
+
+	cluster := newFailoverClusterInvoker(staticDir)
+	return cluster
+}
+
+func TestBroadcastInvokeSuccess(t *testing.T) {
+	ctrl := gomock.NewController(t)
+	defer ctrl.Finish()
+
+	invokers := make([]*mock.MockInvoker, 0)
+
+	mockResult := &result.RPCResult{Rest: clusterpkg.Rest{Tried: 0, Success: true}}
+	for i := 0; i < 3; i++ {
+		invoker := mock.NewMockInvoker(ctrl)
+		invokers = append(invokers, invoker)
+		invoker.EXPECT().Invoke(gomock.Any(), gomock.Any()).Return(mockResult).AnyTimes()
+		invoker.EXPECT().IsAvailable().Return(true).AnyTimes() // Mock IsAvailable to return true
+	}
+
+	clusterInvoker := registerFailover(invokers...)
+
+	result := clusterInvoker.Invoke(context.Background(), &invocation.RPCInvocation{})
+	assert.Equal(t, mockResult, result)
+}
+
+func TestBroadcastInvokeFailedOver(t *testing.T) {
+	ctrl := gomock.NewController(t)
+	defer ctrl.Finish()
+
+	invokers := make([]*mock.MockInvoker, 0)
+
+	mockResult := &result.RPCResult{Rest: clusterpkg.Rest{Tried: 0, Success: true}}
+	mockFailedResult := &result.RPCResult{Err: errors.New("just failed")}
+
+	// Create 10 invokers that return a successful result
+	for i := 0; i < 10; i++ {
+		invoker := mock.NewMockInvoker(ctrl)
+		invokers = append(invokers, invoker)
+		invoker.EXPECT().IsAvailable().Return(true).AnyTimes() // Mock IsAvailable to return true
+		invoker.EXPECT().Invoke(gomock.Any(), gomock.Any()).Return(mockResult).AnyTimes()
+	}
+
+	// Create 1 invoker that returns a failed result
+	{
+		invoker := mock.NewMockInvoker(ctrl)
+		invokers = append(invokers, invoker)
+		invoker.EXPECT().IsAvailable().Return(false).AnyTimes() // Mock IsAvailable to return true
+		invoker.EXPECT().Invoke(gomock.Any(), gomock.Any()).Return(mockFailedResult).AnyTimes()
+	}
+
+	// Create 10 more invokers that return successful results
+	for i := 0; i < 10; i++ {
+		invoker := mock.NewMockInvoker(ctrl)
+		invokers = append(invokers, invoker)
+		invoker.EXPECT().IsAvailable().Return(true).AnyTimes() // Mock IsAvailable to return true
+		invoker.EXPECT().Invoke(gomock.Any(), gomock.Any()).Return(mockResult).AnyTimes()
+	}
+
+	// Register the clusterInvoker
+	clusterInvoker := registerFailover(invokers...)
+
+	// Perform the invocation
+	result := clusterInvoker.Invoke(context.Background(), &invocation.RPCInvocation{})
+
+	// Assert that the result is the same as the failed result
+	assert.Equal(t, nil, result.Error())
+}
+
+func TestBroadcastInvokeFailed(t *testing.T) {
+	ctrl := gomock.NewController(t)
+	defer ctrl.Finish()
+
+	invokers := make([]*mock.MockInvoker, 0)
+
+	mockFailedResult := &result.RPCResult{Err: errors.New("just failed")}
+
+	// Create 1 invoker that returns a failed result
+	{
+		invoker := mock.NewMockInvoker(ctrl)
+		invokers = append(invokers, invoker)
+		invoker.EXPECT().IsAvailable().Return(true).AnyTimes() // Mock IsAvailable to return true
+		invoker.EXPECT().Invoke(gomock.Any(), gomock.Any()).Return(mockFailedResult).AnyTimes()
+	}
+
+	// Register the clusterInvoker
+	clusterInvoker := registerFailover(invokers...)
+
+	// Perform the invocation
+	result := clusterInvoker.Invoke(context.Background(), &invocation.RPCInvocation{})
+
+	// Assert that the result is the same as the failed result
+	assert.Equal(t, mockFailedResult.Err, result.Error())
+}
+
+func TestBroadcastInvokeFailedNoServer(t *testing.T) {
+	ctrl := gomock.NewController(t)
+	defer ctrl.Finish()
+
+	invokers := make([]*mock.MockInvoker, 0)
+
+	mockFailedResult := &result.RPCResult{Err: errors.New("just failed")}
+
+	// Create 1 invoker that returns a failed result
+	{
+		invoker := mock.NewMockInvoker(ctrl)
+		invokers = append(invokers, invoker)
+		invoker.EXPECT().IsAvailable().Return(false).AnyTimes() // Mock IsAvailable to return true
+		invoker.EXPECT().Invoke(gomock.Any(), gomock.Any()).Return(mockFailedResult).AnyTimes()
+	}
+
+	// Register the clusterInvoker
+	clusterInvoker := registerFailover(invokers...)
+
+	// Perform the invocation
+	result := clusterInvoker.Invoke(context.Background(), &invocation.RPCInvocation{})
+
+	// Assert that the result is the same as the failed result
+	assert.Equal(t, "Failed to invoke the method  of the service com.ikurento.user.UserProvider .No provider is available because can't connect server.", result.Error().Error())
+}