Skip to content

Commit 1d84315

Browse files
authored
Merge pull request dapr#8624 from JoshVanL/actors-500-remote-no-retry
Actors: Don't retry remote calls returning 500
2 parents dfd70d3 + 6436e2c commit 1d84315

File tree

2 files changed

+110
-1
lines changed

2 files changed

+110
-1
lines changed

pkg/actors/engine/engine.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ func (e *engine) callActor(ctx context.Context, req *internalv1pb.InternalInvoke
254254

255255
attempt := resiliency.GetAttempt(ctx)
256256
code := status.Code(err)
257-
if code == codes.Unavailable || code == codes.Internal {
257+
if code == codes.Unavailable {
258258
// Destroy the connection and force a re-connection on the next attempt
259259
return res, fmt.Errorf("failed to invoke target %s after %d retries. Error: %w", lar.Address, attempt-1, err)
260260
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
/*
2+
Copyright 2025 The Dapr Authors
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
http://www.apache.org/licenses/LICENSE-2.0
7+
Unless required by applicable law or agreed to in writing, software
8+
distributed under the License is distributed on an "AS IS" BASIS,
9+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implieh.
10+
See the License for the specific language governing permissions and
11+
limitations under the License.
12+
*/
13+
14+
package call
15+
16+
import (
17+
"context"
18+
"fmt"
19+
"io"
20+
nethttp "net/http"
21+
"sync/atomic"
22+
"testing"
23+
"time"
24+
25+
"github.com/stretchr/testify/assert"
26+
"github.com/stretchr/testify/require"
27+
"google.golang.org/grpc/codes"
28+
"google.golang.org/grpc/status"
29+
30+
rtv1 "github.com/dapr/dapr/pkg/proto/runtime/v1"
31+
"github.com/dapr/dapr/tests/integration/framework"
32+
"github.com/dapr/dapr/tests/integration/framework/client"
33+
"github.com/dapr/dapr/tests/integration/framework/process/daprd/actors"
34+
"github.com/dapr/dapr/tests/integration/suite"
35+
)
36+
37+
func init() {
38+
suite.Register(new(non200))
39+
}
40+
41+
type non200 struct {
42+
actors1 *actors.Actors
43+
actors2 *actors.Actors
44+
called atomic.Int64
45+
}
46+
47+
func (n *non200) Setup(t *testing.T) []framework.Option {
48+
n.actors1 = actors.New(t,
49+
actors.WithActorTypes("abc"),
50+
actors.WithActorTypeHandler("abc", func(w nethttp.ResponseWriter, _ *nethttp.Request) {
51+
n.called.Add(1)
52+
w.WriteHeader(nethttp.StatusInternalServerError)
53+
w.Write([]byte("custom error"))
54+
}),
55+
)
56+
n.actors2 = actors.New(t,
57+
actors.WithPeerActor(n.actors1),
58+
)
59+
60+
return []framework.Option{
61+
framework.WithProcesses(n.actors1, n.actors2),
62+
}
63+
}
64+
65+
func (n *non200) Run(t *testing.T, ctx context.Context) {
66+
n.actors1.WaitUntilRunning(t, ctx)
67+
n.actors2.WaitUntilRunning(t, ctx)
68+
69+
for i, actor := range []*actors.Actors{n.actors1, n.actors2} {
70+
_, err := actor.GRPCClient(t, ctx).InvokeActor(ctx, &rtv1.InvokeActorRequest{
71+
ActorType: "abc",
72+
ActorId: "xyz",
73+
Method: "foo",
74+
})
75+
require.Error(t, err)
76+
status, ok := status.FromError(err)
77+
require.True(t, ok)
78+
assert.Equal(t, "error invoke actor method: error from actor service: (500) custom error", status.Message())
79+
assert.Equal(t, codes.Internal, status.Code())
80+
assert.Equal(t, int64(i+1), n.called.Load())
81+
}
82+
83+
time.Sleep(time.Second * 3)
84+
assert.Equal(t, int64(2), n.called.Load())
85+
86+
for i, url := range []string{
87+
fmt.Sprintf("http://%s/v1.0/actors/abc/xyz/method/foo", n.actors1.Daprd().HTTPAddress()),
88+
fmt.Sprintf("http://%s/v1.0/actors/abc/xyz/method/foo", n.actors2.Daprd().HTTPAddress()),
89+
} {
90+
req, err := nethttp.NewRequest(nethttp.MethodGet, url, nil)
91+
require.NoError(t, err)
92+
resp, err := client.HTTP(t).Do(req)
93+
require.NoError(t, err)
94+
assert.Equal(t, 500, resp.StatusCode)
95+
96+
body, err := io.ReadAll(resp.Body)
97+
require.NoError(t, err)
98+
if i == 0 {
99+
assert.JSONEq(t, `{"errorCode":"ERR_ACTOR_INVOKE_METHOD","message":"error invoke actor method: error from actor service: (500) custom error"}`, string(body))
100+
} else {
101+
assert.JSONEq(t, `{"errorCode":"ERR_ACTOR_INVOKE_METHOD","message":"error invoke actor method: rpc error: code = Internal desc = error invoke actor method: error from actor service: (500) custom error"}`, string(body))
102+
}
103+
require.NoError(t, resp.Body.Close())
104+
assert.Equal(t, int64(i+3), n.called.Load())
105+
}
106+
107+
time.Sleep(time.Second * 3)
108+
assert.Equal(t, int64(4), n.called.Load())
109+
}

0 commit comments

Comments
 (0)