...

Source file src/github.com/thales-e-security/pool/resource_pool_test.go

Documentation: github.com/thales-e-security/pool

     1  /*
     2  Copyright 2017 Google Inc.
     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  Modified by Duncan Jones to remove external dependencies.
    17  */
    18  
    19  package pool
    20  
    21  import (
    22  	"context"
    23  	"errors"
    24  	"testing"
    25  	"time"
    26  )
    27  
    28  var lastID, count AtomicInt64
    29  
    30  type TestResource struct {
    31  	num    int64
    32  	closed bool
    33  }
    34  
    35  func (tr *TestResource) Close() {
    36  	if !tr.closed {
    37  		count.Add(-1)
    38  		tr.closed = true
    39  	}
    40  }
    41  
    42  func PoolFactory() (Resource, error) {
    43  	count.Add(1)
    44  	return &TestResource{lastID.Add(1), false}, nil
    45  }
    46  
    47  func FailFactory() (Resource, error) {
    48  	return nil, errors.New("Failed")
    49  }
    50  
    51  func SlowFailFactory() (Resource, error) {
    52  	time.Sleep(10 * time.Millisecond)
    53  	return nil, errors.New("Failed")
    54  }
    55  
    56  func TestOpen(t *testing.T) {
    57  	ctx := context.Background()
    58  	lastID.Set(0)
    59  	count.Set(0)
    60  	p := NewResourcePool(PoolFactory, 6, 6, time.Second, 0)
    61  	p.SetCapacity(5)
    62  	var resources [10]Resource
    63  
    64  	// Test Get
    65  	for i := 0; i < 5; i++ {
    66  		r, err := p.Get(ctx)
    67  		resources[i] = r
    68  		if err != nil {
    69  			t.Errorf("Unexpected error %v", err)
    70  		}
    71  		if p.Available() != int64(5-i-1) {
    72  			t.Errorf("expecting %d, received %d", 5-i-1, p.Available())
    73  		}
    74  		if p.WaitCount() != 0 {
    75  			t.Errorf("expecting 0, received %d", p.WaitCount())
    76  		}
    77  		if p.WaitTime() != 0 {
    78  			t.Errorf("expecting 0, received %d", p.WaitTime())
    79  		}
    80  		if lastID.Get() != int64(i+1) {
    81  			t.Errorf("Expecting %d, received %d", i+1, lastID.Get())
    82  		}
    83  		if count.Get() != int64(i+1) {
    84  			t.Errorf("Expecting %d, received %d", i+1, count.Get())
    85  		}
    86  	}
    87  
    88  	// Test that Get waits
    89  	ch := make(chan bool)
    90  	go func() {
    91  		for i := 0; i < 5; i++ {
    92  			r, err := p.Get(ctx)
    93  			if err != nil {
    94  				t.Errorf("Get failed: %v", err)
    95  			}
    96  			resources[i] = r
    97  		}
    98  		for i := 0; i < 5; i++ {
    99  			p.Put(resources[i])
   100  		}
   101  		ch <- true
   102  	}()
   103  	for i := 0; i < 5; i++ {
   104  		// Sleep to ensure the goroutine waits
   105  		time.Sleep(10 * time.Millisecond)
   106  		p.Put(resources[i])
   107  	}
   108  	<-ch
   109  	if p.WaitCount() != 5 {
   110  		t.Errorf("Expecting 5, received %d", p.WaitCount())
   111  	}
   112  	if p.WaitTime() == 0 {
   113  		t.Errorf("Expecting non-zero")
   114  	}
   115  	if lastID.Get() != 5 {
   116  		t.Errorf("Expecting 5, received %d", lastID.Get())
   117  	}
   118  
   119  	// Test Close resource
   120  	r, err := p.Get(ctx)
   121  	if err != nil {
   122  		t.Errorf("Unexpected error %v", err)
   123  	}
   124  	r.Close()
   125  	// A nil Put should cause the resource to be reopened.
   126  	p.Put(nil)
   127  	if count.Get() != 5 {
   128  		t.Errorf("Expecting 5, received %d", count.Get())
   129  	}
   130  	for i := 0; i < 5; i++ {
   131  		r, err := p.Get(ctx)
   132  		if err != nil {
   133  			t.Errorf("Get failed: %v", err)
   134  		}
   135  		resources[i] = r
   136  	}
   137  	for i := 0; i < 5; i++ {
   138  		p.Put(resources[i])
   139  	}
   140  	if count.Get() != 5 {
   141  		t.Errorf("Expecting 5, received %d", count.Get())
   142  	}
   143  	if lastID.Get() != 6 {
   144  		t.Errorf("Expecting 6, received %d", lastID.Get())
   145  	}
   146  
   147  	// SetCapacity
   148  	p.SetCapacity(3)
   149  	if count.Get() != 3 {
   150  		t.Errorf("Expecting 3, received %d", count.Get())
   151  	}
   152  	if lastID.Get() != 6 {
   153  		t.Errorf("Expecting 6, received %d", lastID.Get())
   154  	}
   155  	if p.Capacity() != 3 {
   156  		t.Errorf("Expecting 3, received %d", p.Capacity())
   157  	}
   158  	if p.Available() != 3 {
   159  		t.Errorf("Expecting 3, received %d", p.Available())
   160  	}
   161  	p.SetCapacity(6)
   162  	if p.Capacity() != 6 {
   163  		t.Errorf("Expecting 6, received %d", p.Capacity())
   164  	}
   165  	if p.Available() != 6 {
   166  		t.Errorf("Expecting 6, received %d", p.Available())
   167  	}
   168  	for i := 0; i < 6; i++ {
   169  		r, err := p.Get(ctx)
   170  		if err != nil {
   171  			t.Errorf("Get failed: %v", err)
   172  		}
   173  		resources[i] = r
   174  	}
   175  	for i := 0; i < 6; i++ {
   176  		p.Put(resources[i])
   177  	}
   178  	if count.Get() != 6 {
   179  		t.Errorf("Expecting 5, received %d", count.Get())
   180  	}
   181  	if lastID.Get() != 9 {
   182  		t.Errorf("Expecting 9, received %d", lastID.Get())
   183  	}
   184  
   185  	// Close
   186  	p.Close()
   187  	if p.Capacity() != 0 {
   188  		t.Errorf("Expecting 0, received %d", p.Capacity())
   189  	}
   190  	if p.Available() != 0 {
   191  		t.Errorf("Expecting 0, received %d", p.Available())
   192  	}
   193  	if count.Get() != 0 {
   194  		t.Errorf("Expecting 0, received %d", count.Get())
   195  	}
   196  }
   197  
   198  func TestPrefill(t *testing.T) {
   199  	lastID.Set(0)
   200  	count.Set(0)
   201  	p := NewResourcePool(PoolFactory, 5, 5, time.Second, 1)
   202  	defer p.Close()
   203  	if p.Active() != 5 {
   204  		t.Errorf("p.Active(): %d, want 5", p.Active())
   205  	}
   206  	p = NewResourcePool(FailFactory, 5, 5, time.Second, 1)
   207  	defer p.Close()
   208  	if p.Active() != 0 {
   209  		t.Errorf("p.Active(): %d, want 0", p.Active())
   210  	}
   211  }
   212  
   213  func TestPrefillTimeout(t *testing.T) {
   214  	lastID.Set(0)
   215  	count.Set(0)
   216  	saveTimeout := prefillTimeout
   217  	prefillTimeout = 1 * time.Millisecond
   218  	defer func() { prefillTimeout = saveTimeout }()
   219  
   220  	start := time.Now()
   221  	p := NewResourcePool(SlowFailFactory, 5, 5, time.Second, 1)
   222  	defer p.Close()
   223  	if elapsed := time.Since(start); elapsed > 20*time.Millisecond {
   224  		t.Errorf("elapsed: %v, should be around 10ms", elapsed)
   225  	}
   226  	if p.Active() != 0 {
   227  		t.Errorf("p.Active(): %d, want 0", p.Active())
   228  	}
   229  }
   230  
   231  func TestShrinking(t *testing.T) {
   232  	ctx := context.Background()
   233  	lastID.Set(0)
   234  	count.Set(0)
   235  	p := NewResourcePool(PoolFactory, 5, 5, time.Second, 0)
   236  	var resources [10]Resource
   237  	// Leave one empty slot in the pool
   238  	for i := 0; i < 4; i++ {
   239  		r, err := p.Get(ctx)
   240  		if err != nil {
   241  			t.Errorf("Get failed: %v", err)
   242  		}
   243  		resources[i] = r
   244  	}
   245  	done := make(chan bool)
   246  	go func() {
   247  		p.SetCapacity(3)
   248  		done <- true
   249  	}()
   250  	expected := `{"Capacity": 3, "Available": 0, "Active": 4, "InUse": 4, "MaxCapacity": 5, "WaitCount": 0, "WaitTime": 0, "IdleTimeout": 1000000000, "IdleClosed": 0}`
   251  	for i := 0; i < 10; i++ {
   252  		time.Sleep(10 * time.Millisecond)
   253  		stats := p.StatsJSON()
   254  		if stats != expected {
   255  			if i == 9 {
   256  				t.Errorf(`expecting '%s', received '%s'`, expected, stats)
   257  			}
   258  		}
   259  	}
   260  	// There are already 2 resources available in the pool.
   261  	// So, returning one should be enough for SetCapacity to complete.
   262  	p.Put(resources[3])
   263  	<-done
   264  	// Return the rest of the resources
   265  	for i := 0; i < 3; i++ {
   266  		p.Put(resources[i])
   267  	}
   268  	stats := p.StatsJSON()
   269  	expected = `{"Capacity": 3, "Available": 3, "Active": 3, "InUse": 0, "MaxCapacity": 5, "WaitCount": 0, "WaitTime": 0, "IdleTimeout": 1000000000, "IdleClosed": 0}`
   270  	if stats != expected {
   271  		t.Errorf(`expecting '%s', received '%s'`, expected, stats)
   272  	}
   273  	if count.Get() != 3 {
   274  		t.Errorf("Expecting 3, received %d", count.Get())
   275  	}
   276  
   277  	// Ensure no deadlock if SetCapacity is called after we start
   278  	// waiting for a resource
   279  	var err error
   280  	for i := 0; i < 3; i++ {
   281  		resources[i], err = p.Get(ctx)
   282  		if err != nil {
   283  			t.Errorf("Unexpected error %v", err)
   284  		}
   285  	}
   286  	// This will wait because pool is empty
   287  	go func() {
   288  		r, err := p.Get(ctx)
   289  		if err != nil {
   290  			t.Errorf("Unexpected error %v", err)
   291  		}
   292  		p.Put(r)
   293  		done <- true
   294  	}()
   295  
   296  	// This will also wait
   297  	go func() {
   298  		p.SetCapacity(2)
   299  		done <- true
   300  	}()
   301  	time.Sleep(10 * time.Millisecond)
   302  
   303  	// This should not hang
   304  	for i := 0; i < 3; i++ {
   305  		p.Put(resources[i])
   306  	}
   307  	<-done
   308  	<-done
   309  	if p.Capacity() != 2 {
   310  		t.Errorf("Expecting 2, received %d", p.Capacity())
   311  	}
   312  	if p.Available() != 2 {
   313  		t.Errorf("Expecting 2, received %d", p.Available())
   314  	}
   315  	if p.WaitCount() != 1 {
   316  		t.Errorf("Expecting 1, received %d", p.WaitCount())
   317  	}
   318  	if count.Get() != 2 {
   319  		t.Errorf("Expecting 2, received %d", count.Get())
   320  	}
   321  
   322  	// Test race condition of SetCapacity with itself
   323  	p.SetCapacity(3)
   324  	for i := 0; i < 3; i++ {
   325  		resources[i], err = p.Get(ctx)
   326  		if err != nil {
   327  			t.Errorf("Unexpected error %v", err)
   328  		}
   329  	}
   330  	// This will wait because pool is empty
   331  	go func() {
   332  		r, err := p.Get(ctx)
   333  		if err != nil {
   334  			t.Errorf("Unexpected error %v", err)
   335  		}
   336  		p.Put(r)
   337  		done <- true
   338  	}()
   339  	time.Sleep(10 * time.Millisecond)
   340  
   341  	// This will wait till we Put
   342  	go p.SetCapacity(2)
   343  	time.Sleep(10 * time.Millisecond)
   344  	go p.SetCapacity(4)
   345  	time.Sleep(10 * time.Millisecond)
   346  
   347  	// This should not hang
   348  	for i := 0; i < 3; i++ {
   349  		p.Put(resources[i])
   350  	}
   351  	<-done
   352  
   353  	err = p.SetCapacity(-1)
   354  	if err == nil {
   355  		t.Errorf("Expecting error")
   356  	}
   357  	err = p.SetCapacity(255555)
   358  	if err == nil {
   359  		t.Errorf("Expecting error")
   360  	}
   361  
   362  	if p.Capacity() != 4 {
   363  		t.Errorf("Expecting 4, received %d", p.Capacity())
   364  	}
   365  	if p.Available() != 4 {
   366  		t.Errorf("Expecting 4, received %d", p.Available())
   367  	}
   368  }
   369  
   370  func TestClosing(t *testing.T) {
   371  	ctx := context.Background()
   372  	lastID.Set(0)
   373  	count.Set(0)
   374  	p := NewResourcePool(PoolFactory, 5, 5, time.Second, 0)
   375  	var resources [10]Resource
   376  	for i := 0; i < 5; i++ {
   377  		r, err := p.Get(ctx)
   378  		if err != nil {
   379  			t.Errorf("Get failed: %v", err)
   380  		}
   381  		resources[i] = r
   382  	}
   383  	ch := make(chan bool)
   384  	go func() {
   385  		p.Close()
   386  		ch <- true
   387  	}()
   388  
   389  	// Wait for goroutine to call Close
   390  	time.Sleep(10 * time.Millisecond)
   391  	stats := p.StatsJSON()
   392  	expected := `{"Capacity": 0, "Available": 0, "Active": 5, "InUse": 5, "MaxCapacity": 5, "WaitCount": 0, "WaitTime": 0, "IdleTimeout": 1000000000, "IdleClosed": 0}`
   393  	if stats != expected {
   394  		t.Errorf(`expecting '%s', received '%s'`, expected, stats)
   395  	}
   396  
   397  	// Put is allowed when closing
   398  	for i := 0; i < 5; i++ {
   399  		p.Put(resources[i])
   400  	}
   401  
   402  	// Wait for Close to return
   403  	<-ch
   404  
   405  	// SetCapacity must be ignored after Close
   406  	err := p.SetCapacity(1)
   407  	if err == nil {
   408  		t.Errorf("expecting error")
   409  	}
   410  
   411  	stats = p.StatsJSON()
   412  	expected = `{"Capacity": 0, "Available": 0, "Active": 0, "InUse": 0, "MaxCapacity": 5, "WaitCount": 0, "WaitTime": 0, "IdleTimeout": 1000000000, "IdleClosed": 0}`
   413  	if stats != expected {
   414  		t.Errorf(`expecting '%s', received '%s'`, expected, stats)
   415  	}
   416  	if lastID.Get() != 5 {
   417  		t.Errorf("Expecting 5, received %d", count.Get())
   418  	}
   419  	if count.Get() != 0 {
   420  		t.Errorf("Expecting 0, received %d", count.Get())
   421  	}
   422  }
   423  
   424  func TestIdleTimeout(t *testing.T) {
   425  	ctx := context.Background()
   426  	lastID.Set(0)
   427  	count.Set(0)
   428  	p := NewResourcePool(PoolFactory, 1, 1, 10*time.Millisecond, 0)
   429  	defer p.Close()
   430  
   431  	r, err := p.Get(ctx)
   432  	if err != nil {
   433  		t.Errorf("Unexpected error %v", err)
   434  	}
   435  	if count.Get() != 1 {
   436  		t.Errorf("Expecting 1, received %d", count.Get())
   437  	}
   438  	if p.IdleClosed() != 0 {
   439  		t.Errorf("Expecting 0, received %d", p.IdleClosed())
   440  	}
   441  	p.Put(r)
   442  	if lastID.Get() != 1 {
   443  		t.Errorf("Expecting 1, received %d", count.Get())
   444  	}
   445  	if count.Get() != 1 {
   446  		t.Errorf("Expecting 1, received %d", count.Get())
   447  	}
   448  	if p.IdleClosed() != 0 {
   449  		t.Errorf("Expecting 0, received %d", p.IdleClosed())
   450  	}
   451  	time.Sleep(15 * time.Millisecond)
   452  
   453  	if count.Get() != 1 {
   454  		t.Errorf("Expecting 1, received %d", count.Get())
   455  	}
   456  	if p.IdleClosed() != 1 {
   457  		t.Errorf("Expecting 1, received %d", p.IdleClosed())
   458  	}
   459  	r, err = p.Get(ctx)
   460  	if err != nil {
   461  		t.Errorf("Unexpected error %v", err)
   462  	}
   463  	if lastID.Get() != 2 {
   464  		t.Errorf("Expecting 2, received %d", count.Get())
   465  	}
   466  	if count.Get() != 1 {
   467  		t.Errorf("Expecting 1, received %d", count.Get())
   468  	}
   469  	if p.IdleClosed() != 1 {
   470  		t.Errorf("Expecting 1, received %d", p.IdleClosed())
   471  	}
   472  
   473  	// sleep to let the idle closer run while all resources are in use
   474  	// then make sure things are still as we expect
   475  	time.Sleep(15 * time.Millisecond)
   476  	if lastID.Get() != 2 {
   477  		t.Errorf("Expecting 2, received %d", count.Get())
   478  	}
   479  	if count.Get() != 1 {
   480  		t.Errorf("Expecting 1, received %d", count.Get())
   481  	}
   482  	if p.IdleClosed() != 1 {
   483  		t.Errorf("Expecting 1, received %d", p.IdleClosed())
   484  	}
   485  	p.Put(r)
   486  	r, err = p.Get(ctx)
   487  	if err != nil {
   488  		t.Errorf("Unexpected error %v", err)
   489  	}
   490  	if lastID.Get() != 2 {
   491  		t.Errorf("Expecting 2, received %d", count.Get())
   492  	}
   493  	if count.Get() != 1 {
   494  		t.Errorf("Expecting 1, received %d", count.Get())
   495  	}
   496  	if p.IdleClosed() != 1 {
   497  		t.Errorf("Expecting 1, received %d", p.IdleClosed())
   498  	}
   499  
   500  	// the idle close thread wakes up every 1/100 of the idle time, so ensure
   501  	// the timeout change applies to newly added resources
   502  	p.SetIdleTimeout(1000 * time.Millisecond)
   503  	p.Put(r)
   504  
   505  	time.Sleep(15 * time.Millisecond)
   506  	if lastID.Get() != 2 {
   507  		t.Errorf("Expecting 2, received %d", count.Get())
   508  	}
   509  	if count.Get() != 1 {
   510  		t.Errorf("Expecting 1, received %d", count.Get())
   511  	}
   512  	if p.IdleClosed() != 1 {
   513  		t.Errorf("Expecting 1, received %d", p.IdleClosed())
   514  	}
   515  
   516  	// Get and Put to refresh timeUsed
   517  	r, err = p.Get(ctx)
   518  	if err != nil {
   519  		t.Errorf("Unexpected error %v", err)
   520  	}
   521  	p.Put(r)
   522  	p.SetIdleTimeout(10 * time.Millisecond)
   523  	time.Sleep(15 * time.Millisecond)
   524  	if lastID.Get() != 3 {
   525  		t.Errorf("Expecting 3, received %d", lastID.Get())
   526  	}
   527  	if count.Get() != 1 {
   528  		t.Errorf("Expecting 1, received %d", count.Get())
   529  	}
   530  	if p.IdleClosed() != 2 {
   531  		t.Errorf("Expecting 2, received %d", p.IdleClosed())
   532  	}
   533  }
   534  
   535  func TestIdleTimeoutCreateFail(t *testing.T) {
   536  	ctx := context.Background()
   537  	lastID.Set(0)
   538  	count.Set(0)
   539  	p := NewResourcePool(PoolFactory, 1, 1, 10*time.Millisecond, 0)
   540  	defer p.Close()
   541  	r, err := p.Get(ctx)
   542  	if err != nil {
   543  		t.Fatal(err)
   544  	}
   545  	// Change the factory before putting back
   546  	// to prevent race with the idle closer, who will
   547  	// try to use it.
   548  	p.factory = FailFactory
   549  	p.Put(r)
   550  	time.Sleep(15 * time.Millisecond)
   551  	if p.Active() != 0 {
   552  		t.Errorf("p.Active(): %d, want 0", p.Active())
   553  	}
   554  }
   555  
   556  func TestCreateFail(t *testing.T) {
   557  	ctx := context.Background()
   558  	lastID.Set(0)
   559  	count.Set(0)
   560  	p := NewResourcePool(FailFactory, 5, 5, time.Second, 0)
   561  	defer p.Close()
   562  	if _, err := p.Get(ctx); err.Error() != "Failed" {
   563  		t.Errorf("Expecting Failed, received %v", err)
   564  	}
   565  	stats := p.StatsJSON()
   566  	expected := `{"Capacity": 5, "Available": 5, "Active": 0, "InUse": 0, "MaxCapacity": 5, "WaitCount": 0, "WaitTime": 0, "IdleTimeout": 1000000000, "IdleClosed": 0}`
   567  	if stats != expected {
   568  		t.Errorf(`expecting '%s', received '%s'`, expected, stats)
   569  	}
   570  }
   571  
   572  func TestCreateFailOnPut(t *testing.T) {
   573  	ctx := context.Background()
   574  	lastID.Set(0)
   575  	count.Set(0)
   576  	p := NewResourcePool(PoolFactory, 5, 5, time.Second, 0)
   577  	defer p.Close()
   578  	_, err := p.Get(ctx)
   579  	if err != nil {
   580  		t.Fatal(err)
   581  	}
   582  	p.factory = FailFactory
   583  	p.Put(nil)
   584  	if p.Active() != 0 {
   585  		t.Errorf("p.Active(): %d, want 0", p.Active())
   586  	}
   587  }
   588  
   589  func TestSlowCreateFail(t *testing.T) {
   590  	ctx := context.Background()
   591  	lastID.Set(0)
   592  	count.Set(0)
   593  	p := NewResourcePool(SlowFailFactory, 2, 2, time.Second, 0)
   594  	defer p.Close()
   595  	ch := make(chan bool)
   596  	// The third Get should not wait indefinitely
   597  	for i := 0; i < 3; i++ {
   598  		go func() {
   599  			p.Get(ctx)
   600  			ch <- true
   601  		}()
   602  	}
   603  	for i := 0; i < 3; i++ {
   604  		<-ch
   605  	}
   606  	if p.Available() != 2 {
   607  		t.Errorf("Expecting 2, received %d", p.Available())
   608  	}
   609  }
   610  
   611  func TestTimeout(t *testing.T) {
   612  	ctx := context.Background()
   613  	lastID.Set(0)
   614  	count.Set(0)
   615  	p := NewResourcePool(PoolFactory, 1, 1, time.Second, 0)
   616  	defer p.Close()
   617  	r, err := p.Get(ctx)
   618  	if err != nil {
   619  		t.Fatal(err)
   620  	}
   621  	newctx, cancel := context.WithTimeout(ctx, 1*time.Millisecond)
   622  	_, err = p.Get(newctx)
   623  	cancel()
   624  	want := "resource pool timed out"
   625  	if err == nil || err.Error() != want {
   626  		t.Errorf("got %v, want %s", err, want)
   627  	}
   628  	p.Put(r)
   629  }
   630  
   631  func TestExpired(t *testing.T) {
   632  	lastID.Set(0)
   633  	count.Set(0)
   634  	p := NewResourcePool(PoolFactory, 1, 1, time.Second, 0)
   635  	defer p.Close()
   636  	ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(-1*time.Second))
   637  	r, err := p.Get(ctx)
   638  	if err == nil {
   639  		p.Put(r)
   640  	}
   641  	cancel()
   642  	want := "resource pool timed out"
   643  	if err == nil || err.Error() != want {
   644  		t.Errorf("got %v, want %s", err, want)
   645  	}
   646  }
   647  

View as plain text