vitess.io/vitess@v0.16.2/go/pools/resource_pool_test.go (about) 1 /* 2 Copyright 2019 The Vitess Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package pools 18 19 import ( 20 "context" 21 "errors" 22 "fmt" 23 "testing" 24 "time" 25 26 "github.com/stretchr/testify/assert" 27 "github.com/stretchr/testify/require" 28 29 "vitess.io/vitess/go/sync2" 30 ) 31 32 var ( 33 lastID, count, closeCount, resetCount sync2.AtomicInt64 34 35 waitStarts []time.Time 36 37 sFoo = &Setting{query: "set foo=1"} 38 sBar = &Setting{query: "set bar=1"} 39 sFooBar = &Setting{query: "set foo=1, bar=2"} 40 ) 41 42 type TestResource struct { 43 num int64 44 timeCreated time.Time 45 closed bool 46 setting string 47 failApply bool 48 } 49 50 func (tr *TestResource) ResetSetting(ctx context.Context) error { 51 resetCount.Add(1) 52 tr.setting = "" 53 return nil 54 } 55 56 func (tr *TestResource) ApplySetting(ctx context.Context, setting *Setting) error { 57 if tr.failApply { 58 return fmt.Errorf("ApplySetting failed") 59 } 60 tr.setting = setting.query 61 return nil 62 } 63 64 func (tr *TestResource) IsSettingApplied() bool { 65 return len(tr.setting) > 0 66 } 67 68 func (tr *TestResource) IsSameSetting(setting string) bool { 69 return tr.setting == setting 70 } 71 72 func (tr *TestResource) Close() { 73 if !tr.closed { 74 count.Add(-1) 75 closeCount.Add(1) 76 tr.closed = true 77 } 78 } 79 80 var _ Resource = (*TestResource)(nil) 81 82 func (tr *TestResource) Expired(lifetimeTimeout time.Duration) bool { 83 return lifetimeTimeout > 0 && time.Until(tr.timeCreated.Add(lifetimeTimeout)) < 0 84 } 85 86 func logWait(start time.Time) { 87 waitStarts = append(waitStarts, start) 88 } 89 90 func PoolFactory(context.Context) (Resource, error) { 91 count.Add(1) 92 return &TestResource{num: lastID.Add(1), timeCreated: time.Now()}, nil 93 } 94 95 func FailFactory(context.Context) (Resource, error) { 96 return nil, errors.New("Failed") 97 } 98 99 func SlowFailFactory(context.Context) (Resource, error) { 100 time.Sleep(10 * time.Millisecond) 101 return nil, errors.New("Failed") 102 } 103 104 func DisallowSettingsFactory(context.Context) (Resource, error) { 105 count.Add(1) 106 return &TestResource{num: lastID.Add(1), failApply: true}, nil 107 } 108 109 func TestOpen(t *testing.T) { 110 ctx := context.Background() 111 lastID.Set(0) 112 count.Set(0) 113 waitStarts = waitStarts[:0] 114 115 p := NewResourcePool(PoolFactory, 6, 6, time.Second, 0, logWait, nil, 0) 116 p.SetCapacity(5) 117 var resources [10]Resource 118 var r Resource 119 var err error 120 121 // Test Get 122 for i := 0; i < 5; i++ { 123 if i%2 == 0 { 124 r, err = p.Get(ctx, nil) 125 } else { 126 r, err = p.Get(ctx, sFoo) 127 } 128 require.NoError(t, err) 129 resources[i] = r 130 assert.EqualValues(t, 5-i-1, p.Available()) 131 assert.Zero(t, p.WaitCount()) 132 assert.Zero(t, len(waitStarts)) 133 assert.Zero(t, p.WaitTime()) 134 assert.EqualValues(t, i+1, lastID.Get()) 135 assert.EqualValues(t, i+1, count.Get()) 136 } 137 138 // Test that Get waits 139 ch := make(chan bool) 140 go func() { 141 for i := 0; i < 5; i++ { 142 if i%2 == 0 { 143 r, err = p.Get(ctx, nil) 144 } else { 145 r, err = p.Get(ctx, sFoo) 146 } 147 require.NoError(t, err) 148 resources[i] = r 149 } 150 for i := 0; i < 5; i++ { 151 p.Put(resources[i]) 152 } 153 ch <- true 154 }() 155 for i := 0; i < 5; i++ { 156 // Sleep to ensure the goroutine waits 157 time.Sleep(10 * time.Millisecond) 158 p.Put(resources[i]) 159 } 160 <-ch 161 assert.EqualValues(t, 5, p.WaitCount()) 162 assert.Equal(t, 5, len(waitStarts)) 163 // verify start times are monotonic increasing 164 for i := 1; i < len(waitStarts); i++ { 165 if waitStarts[i].Before(waitStarts[i-1]) { 166 t.Errorf("Expecting monotonic increasing start times") 167 } 168 } 169 assert.NotZero(t, p.WaitTime()) 170 assert.EqualValues(t, 5, lastID.Get()) 171 // Test Close resource 172 r, err = p.Get(ctx, nil) 173 require.NoError(t, err) 174 r.Close() 175 // A nil Put should cause the resource to be reopened. 176 p.Put(nil) 177 assert.EqualValues(t, 5, count.Get()) 178 assert.EqualValues(t, 6, lastID.Get()) 179 180 for i := 0; i < 5; i++ { 181 if i%2 == 0 { 182 r, err = p.Get(ctx, nil) 183 } else { 184 r, err = p.Get(ctx, sFoo) 185 } 186 require.NoError(t, err) 187 resources[i] = r 188 } 189 for i := 0; i < 5; i++ { 190 p.Put(resources[i]) 191 } 192 assert.EqualValues(t, 5, count.Get()) 193 assert.EqualValues(t, 6, lastID.Get()) 194 195 // SetCapacity 196 p.SetCapacity(3) 197 assert.EqualValues(t, 3, count.Get()) 198 assert.EqualValues(t, 6, lastID.Get()) 199 assert.EqualValues(t, 3, p.Capacity()) 200 assert.EqualValues(t, 3, p.Available()) 201 202 p.SetCapacity(6) 203 assert.EqualValues(t, 6, p.Capacity()) 204 assert.EqualValues(t, 6, p.Available()) 205 206 for i := 0; i < 6; i++ { 207 if i%2 == 0 { 208 r, err = p.Get(ctx, nil) 209 } else { 210 r, err = p.Get(ctx, sFoo) 211 } 212 require.NoError(t, err) 213 resources[i] = r 214 } 215 for i := 0; i < 6; i++ { 216 p.Put(resources[i]) 217 } 218 assert.EqualValues(t, 6, count.Get()) 219 assert.EqualValues(t, 9, lastID.Get()) 220 221 // Close 222 p.Close() 223 assert.EqualValues(t, 0, p.Capacity()) 224 assert.EqualValues(t, 0, p.Available()) 225 assert.EqualValues(t, 0, count.Get()) 226 } 227 228 func TestShrinking(t *testing.T) { 229 ctx := context.Background() 230 lastID.Set(0) 231 count.Set(0) 232 waitStarts = waitStarts[:0] 233 234 p := NewResourcePool(PoolFactory, 5, 5, time.Second, 0, logWait, nil, 0) 235 var resources [10]Resource 236 // Leave one empty slot in the pool 237 for i := 0; i < 4; i++ { 238 var r Resource 239 var err error 240 if i%2 == 0 { 241 r, err = p.Get(ctx, nil) 242 } else { 243 r, err = p.Get(ctx, sFoo) 244 } 245 require.NoError(t, err) 246 resources[i] = r 247 } 248 done := make(chan bool) 249 go func() { 250 p.SetCapacity(3) 251 done <- true 252 }() 253 expected := `{"Capacity": 3, "Available": 0, "Active": 4, "InUse": 4, "MaxCapacity": 5, "WaitCount": 0, "WaitTime": 0, "IdleTimeout": 1000000000, "IdleClosed": 0, "MaxLifetimeClosed": 0, "Exhausted": 0}` 254 for i := 0; i < 10; i++ { 255 time.Sleep(10 * time.Millisecond) 256 stats := p.StatsJSON() 257 if stats != expected { 258 if i == 9 { 259 t.Errorf(`expecting '%s', received '%s'`, expected, stats) 260 } 261 } 262 } 263 // There are already 2 resources available in the pool. 264 // So, returning one should be enough for SetCapacity to complete. 265 p.Put(resources[3]) 266 <-done 267 // Return the rest of the resources 268 for i := 0; i < 3; i++ { 269 p.Put(resources[i]) 270 } 271 stats := p.StatsJSON() 272 expected = `{"Capacity": 3, "Available": 3, "Active": 3, "InUse": 0, "MaxCapacity": 5, "WaitCount": 0, "WaitTime": 0, "IdleTimeout": 1000000000, "IdleClosed": 0, "MaxLifetimeClosed": 0, "Exhausted": 0}` 273 assert.Equal(t, expected, stats) 274 assert.EqualValues(t, 3, count.Get()) 275 276 // Ensure no deadlock if SetCapacity is called after we start 277 // waiting for a resource 278 var err error 279 for i := 0; i < 3; i++ { 280 var r Resource 281 if i%2 == 0 { 282 r, err = p.Get(ctx, nil) 283 } else { 284 r, err = p.Get(ctx, sFoo) 285 } 286 require.NoError(t, err) 287 resources[i] = r 288 } 289 // This will wait because pool is empty 290 go func() { 291 r, err := p.Get(ctx, nil) 292 require.NoError(t, err) 293 p.Put(r) 294 done <- true 295 }() 296 297 // This will also wait 298 go func() { 299 p.SetCapacity(2) 300 done <- true 301 }() 302 time.Sleep(10 * time.Millisecond) 303 304 // This should not hang 305 for i := 0; i < 3; i++ { 306 p.Put(resources[i]) 307 } 308 <-done 309 <-done 310 assert.EqualValues(t, 2, p.Capacity()) 311 assert.EqualValues(t, 2, p.Available()) 312 assert.EqualValues(t, 1, p.WaitCount()) 313 assert.EqualValues(t, p.WaitCount(), len(waitStarts)) 314 assert.EqualValues(t, 2, count.Get()) 315 316 // Test race condition of SetCapacity with itself 317 p.SetCapacity(3) 318 for i := 0; i < 3; i++ { 319 var r Resource 320 var err error 321 if i%2 == 0 { 322 r, err = p.Get(ctx, nil) 323 } else { 324 r, err = p.Get(ctx, sFoo) 325 } 326 require.NoError(t, err) 327 resources[i] = r 328 } 329 // This will wait because pool is empty 330 go func() { 331 r, err := p.Get(ctx, nil) 332 require.NoError(t, err) 333 p.Put(r) 334 done <- true 335 }() 336 time.Sleep(10 * time.Millisecond) 337 338 // This will wait till we Put 339 go p.SetCapacity(2) 340 time.Sleep(10 * time.Millisecond) 341 go p.SetCapacity(4) 342 time.Sleep(10 * time.Millisecond) 343 344 // This should not hang 345 for i := 0; i < 3; i++ { 346 p.Put(resources[i]) 347 } 348 <-done 349 350 err = p.SetCapacity(-1) 351 if err == nil { 352 t.Errorf("Expecting error") 353 } 354 err = p.SetCapacity(255555) 355 if err == nil { 356 t.Errorf("Expecting error") 357 } 358 359 assert.EqualValues(t, 4, p.Capacity()) 360 assert.EqualValues(t, 4, p.Available()) 361 } 362 363 func TestClosing(t *testing.T) { 364 ctx := context.Background() 365 lastID.Set(0) 366 count.Set(0) 367 p := NewResourcePool(PoolFactory, 5, 5, time.Second, 0, logWait, nil, 0) 368 var resources [10]Resource 369 for i := 0; i < 5; i++ { 370 var r Resource 371 var err error 372 if i%2 == 0 { 373 r, err = p.Get(ctx, nil) 374 } else { 375 r, err = p.Get(ctx, sFoo) 376 } 377 require.NoError(t, err) 378 resources[i] = r 379 } 380 ch := make(chan bool) 381 go func() { 382 p.Close() 383 ch <- true 384 }() 385 386 // Wait for goroutine to call Close 387 time.Sleep(10 * time.Millisecond) 388 stats := p.StatsJSON() 389 expected := `{"Capacity": 0, "Available": 0, "Active": 5, "InUse": 5, "MaxCapacity": 5, "WaitCount": 0, "WaitTime": 0, "IdleTimeout": 1000000000, "IdleClosed": 0, "MaxLifetimeClosed": 0, "Exhausted": 1}` 390 assert.Equal(t, expected, stats) 391 392 // Put is allowed when closing 393 for i := 0; i < 5; i++ { 394 p.Put(resources[i]) 395 } 396 397 // Wait for Close to return 398 <-ch 399 400 stats = p.StatsJSON() 401 expected = `{"Capacity": 0, "Available": 0, "Active": 0, "InUse": 0, "MaxCapacity": 5, "WaitCount": 0, "WaitTime": 0, "IdleTimeout": 1000000000, "IdleClosed": 0, "MaxLifetimeClosed": 0, "Exhausted": 1}` 402 assert.Equal(t, expected, stats) 403 assert.EqualValues(t, 5, lastID.Get()) 404 assert.EqualValues(t, 0, count.Get()) 405 } 406 407 func TestReopen(t *testing.T) { 408 ctx := context.Background() 409 lastID.Set(0) 410 count.Set(0) 411 refreshCheck := func() (bool, error) { 412 return true, nil 413 } 414 p := NewResourcePool(PoolFactory, 5, 5, time.Second, 0, logWait, refreshCheck, 500*time.Millisecond) 415 var resources [10]Resource 416 for i := 0; i < 5; i++ { 417 var r Resource 418 var err error 419 if i%2 == 0 { 420 r, err = p.Get(ctx, nil) 421 } else { 422 r, err = p.Get(ctx, sFoo) 423 } 424 require.NoError(t, err) 425 resources[i] = r 426 } 427 428 time.Sleep(10 * time.Millisecond) 429 stats := p.StatsJSON() 430 expected := `{"Capacity": 5, "Available": 0, "Active": 5, "InUse": 5, "MaxCapacity": 5, "WaitCount": 0, "WaitTime": 0, "IdleTimeout": 1000000000, "IdleClosed": 0, "MaxLifetimeClosed": 0, "Exhausted": 1}` 431 assert.Equal(t, expected, stats) 432 433 time.Sleep(650 * time.Millisecond) 434 for i := 0; i < 5; i++ { 435 p.Put(resources[i]) 436 } 437 time.Sleep(50 * time.Millisecond) 438 stats = p.StatsJSON() 439 expected = `{"Capacity": 5, "Available": 5, "Active": 0, "InUse": 0, "MaxCapacity": 5, "WaitCount": 0, "WaitTime": 0, "IdleTimeout": 1000000000, "IdleClosed": 0, "MaxLifetimeClosed": 0, "Exhausted": 1}` 440 assert.Equal(t, expected, stats) 441 assert.EqualValues(t, 5, lastID.Get()) 442 assert.EqualValues(t, 0, count.Get()) 443 } 444 445 func TestIdleTimeout(t *testing.T) { 446 ctx := context.Background() 447 lastID.Set(0) 448 count.Set(0) 449 p := NewResourcePool(PoolFactory, 1, 1, 10*time.Millisecond, 0, logWait, nil, 0) 450 defer p.Close() 451 452 r, err := p.Get(ctx, nil) 453 require.NoError(t, err) 454 assert.EqualValues(t, 1, count.Get()) 455 assert.EqualValues(t, 0, p.IdleClosed()) 456 457 p.Put(r) 458 assert.EqualValues(t, 1, lastID.Get()) 459 assert.EqualValues(t, 1, count.Get()) 460 assert.EqualValues(t, 0, p.IdleClosed()) 461 462 time.Sleep(15 * time.Millisecond) 463 assert.EqualValues(t, 1, count.Get()) 464 assert.EqualValues(t, 1, p.IdleClosed()) 465 466 r, err = p.Get(ctx, nil) 467 require.NoError(t, err) 468 assert.EqualValues(t, 2, lastID.Get()) 469 assert.EqualValues(t, 1, count.Get()) 470 assert.EqualValues(t, 1, p.IdleClosed()) 471 472 // sleep to let the idle closer run while all resources are in use 473 // then make sure things are still as we expect 474 time.Sleep(15 * time.Millisecond) 475 assert.EqualValues(t, 2, lastID.Get()) 476 assert.EqualValues(t, 1, count.Get()) 477 assert.EqualValues(t, 1, p.IdleClosed()) 478 479 p.Put(r) 480 r, err = p.Get(ctx, nil) 481 require.NoError(t, err) 482 assert.EqualValues(t, 2, lastID.Get()) 483 assert.EqualValues(t, 1, count.Get()) 484 assert.EqualValues(t, 1, p.IdleClosed()) 485 486 // the idle close thread wakes up every 1/100 of the idle time, so ensure 487 // the timeout change applies to newly added resources 488 p.SetIdleTimeout(1000 * time.Millisecond) 489 p.Put(r) 490 491 time.Sleep(15 * time.Millisecond) 492 assert.EqualValues(t, 2, lastID.Get()) 493 assert.EqualValues(t, 1, count.Get()) 494 assert.EqualValues(t, 1, p.IdleClosed()) 495 496 // Get and Put to refresh timeUsed 497 r, err = p.Get(ctx, nil) 498 require.NoError(t, err) 499 p.Put(r) 500 p.SetIdleTimeout(10 * time.Millisecond) 501 time.Sleep(15 * time.Millisecond) 502 assert.EqualValues(t, 3, lastID.Get()) 503 assert.EqualValues(t, 1, count.Get()) 504 assert.EqualValues(t, 2, p.IdleClosed()) 505 } 506 507 func TestIdleTimeoutWithSettings(t *testing.T) { 508 ctx := context.Background() 509 lastID.Set(0) 510 count.Set(0) 511 p := NewResourcePool(PoolFactory, 1, 1, 10*time.Millisecond, 0, logWait, nil, 0) 512 defer p.Close() 513 514 r, err := p.Get(ctx, sFooBar) 515 require.NoError(t, err) 516 assert.EqualValues(t, 1, count.Get()) 517 assert.EqualValues(t, 0, p.IdleClosed()) 518 519 p.Put(r) 520 assert.EqualValues(t, 1, lastID.Get()) 521 assert.EqualValues(t, 1, count.Get()) 522 assert.EqualValues(t, 0, p.IdleClosed()) 523 524 time.Sleep(15 * time.Millisecond) 525 assert.EqualValues(t, 1, count.Get()) 526 assert.EqualValues(t, 1, p.IdleClosed()) 527 528 r, err = p.Get(ctx, sFooBar) 529 require.NoError(t, err) 530 assert.EqualValues(t, 2, lastID.Get()) 531 assert.EqualValues(t, 1, count.Get()) 532 assert.EqualValues(t, 1, p.IdleClosed()) 533 534 // sleep to let the idle closer run while all resources are in use 535 // then make sure things are still as we expect 536 time.Sleep(15 * time.Millisecond) 537 assert.EqualValues(t, 2, lastID.Get()) 538 assert.EqualValues(t, 1, count.Get()) 539 assert.EqualValues(t, 1, p.IdleClosed()) 540 541 p.Put(r) 542 r, err = p.Get(ctx, sFooBar) 543 require.NoError(t, err) 544 assert.EqualValues(t, 2, lastID.Get()) 545 assert.EqualValues(t, 1, count.Get()) 546 assert.EqualValues(t, 1, p.IdleClosed()) 547 548 // the idle close thread wakes up every 1/100 of the idle time, so ensure 549 // the timeout change applies to newly added resources 550 p.SetIdleTimeout(1000 * time.Millisecond) 551 p.Put(r) 552 553 time.Sleep(15 * time.Millisecond) 554 assert.EqualValues(t, 2, lastID.Get()) 555 assert.EqualValues(t, 1, count.Get()) 556 assert.EqualValues(t, 1, p.IdleClosed()) 557 558 // Get and Put to refresh timeUsed 559 r, err = p.Get(ctx, sFooBar) 560 require.NoError(t, err) 561 p.Put(r) 562 p.SetIdleTimeout(10 * time.Millisecond) 563 time.Sleep(15 * time.Millisecond) 564 assert.EqualValues(t, 3, lastID.Get()) 565 assert.EqualValues(t, 1, count.Get()) 566 assert.EqualValues(t, 2, p.IdleClosed()) 567 } 568 569 func TestIdleTimeoutCreateFail(t *testing.T) { 570 ctx := context.Background() 571 lastID.Set(0) 572 count.Set(0) 573 p := NewResourcePool(PoolFactory, 1, 1, 10*time.Millisecond, 0, logWait, nil, 0) 574 defer p.Close() 575 for _, setting := range []*Setting{nil, sFoo} { 576 r, err := p.Get(ctx, setting) 577 require.NoError(t, err) 578 // Change the factory before putting back 579 // to prevent race with the idle closer, who will 580 // try to use it. 581 p.factory = FailFactory 582 p.Put(r) 583 timeout := time.After(1 * time.Second) 584 for p.Active() != 0 { 585 select { 586 case <-timeout: 587 t.Errorf("Timed out waiting for resource to be closed by idle timeout") 588 default: 589 } 590 } 591 // reset factory for next run. 592 p.factory = PoolFactory 593 } 594 } 595 596 func TestMaxLifetime(t *testing.T) { 597 // maxLifetime 0 598 ctx := context.Background() 599 lastID.Set(0) 600 count.Set(0) 601 602 p := NewResourcePool(PoolFactory, 1, 1, 10*time.Second, 0, logWait, nil, 0) 603 defer p.Close() 604 605 r, err := p.Get(ctx, nil) 606 require.NoError(t, err) 607 assert.EqualValues(t, 1, count.Get()) 608 assert.EqualValues(t, 0, p.MaxLifetimeClosed()) 609 610 time.Sleep(10 * time.Millisecond) 611 612 p.Put(r) 613 assert.EqualValues(t, 1, lastID.Get()) 614 assert.EqualValues(t, 1, count.Get()) 615 assert.EqualValues(t, 0, p.MaxLifetimeClosed()) 616 617 // maxLifetime > 0 618 ctx = context.Background() 619 lastID.Set(0) 620 count.Set(0) 621 622 p = NewResourcePool(PoolFactory, 1, 1, 10*time.Second, 10*time.Millisecond, logWait, nil, 0) 623 defer p.Close() 624 625 r, err = p.Get(ctx, nil) 626 require.NoError(t, err) 627 assert.EqualValues(t, 1, count.Get()) 628 assert.EqualValues(t, 0, p.MaxLifetimeClosed()) 629 630 time.Sleep(5 * time.Millisecond) 631 632 p.Put(r) 633 assert.EqualValues(t, 1, lastID.Get()) 634 assert.EqualValues(t, 1, count.Get()) 635 assert.EqualValues(t, 0, p.MaxLifetimeClosed()) 636 637 r, err = p.Get(ctx, nil) 638 require.NoError(t, err) 639 assert.EqualValues(t, 1, count.Get()) 640 assert.EqualValues(t, 0, p.MaxLifetimeClosed()) 641 642 time.Sleep(10 * time.Millisecond * 2) 643 644 p.Put(r) 645 assert.EqualValues(t, 2, lastID.Get()) 646 assert.EqualValues(t, 1, count.Get()) 647 assert.EqualValues(t, 1, p.MaxLifetimeClosed()) 648 } 649 650 func TestExtendedLifetimeTimeout(t *testing.T) { 651 // maxLifetime 0 652 p := NewResourcePool(PoolFactory, 5, 5, time.Second, 0, logWait, nil, 0) 653 defer p.Close() 654 assert.Zero(t, p.extendedMaxLifetime()) 655 656 // maxLifetime > 0 657 maxLifetime := 10 * time.Millisecond 658 for i := 0; i < 10; i++ { 659 p = NewResourcePool(PoolFactory, 5, 5, time.Second, maxLifetime, logWait, nil, 0) 660 defer p.Close() 661 assert.LessOrEqual(t, maxLifetime, p.extendedMaxLifetime()) 662 assert.Greater(t, 2*maxLifetime, p.extendedMaxLifetime()) 663 } 664 } 665 666 func TestCreateFail(t *testing.T) { 667 ctx := context.Background() 668 lastID.Set(0) 669 count.Set(0) 670 p := NewResourcePool(FailFactory, 5, 5, time.Second, 0, logWait, nil, 0) 671 defer p.Close() 672 673 for _, setting := range []*Setting{nil, sFoo} { 674 if _, err := p.Get(ctx, setting); err.Error() != "Failed" { 675 t.Errorf("Expecting Failed, received %v", err) 676 } 677 stats := p.StatsJSON() 678 expected := `{"Capacity": 5, "Available": 5, "Active": 0, "InUse": 0, "MaxCapacity": 5, "WaitCount": 0, "WaitTime": 0, "IdleTimeout": 1000000000, "IdleClosed": 0, "MaxLifetimeClosed": 0, "Exhausted": 0}` 679 assert.Equal(t, expected, stats) 680 } 681 } 682 683 func TestCreateFailOnPut(t *testing.T) { 684 ctx := context.Background() 685 lastID.Set(0) 686 count.Set(0) 687 p := NewResourcePool(PoolFactory, 5, 5, time.Second, 0, logWait, nil, 0) 688 defer p.Close() 689 690 for _, setting := range []*Setting{nil, sFoo} { 691 _, err := p.Get(ctx, setting) 692 require.NoError(t, err) 693 694 // change factory to fail the put. 695 p.factory = FailFactory 696 p.Put(nil) 697 assert.Zero(t, p.Active()) 698 699 // change back for next iteration. 700 p.factory = PoolFactory 701 } 702 } 703 704 func TestSlowCreateFail(t *testing.T) { 705 ctx := context.Background() 706 lastID.Set(0) 707 count.Set(0) 708 p := NewResourcePool(SlowFailFactory, 2, 2, time.Second, 0, logWait, nil, 0) 709 defer p.Close() 710 ch := make(chan bool) 711 for _, setting := range []*Setting{nil, sFoo} { 712 // The third Get should not wait indefinitely 713 for i := 0; i < 3; i++ { 714 go func() { 715 p.Get(ctx, setting) 716 ch <- true 717 }() 718 } 719 for i := 0; i < 3; i++ { 720 <-ch 721 } 722 assert.EqualValues(t, 2, p.Available()) 723 } 724 } 725 726 func TestTimeout(t *testing.T) { 727 ctx := context.Background() 728 lastID.Set(0) 729 count.Set(0) 730 p := NewResourcePool(PoolFactory, 1, 1, time.Second, 0, logWait, nil, 0) 731 defer p.Close() 732 733 // take the only connection available 734 r, err := p.Get(ctx, nil) 735 require.NoError(t, err) 736 737 for _, setting := range []*Setting{nil, sFoo} { 738 // trying to get the connection without a timeout. 739 newctx, cancel := context.WithTimeout(ctx, 10*time.Millisecond) 740 _, err = p.Get(newctx, setting) 741 cancel() 742 assert.EqualError(t, err, "resource pool timed out") 743 744 } 745 746 // put the connection take was taken initially. 747 p.Put(r) 748 } 749 750 func TestExpired(t *testing.T) { 751 lastID.Set(0) 752 count.Set(0) 753 p := NewResourcePool(PoolFactory, 1, 1, time.Second, 0, logWait, nil, 0) 754 defer p.Close() 755 756 for _, setting := range []*Setting{nil, sFoo} { 757 // expired context 758 ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(-1*time.Second)) 759 _, err := p.Get(ctx, setting) 760 cancel() 761 require.EqualError(t, err, "resource pool context already expired") 762 } 763 } 764 765 func TestMultiSettings(t *testing.T) { 766 ctx := context.Background() 767 lastID.Set(0) 768 count.Set(0) 769 waitStarts = waitStarts[:0] 770 771 p := NewResourcePool(PoolFactory, 5, 5, time.Second, 0, logWait, nil, 0) 772 var resources [10]Resource 773 var r Resource 774 var err error 775 776 settings := []*Setting{nil, sFoo, sBar, sBar, sFoo} 777 778 // Test Get 779 for i := 0; i < 5; i++ { 780 r, err = p.Get(ctx, settings[i]) 781 require.NoError(t, err) 782 resources[i] = r 783 assert.EqualValues(t, 5-i-1, p.Available()) 784 assert.Zero(t, p.WaitCount()) 785 assert.Zero(t, len(waitStarts)) 786 assert.Zero(t, p.WaitTime()) 787 assert.EqualValues(t, i+1, lastID.Get()) 788 assert.EqualValues(t, i+1, count.Get()) 789 } 790 791 // Test that Get waits 792 ch := make(chan bool) 793 go func() { 794 for i := 0; i < 5; i++ { 795 r, err = p.Get(ctx, settings[i]) 796 require.NoError(t, err) 797 resources[i] = r 798 } 799 for i := 0; i < 5; i++ { 800 p.Put(resources[i]) 801 } 802 ch <- true 803 }() 804 for i := 0; i < 5; i++ { 805 // Sleep to ensure the goroutine waits 806 time.Sleep(10 * time.Millisecond) 807 p.Put(resources[i]) 808 } 809 <-ch 810 assert.EqualValues(t, 5, p.WaitCount()) 811 assert.Equal(t, 5, len(waitStarts)) 812 // verify start times are monotonic increasing 813 for i := 1; i < len(waitStarts); i++ { 814 if waitStarts[i].Before(waitStarts[i-1]) { 815 t.Errorf("Expecting monotonic increasing start times") 816 } 817 } 818 assert.NotZero(t, p.WaitTime()) 819 assert.EqualValues(t, 5, lastID.Get()) 820 821 // Close 822 p.Close() 823 assert.EqualValues(t, 0, p.Capacity()) 824 assert.EqualValues(t, 0, p.Available()) 825 assert.EqualValues(t, 0, count.Get()) 826 } 827 828 func TestMultiSettingsWithReset(t *testing.T) { 829 ctx := context.Background() 830 lastID.Set(0) 831 count.Set(0) 832 resetCount.Set(0) 833 834 p := NewResourcePool(PoolFactory, 5, 5, time.Second, 0, logWait, nil, 0) 835 var resources [10]Resource 836 var r Resource 837 var err error 838 839 settings := []*Setting{nil, sFoo, sBar, sBar, sFoo} 840 841 // Test Get 842 for i := 0; i < 5; i++ { 843 r, err = p.Get(ctx, settings[i]) 844 require.NoError(t, err) 845 resources[i] = r 846 assert.EqualValues(t, 5-i-1, p.Available()) 847 assert.EqualValues(t, i+1, lastID.Get()) 848 assert.EqualValues(t, i+1, count.Get()) 849 } 850 851 // Put all of them back 852 for i := 0; i < 5; i++ { 853 p.Put(resources[i]) 854 } 855 856 // Getting all with same setting. 857 for i := 0; i < 5; i++ { 858 r, err = p.Get(ctx, settings[1]) // {foo} 859 require.NoError(t, err) 860 p.Put(r) 861 } 862 assert.EqualValues(t, 2, resetCount.Get()) // when setting was {bar} and getting for {foo} 863 assert.EqualValues(t, 5, p.Available()) 864 assert.EqualValues(t, 5, lastID.Get()) 865 assert.EqualValues(t, 5, count.Get()) 866 867 // Close 868 p.Close() 869 assert.EqualValues(t, 0, p.Capacity()) 870 assert.EqualValues(t, 0, p.Available()) 871 assert.EqualValues(t, 0, count.Get()) 872 } 873 874 func TestApplySettingsFailure(t *testing.T) { 875 ctx := context.Background() 876 var resources []Resource 877 var r Resource 878 var err error 879 880 p := NewResourcePool(PoolFactory, 5, 5, time.Second, 0, logWait, nil, 0) 881 defer p.Close() 882 883 settings := []*Setting{nil, sFoo, sBar, sBar, sFoo} 884 // get the resource and mark for failure 885 for i := 0; i < 5; i++ { 886 r, err = p.Get(ctx, settings[i]) 887 require.NoError(t, err) 888 r.(*TestResource).failApply = true 889 resources = append(resources, r) 890 } 891 // put them back 892 for _, r = range resources { 893 p.Put(r) 894 } 895 896 // any new connection created will fail to apply setting 897 p.factory = DisallowSettingsFactory 898 899 // Get the resource with "foo" setting 900 // For an applied connection if the setting are same it will be returned as-is. 901 // Otherwise, will fail to get the resource. 902 var failCount int 903 resources = nil 904 for i := 0; i < 5; i++ { 905 r, err = p.Get(ctx, settings[1]) 906 if err != nil { 907 failCount++ 908 assert.EqualError(t, err, "ApplySetting failed") 909 continue 910 } 911 resources = append(resources, r) 912 } 913 // put them back 914 for _, r = range resources { 915 p.Put(r) 916 } 917 require.Equal(t, 3, failCount) 918 919 // should be able to get all the resource with no setting 920 resources = nil 921 for i := 0; i < 5; i++ { 922 r, err = p.Get(ctx, nil) 923 require.NoError(t, err) 924 resources = append(resources, r) 925 } 926 // put them back 927 for _, r = range resources { 928 p.Put(r) 929 } 930 }