@@ -27,16 +27,17 @@ func (c VshardMode) String() string {
27
27
return string (c )
28
28
}
29
29
30
- type StorageCallAssertError struct {
30
+ type storageCallAssertError struct {
31
31
Code int `msgpack:"code"`
32
32
BaseType string `msgpack:"base_type"`
33
33
Type string `msgpack:"type"`
34
34
Message string `msgpack:"message"`
35
35
Trace interface {} `msgpack:"trace"`
36
36
}
37
37
38
- func (s StorageCallAssertError ) Error () string {
39
- return fmt .Sprintf ("vshard.storage.call assert error code: %d, type:%s, message: %s" , s .Code , s .Type , s .Message )
38
+ func (s storageCallAssertError ) Error () string {
39
+ type alias storageCallAssertError
40
+ return fmt .Sprintf ("%+v" , alias (s ))
40
41
}
41
42
42
43
type StorageCallVShardError struct {
@@ -51,7 +52,11 @@ type StorageCallVShardError struct {
51
52
}
52
53
53
54
func (s StorageCallVShardError ) Error () string {
54
- return fmt .Sprintf ("vshard.storage.call bucket error bucket_id: %d, reason: %s, name: %s" , s .BucketID , s .Reason , s .Name )
55
+ // Just print struct as is, use hack with alias type to avoid recursion:
56
+ // %v attempts to call Error() method for s, which is recursion.
57
+ // This alias doesn't have method Error().
58
+ type alias StorageCallVShardError
59
+ return fmt .Sprintf ("%+v" , alias (s ))
55
60
}
56
61
57
62
type StorageResultTypedFunc = func (result interface {}) error
@@ -76,6 +81,8 @@ func (r *Router) RouterCallImpl(ctx context.Context,
76
81
fnc string ,
77
82
args interface {}) (interface {}, StorageResultTypedFunc , error ) {
78
83
84
+ const vshardStorageClientCall = "vshard.storage.call"
85
+
79
86
if bucketID < 1 || r .cfg .TotalBucketCount < bucketID {
80
87
return nil , nil , fmt .Errorf ("bucket id is out of range: %d (total %d)" , bucketID , r .cfg .TotalBucketCount )
81
88
}
@@ -87,7 +94,7 @@ func (r *Router) RouterCallImpl(ctx context.Context,
87
94
timeout := opts .Timeout
88
95
timeStart := time .Now ()
89
96
90
- req := tarantool .NewCallRequest ("vshard.storage.call" )
97
+ req := tarantool .NewCallRequest (vshardStorageClientCall )
91
98
req = req .Context (ctx )
92
99
req = req .Args ([]interface {}{
93
100
bucketID ,
@@ -97,6 +104,7 @@ func (r *Router) RouterCallImpl(ctx context.Context,
97
104
})
98
105
99
106
var err error
107
+ var vshardError StorageCallVShardError
100
108
101
109
for {
102
110
if since := time .Since (timeStart ); since > timeout {
@@ -114,10 +122,9 @@ func (r *Router) RouterCallImpl(ctx context.Context,
114
122
115
123
rs , err = r .BucketResolve (ctx , bucketID )
116
124
if err != nil {
117
- r .log ().Debugf (ctx , "cant resolve bucket %d with error: %s" , bucketID , err .Error ())
118
-
119
125
r .metrics ().RetryOnCall ("bucket_resolve_error" )
120
- continue
126
+
127
+ return nil , nil , fmt .Errorf ("cant resolve bucket %d: %w" , bucketID , err )
121
128
}
122
129
123
130
r .log ().Infof (ctx , "try call %s on replicaset %s for bucket %d" , fnc , rs .info .Name , bucketID )
@@ -127,97 +134,73 @@ func (r *Router) RouterCallImpl(ctx context.Context,
127
134
var respData []interface {}
128
135
respData , err = future .Get ()
129
136
if err != nil {
130
- r .log ().Errorf (ctx , "got future error: %s" , err )
131
137
r .metrics ().RetryOnCall ("future_get_error" )
132
138
133
- continue
139
+ return nil , nil , fmt . Errorf ( "got error on future.Get(): %w" , err )
134
140
}
135
141
136
- r .log ().Debugf (ctx , "got call result response data %s " , respData )
142
+ r .log ().Debugf (ctx , "got call result response data %v " , respData )
137
143
138
- if len (respData ) < 1 {
144
+ if len (respData ) == 0 {
139
145
// vshard.storage.call(func) returns up to two values:
140
- // - true/false
146
+ // - true/false/nil
141
147
// - func result, omitted if func does not return anything
142
- err = fmt .Errorf ("invalid length of response data: must be >= 1, current: %d" , len (respData ))
143
-
144
- r .log ().Errorf (ctx , "%s" , err .Error ())
145
-
146
148
r .metrics ().RetryOnCall ("resp_data_error" )
147
- continue
149
+
150
+ return nil , nil , fmt .Errorf ("protocol violation %s: got empty response" , vshardStorageClientCall )
148
151
}
149
152
150
153
if respData [0 ] == nil {
151
- vshardErr := & StorageCallVShardError {}
152
-
153
- if len (respData ) < 2 {
154
- err = fmt .Errorf ("unexpected response length when respData[0] == nil: %d" , len (respData ))
155
- } else {
156
- err = mapstructure .Decode (respData [1 ], vshardErr )
154
+ if len (respData ) != 2 {
155
+ return nil , nil , fmt .Errorf ("protocol violation %s: length is %d when respData[0] is nil" , vshardStorageClientCall , len (respData ))
157
156
}
158
157
158
+ err = mapstructure .Decode (respData [1 ], & vshardError )
159
159
if err != nil {
160
+ // Something unexpected happened: we couldn't decode respData[1] as a vshardError,
161
+ // so return reason why and respData[1], that is supposed to be a vshardError.
160
162
r .metrics ().RetryOnCall ("internal_error" )
161
163
162
- err = fmt .Errorf ("cant decode vhsard err by trarantool with err: %s; continue try" , err )
163
-
164
- r .log ().Errorf (ctx , "%s" , err .Error ())
165
- continue
164
+ return nil , nil , fmt .Errorf ("cant decode vhsard err by trarantool with err: %v (%v)" , err , respData [1 ])
166
165
}
167
166
168
- err = vshardErr
169
-
170
- r .log ().Errorf (ctx , "got vshard storage call error: %s" , err )
171
-
172
- if vshardErr .Name == "WRONG_BUCKET" ||
173
- vshardErr .Name == "BUCKET_IS_LOCKED" ||
174
- vshardErr .Name == "TRANSFER_IS_IN_PROGRESS" {
167
+ switch vshardError .Name {
168
+ case "WRONG_BUCKET" , "BUCKET_IS_LOCKED" , "TRANSFER_IS_IN_PROGRESS" :
175
169
r .BucketReset (bucketID )
176
170
r .metrics ().RetryOnCall ("bucket_migrate" )
177
171
178
- r .log ().Debugf (ctx , "retrying cause bucket in migrate state (%s)" , vshardErr . Name )
172
+ r .log ().Debugf (ctx , "retrying %s cause bucket in migrate state (%s)" , fnc , vshardError . Error () )
179
173
174
+ // this vshardError will be returned to a caller in case of timeout
175
+ err = & vshardError
180
176
continue
181
177
}
182
178
183
- continue
179
+ return nil , nil , & vshardError
184
180
}
185
181
186
- isVShardRespOk := false
182
+ var isVShardRespOk bool
187
183
err = future .GetTyped (& []interface {}{& isVShardRespOk })
188
184
if err != nil {
189
- r .log ().Debugf (ctx , "cant get typed with err: %s" , err )
190
-
191
- continue
185
+ return nil , nil , fmt .Errorf ("protocol violation %s: can't decode respData[0] as boolean: %v" , vshardStorageClientCall , err )
192
186
}
193
187
194
- if ! isVShardRespOk { // error
195
- errorResp := & StorageCallAssertError {}
196
-
197
- // Since we got respData[0] == false, it means that assert has happened
188
+ if ! isVShardRespOk {
189
+ // Since we got respData[0] == false, it means that an error has happened
198
190
// while executing user-defined function on vshard storage.
199
191
// In this case, vshard storage must return a pair: false, error.
200
- if len (respData ) < 2 {
201
- err = fmt .Errorf ("protocol violation: unexpected response length when respData[0] == false: %d" , len (respData ))
202
- } else {
203
- err = future .GetTyped (& []interface {}{& isVShardRespOk , errorResp })
192
+ if len (respData ) != 2 {
193
+ return nil , nil , fmt .Errorf ("protocol violation %s: response length is %d when respData[0] is false" , vshardStorageClientCall , len (respData ))
204
194
}
205
195
196
+ var assertError storageCallAssertError
197
+ err = mapstructure .Decode (respData [1 ], & assertError )
206
198
if err != nil {
207
- // Either protocol has been violated or decoding has failed.
208
- err = fmt .Errorf ("cant get typed vshard err with err: %s" , err )
209
- } else {
210
- // StorageCallAssertError successfully has been decoded.
211
- err = errorResp
199
+ // We could not decode respData[1] as assertError, so return respData[1] as is, add info why we could not decode.
200
+ return nil , nil , fmt .Errorf ("%s: %s failed %v (decoding to assertError failed %v)" , vshardStorageClientCall , fnc , respData [1 ], err )
212
201
}
213
202
214
- if errorResp .Type == "ClientError" || errorResp .Type == "LuajitError" {
215
- return nil , nil , errorResp
216
- }
217
-
218
- r .log ().Debugf (ctx , "retry cause vhsard response not ok: %s" , errorResp )
219
-
220
- continue
203
+ return nil , nil , fmt .Errorf ("%s: %s failed: %+v" , vshardStorageClientCall , fnc , assertError )
221
204
}
222
205
223
206
r .metrics ().RequestDuration (time .Since (timeStart ), true , false )
@@ -363,14 +346,14 @@ func (r *Router) RouterMapCallRWImpl(
363
346
return nil , fmt .Errorf ("protocol violation: invalid respData length when respData[0] == nil, must be = 2, current: %d" , len (respData ))
364
347
}
365
348
366
- assertError := & StorageCallAssertError {}
367
- err = mapstructure .Decode (respData [1 ], assertError )
349
+ var assertError storageCallAssertError
350
+ err = mapstructure .Decode (respData [1 ], & assertError )
368
351
if err != nil {
369
- // TODO: not only StorageCallAssertError is possible here?
352
+ // We could not decode respData[1] as assertError, so return respData[1] as is, add info why we could not decode.
370
353
return nil , fmt .Errorf ("storage_map failed on %v: %+v (decoding to assertError failed %v)" , rsFuture .uuid , respData [1 ], err )
371
354
}
372
355
373
- return nil , fmt .Errorf ("storage_map failed on %v: %w " , rsFuture .uuid , assertError )
356
+ return nil , fmt .Errorf ("storage_map failed on %v: %+v " , rsFuture .uuid , assertError )
374
357
}
375
358
376
359
var isVShardRespOk bool
0 commit comments