@@ -10,7 +10,9 @@ import (
10
10
"sync"
11
11
"time"
12
12
13
+ azlog "github.com/Azure/azure-sdk-for-go/sdk/internal/log"
13
14
"github.com/Azure/azure-sdk-for-go/sdk/internal/uuid"
15
+ "github.com/Azure/azure-sdk-for-go/sdk/messaging/azservicebus/internal/amqpwrap"
14
16
"github.com/Azure/go-amqp"
15
17
)
16
18
@@ -24,10 +26,9 @@ const (
24
26
type (
25
27
// rpcLink is the bidirectional communication structure used for CBS negotiation
26
28
rpcLink struct {
27
- session * amqp.Session
28
-
29
- receiver amqpReceiver // *amqp.Receiver
30
- sender amqpSender // *amqp.Sender
29
+ session amqpwrap.AMQPSession
30
+ receiver amqpwrap.AMQPReceiverCloser // *amqp.Receiver
31
+ sender amqpwrap.AMQPSenderCloser // *amqp.Sender
31
32
32
33
clientAddress string
33
34
sessionID * string
@@ -38,9 +39,10 @@ type (
38
39
responseMap map [string ]chan rpcResponse
39
40
broadcastErr error // the error that caused the responseMap to be nil'd
40
41
42
+ logEvent azlog.Event
43
+
41
44
// for unit tests
42
- uuidNewV4 func () (uuid.UUID , error )
43
- messageAccept func (ctx context.Context , message * amqp.Message ) error
45
+ uuidNewV4 func () (uuid.UUID , error )
44
46
}
45
47
46
48
// RPCResponse is the simplified response structure from an RPC like call
@@ -57,17 +59,6 @@ type (
57
59
message * amqp.Message
58
60
err error
59
61
}
60
-
61
- // Actually: *amqp.Receiver
62
- amqpReceiver interface {
63
- Receive (ctx context.Context ) (* amqp.Message , error )
64
- Close (ctx context.Context ) error
65
- }
66
-
67
- amqpSender interface {
68
- Send (ctx context.Context , msg * amqp.Message ) error
69
- Close (ctx context.Context ) error
70
- }
71
62
)
72
63
73
64
type rpcError struct {
@@ -84,8 +75,9 @@ func (e rpcError) RPCCode() int {
84
75
}
85
76
86
77
type RPCLinkArgs struct {
87
- Client * amqp.Client
88
- Address string
78
+ Client amqpwrap.AMQPClient
79
+ Address string
80
+ LogEvent azlog.Event
89
81
}
90
82
91
83
// NewRPCLink will build a new request response link
@@ -110,6 +102,7 @@ func NewRPCLink(args RPCLinkArgs) (*rpcLink, error) {
110
102
uuidNewV4 : uuid .New ,
111
103
responseMap : map [string ]chan rpcResponse {},
112
104
startResponseRouterOnce : & sync.Once {},
105
+ logEvent : args .LogEvent ,
113
106
}
114
107
115
108
sender , err := session .NewSender (
@@ -147,45 +140,57 @@ func NewRPCLink(args RPCLinkArgs) (*rpcLink, error) {
147
140
148
141
link .sender = sender
149
142
link .receiver = receiver
150
- link .messageAccept = receiver .AcceptMessage
151
143
152
144
return link , nil
153
145
}
154
146
147
+ const responseRouterShutdownMessage = "Response router has shut down"
148
+
155
149
// startResponseRouter is responsible for taking any messages received on the 'response'
156
150
// link and forwarding it to the proper channel. The channel is being select'd by the
157
151
// original `RPC` call.
158
152
func (l * rpcLink ) startResponseRouter () {
153
+ defer azlog .Writef (l .logEvent , responseRouterShutdownMessage )
154
+
159
155
for {
160
156
res , err := l .receiver .Receive (context .Background ())
161
157
162
- // You'll see this when the link is shutting down (either
163
- // service-initiated via 'detach' or a user-initiated shutdown)
164
- if isClosedError (err ) {
165
- l .broadcastError (err )
166
- break
158
+ if err != nil {
159
+ // if the link or connection has a malfunction that would require it to restart then
160
+ // we need to bail out, broadcasting to all affected callers/consumers.
161
+ if GetRecoveryKind (err ) != RecoveryKindNone {
162
+ azlog .Writef (l .logEvent , "Error in RPCLink, stopping response router: %s" , err .Error ())
163
+ l .broadcastError (err )
164
+ break
165
+ }
166
+
167
+ azlog .Writef (l .logEvent , "Non-fatal error in RPCLink, starting to receive again: %s" , err .Error ())
168
+ continue
167
169
}
168
170
169
171
// I don't believe this should happen. The JS version of this same code
170
172
// ignores errors as well since responses should always be correlated
171
173
// to actual send requests. So this is just here for completeness.
172
174
if res == nil {
175
+ azlog .Writef (l .logEvent , "RPCLink received no error, but also got no response" )
173
176
continue
174
177
}
175
178
176
179
autogenMessageId , ok := res .Properties .CorrelationID .(string )
177
180
178
181
if ! ok {
179
- // TODO: it'd be good to track these in some way. We don't have a good way to
180
- // forward this on at this point.
182
+ azlog .Writef (l .logEvent , "RPCLink message received without a CorrelationID %v" , res )
181
183
continue
182
184
}
183
185
184
186
ch := l .deleteChannelFromMap (autogenMessageId )
185
187
186
- if ch != nil {
187
- ch <- rpcResponse {message : res , err : err }
188
+ if ch == nil {
189
+ azlog .Writef (l .logEvent , "RPCLink had no response channel for correlation ID %v" , autogenMessageId )
190
+ continue
188
191
}
192
+
193
+ ch <- rpcResponse {message : res , err : err }
189
194
}
190
195
}
191
196
@@ -228,7 +233,7 @@ func (l *rpcLink) RPC(ctx context.Context, msg *amqp.Message) (*RPCResponse, err
228
233
229
234
if err != nil {
230
235
l .deleteChannelFromMap (messageID )
231
- return nil , fmt .Errorf ("Failed to send message with ID %s: %w" , messageID , err )
236
+ return nil , fmt .Errorf ("failed to send message with ID %s: %w" , messageID , err )
232
237
}
233
238
234
239
var res * amqp.Message
@@ -281,7 +286,7 @@ func (l *rpcLink) RPC(ctx context.Context, msg *amqp.Message) (*RPCResponse, err
281
286
Message : res ,
282
287
}
283
288
284
- if err := l .messageAccept (ctx , res ); err != nil {
289
+ if err := l .receiver . AcceptMessage (ctx , res ); err != nil {
285
290
return response , fmt .Errorf ("failed accepting message on rpc link: %w" , err )
286
291
}
287
292
@@ -415,15 +420,6 @@ func addMessageID(message *amqp.Message, uuidNewV4 func() (uuid.UUID, error)) (*
415
420
return & copiedMessage , autoGenMessageID , nil
416
421
}
417
422
418
- func isClosedError (err error ) bool {
419
- var detachError * amqp.DetachError
420
-
421
- return errors .Is (err , amqp .ErrLinkClosed ) ||
422
- errors .As (err , & detachError ) ||
423
- errors .Is (err , amqp .ErrConnClosed ) ||
424
- errors .Is (err , amqp .ErrSessionClosed )
425
- }
426
-
427
423
// asRPCError checks to see if the res is actually a failed request
428
424
// (where failed means the status code was non-2xx). If so,
429
425
// it returns true and updates the struct pointed to by err.
0 commit comments