github.phpd.cn/thought-machine/please@v12.2.0+incompatible/tools/cache/server/cache_stress_test.go (about)

     1  // Small stress test for the cache to help flush out concurrency bugs.
     2  package server
     3  
     4  import (
     5  	"crypto/sha1"
     6  	"fmt"
     7  	"math/rand"
     8  	"os"
     9  	"strconv"
    10  	"sync"
    11  	"testing"
    12  
    13  	"github.com/stretchr/testify/assert"
    14  	"gopkg.in/op/go-logging.v1"
    15  )
    16  
    17  var cache *Cache
    18  
    19  const size = 1000
    20  
    21  func init() {
    22  	logging.SetLevel(logging.NOTICE, "server")
    23  	cache = newCache("cache")
    24  }
    25  
    26  func TestStoreFiles(t *testing.T) {
    27  	// Store 1000 files in parallel
    28  	var wg sync.WaitGroup
    29  	wg.Add(size)
    30  	for _, i := range rand.Perm(size) {
    31  		go func(i int) {
    32  			path, contents := artifact(i)
    33  			assert.NoError(t, cache.StoreArtifact(path, contents, ""))
    34  			wg.Done()
    35  		}(i)
    36  	}
    37  	wg.Wait()
    38  }
    39  
    40  func TestRetrieveFiles(t *testing.T) {
    41  	// Store 1000 files in parallel
    42  	var wg sync.WaitGroup
    43  	wg.Add(size)
    44  	for _, i := range rand.Perm(size) {
    45  		go func(i int) {
    46  			path, contents := artifact(i)
    47  			arts, err := cache.RetrieveArtifact(path)
    48  			if os.IsNotExist(err) { // It's allowed not to exist.
    49  				wg.Done()
    50  				return
    51  			}
    52  			assert.NoError(t, err)
    53  			assert.Equal(t, 1, len(arts))
    54  			assert.Equal(t, contents, arts[0].Body)
    55  			wg.Done()
    56  		}(i)
    57  	}
    58  	wg.Wait()
    59  }
    60  
    61  func TestDeleteFiles(t *testing.T) {
    62  	// Delete 1000 files in parallel
    63  	var wg sync.WaitGroup
    64  	wg.Add(size)
    65  	for _, i := range rand.Perm(size) {
    66  		go func(i int) {
    67  			path, _ := artifact(i)
    68  			assert.NoError(t, cache.DeleteArtifact(path))
    69  			wg.Done()
    70  		}(i)
    71  	}
    72  	wg.Wait()
    73  }
    74  
    75  func TestInParallel(t *testing.T) {
    76  	// Run all above tests in parallel.
    77  	var wg sync.WaitGroup
    78  	wg.Add(3)
    79  	go func() {
    80  		TestStoreFiles(t)
    81  		wg.Done()
    82  	}()
    83  	go func() {
    84  		TestRetrieveFiles(t)
    85  		wg.Done()
    86  	}()
    87  	go func() {
    88  		TestDeleteFiles(t)
    89  		wg.Done()
    90  	}()
    91  	wg.Wait()
    92  }
    93  
    94  func TestInParallelWithCleaning(t *testing.T) {
    95  	// Run store & retrieve tests in parallel & cleaning at the same time.
    96  	// It's too awkward to write a reliable test with deleting too and actually
    97  	// guarantee that the cleaner does anything.
    98  	var wg sync.WaitGroup
    99  	wg.Add(3)
   100  	go func() {
   101  		TestStoreFiles(t)
   102  		wg.Done()
   103  	}()
   104  	go func() {
   105  		TestRetrieveFiles(t)
   106  		wg.Done()
   107  	}()
   108  	go func() {
   109  		// Each artifact is sha1.Size bytes long, so this should guarantee it runs once.
   110  		for !cache.singleClean(sha1.Size*size/10, sha1.Size*size/2) {
   111  		}
   112  		// Now run it a bunch more times.
   113  		for i := 0; i < 100; i++ {
   114  			cache.singleClean(sha1.Size*size/10, sha1.Size*size/2)
   115  		}
   116  		wg.Done()
   117  	}()
   118  	wg.Wait()
   119  }
   120  
   121  func artifact(i int) (string, []byte) {
   122  	path := fmt.Sprintf("src/%d/%d/%d.dat", i/100, i/10, i)
   123  	contents := sha1.Sum([]byte(strconv.Itoa(i)))
   124  	return path, contents[:]
   125  }