| 230 | } |
| 231 | |
| 232 | func TestHedge(t *testing.T) { |
| 233 | tests := []struct { |
| 234 | name string |
| 235 | returnIn time.Duration |
| 236 | hedgeAt time.Duration |
| 237 | expectedHedgedRequests int32 |
| 238 | }{ |
| 239 | { |
| 240 | name: "hedge disabled", |
| 241 | expectedHedgedRequests: 1, |
| 242 | }, |
| 243 | { |
| 244 | name: "hedge enabled doesn't hit", |
| 245 | hedgeAt: time.Hour, |
| 246 | expectedHedgedRequests: 1, |
| 247 | }, |
| 248 | { |
| 249 | name: "hedge enabled and hits", |
| 250 | hedgeAt: time.Millisecond, |
| 251 | returnIn: 100 * time.Millisecond, |
| 252 | expectedHedgedRequests: 2, |
| 253 | }, |
| 254 | } |
| 255 | |
| 256 | for _, tc := range tests { |
| 257 | t.Run(tc.name, func(t *testing.T) { |
| 258 | count := int32(0) |
| 259 | server := fakeServer(t, tc.returnIn, &count) |
| 260 | |
| 261 | r, w, _, err := New(&Config{ |
| 262 | Region: "blerg", |
| 263 | AccessKey: "test", |
| 264 | SecretKey: flagext.SecretWithValue("test"), |
| 265 | Bucket: "blerg", |
| 266 | Insecure: true, |
| 267 | Endpoint: server.URL[7:], // [7:] -> strip http:// |
| 268 | HedgeRequestsAt: tc.hedgeAt, |
| 269 | HedgeRequestsUpTo: 2, |
| 270 | }) |
| 271 | require.NoError(t, err) |
| 272 | |
| 273 | ctx := context.Background() |
| 274 | |
| 275 | // the first call on each client initiates an extra http request |
| 276 | // clearing that here |
| 277 | _, _, _ = r.Read(ctx, "object", backend.KeyPath{"test"}, nil) |
| 278 | time.Sleep(tc.returnIn) |
| 279 | atomic.StoreInt32(&count, 0) |
| 280 | |
| 281 | // calls that should hedge |
| 282 | _, _, _ = r.Read(ctx, "object", backend.KeyPath{"test"}, nil) |
| 283 | time.Sleep(tc.returnIn) |
| 284 | assert.Equal(t, tc.expectedHedgedRequests, atomic.LoadInt32(&count)) |
| 285 | atomic.StoreInt32(&count, 0) |
| 286 | |
| 287 | _ = r.ReadRange(ctx, "object", backend.KeyPath{"test"}, 10, []byte{}, nil) |
| 288 | time.Sleep(tc.returnIn) |
| 289 | assert.Equal(t, tc.expectedHedgedRequests, atomic.LoadInt32(&count)) |