github.com/ojongerius/docker@v1.11.2/daemon/graphdriver/aufs/aufs_test.go (about)

     1  // +build linux
     2  
     3  package aufs
     4  
     5  import (
     6  	"crypto/sha256"
     7  	"encoding/hex"
     8  	"fmt"
     9  	"io/ioutil"
    10  	"os"
    11  	"path"
    12  	"sync"
    13  	"testing"
    14  
    15  	"github.com/docker/docker/daemon/graphdriver"
    16  	"github.com/docker/docker/pkg/archive"
    17  	"github.com/docker/docker/pkg/reexec"
    18  	"github.com/docker/docker/pkg/stringid"
    19  )
    20  
    21  var (
    22  	tmpOuter = path.Join(os.TempDir(), "aufs-tests")
    23  	tmp      = path.Join(tmpOuter, "aufs")
    24  )
    25  
    26  func init() {
    27  	reexec.Init()
    28  }
    29  
    30  func testInit(dir string, t testing.TB) graphdriver.Driver {
    31  	d, err := Init(dir, nil, nil, nil)
    32  	if err != nil {
    33  		if err == graphdriver.ErrNotSupported {
    34  			t.Skip(err)
    35  		} else {
    36  			t.Fatal(err)
    37  		}
    38  	}
    39  	return d
    40  }
    41  
    42  func newDriver(t testing.TB) *Driver {
    43  	if err := os.MkdirAll(tmp, 0755); err != nil {
    44  		t.Fatal(err)
    45  	}
    46  
    47  	d := testInit(tmp, t)
    48  	return d.(*Driver)
    49  }
    50  
    51  func TestNewDriver(t *testing.T) {
    52  	if err := os.MkdirAll(tmp, 0755); err != nil {
    53  		t.Fatal(err)
    54  	}
    55  
    56  	d := testInit(tmp, t)
    57  	defer os.RemoveAll(tmp)
    58  	if d == nil {
    59  		t.Fatalf("Driver should not be nil")
    60  	}
    61  }
    62  
    63  func TestAufsString(t *testing.T) {
    64  	d := newDriver(t)
    65  	defer os.RemoveAll(tmp)
    66  
    67  	if d.String() != "aufs" {
    68  		t.Fatalf("Expected aufs got %s", d.String())
    69  	}
    70  }
    71  
    72  func TestCreateDirStructure(t *testing.T) {
    73  	newDriver(t)
    74  	defer os.RemoveAll(tmp)
    75  
    76  	paths := []string{
    77  		"mnt",
    78  		"layers",
    79  		"diff",
    80  	}
    81  
    82  	for _, p := range paths {
    83  		if _, err := os.Stat(path.Join(tmp, p)); err != nil {
    84  			t.Fatal(err)
    85  		}
    86  	}
    87  }
    88  
    89  // We should be able to create two drivers with the same dir structure
    90  func TestNewDriverFromExistingDir(t *testing.T) {
    91  	if err := os.MkdirAll(tmp, 0755); err != nil {
    92  		t.Fatal(err)
    93  	}
    94  
    95  	testInit(tmp, t)
    96  	testInit(tmp, t)
    97  	os.RemoveAll(tmp)
    98  }
    99  
   100  func TestCreateNewDir(t *testing.T) {
   101  	d := newDriver(t)
   102  	defer os.RemoveAll(tmp)
   103  
   104  	if err := d.Create("1", "", ""); err != nil {
   105  		t.Fatal(err)
   106  	}
   107  }
   108  
   109  func TestCreateNewDirStructure(t *testing.T) {
   110  	d := newDriver(t)
   111  	defer os.RemoveAll(tmp)
   112  
   113  	if err := d.Create("1", "", ""); err != nil {
   114  		t.Fatal(err)
   115  	}
   116  
   117  	paths := []string{
   118  		"mnt",
   119  		"diff",
   120  		"layers",
   121  	}
   122  
   123  	for _, p := range paths {
   124  		if _, err := os.Stat(path.Join(tmp, p, "1")); err != nil {
   125  			t.Fatal(err)
   126  		}
   127  	}
   128  }
   129  
   130  func TestRemoveImage(t *testing.T) {
   131  	d := newDriver(t)
   132  	defer os.RemoveAll(tmp)
   133  
   134  	if err := d.Create("1", "", ""); err != nil {
   135  		t.Fatal(err)
   136  	}
   137  
   138  	if err := d.Remove("1"); err != nil {
   139  		t.Fatal(err)
   140  	}
   141  
   142  	paths := []string{
   143  		"mnt",
   144  		"diff",
   145  		"layers",
   146  	}
   147  
   148  	for _, p := range paths {
   149  		if _, err := os.Stat(path.Join(tmp, p, "1")); err == nil {
   150  			t.Fatalf("Error should not be nil because dirs with id 1 should be delted: %s", p)
   151  		}
   152  	}
   153  }
   154  
   155  func TestGetWithoutParent(t *testing.T) {
   156  	d := newDriver(t)
   157  	defer os.RemoveAll(tmp)
   158  
   159  	if err := d.Create("1", "", ""); err != nil {
   160  		t.Fatal(err)
   161  	}
   162  
   163  	diffPath, err := d.Get("1", "")
   164  	if err != nil {
   165  		t.Fatal(err)
   166  	}
   167  	expected := path.Join(tmp, "diff", "1")
   168  	if diffPath != expected {
   169  		t.Fatalf("Expected path %s got %s", expected, diffPath)
   170  	}
   171  }
   172  
   173  func TestCleanupWithNoDirs(t *testing.T) {
   174  	d := newDriver(t)
   175  	defer os.RemoveAll(tmp)
   176  
   177  	if err := d.Cleanup(); err != nil {
   178  		t.Fatal(err)
   179  	}
   180  }
   181  
   182  func TestCleanupWithDir(t *testing.T) {
   183  	d := newDriver(t)
   184  	defer os.RemoveAll(tmp)
   185  
   186  	if err := d.Create("1", "", ""); err != nil {
   187  		t.Fatal(err)
   188  	}
   189  
   190  	if err := d.Cleanup(); err != nil {
   191  		t.Fatal(err)
   192  	}
   193  }
   194  
   195  func TestMountedFalseResponse(t *testing.T) {
   196  	d := newDriver(t)
   197  	defer os.RemoveAll(tmp)
   198  
   199  	if err := d.Create("1", "", ""); err != nil {
   200  		t.Fatal(err)
   201  	}
   202  
   203  	response, err := d.mounted(d.getDiffPath("1"))
   204  	if err != nil {
   205  		t.Fatal(err)
   206  	}
   207  
   208  	if response != false {
   209  		t.Fatalf("Response if dir id 1 is mounted should be false")
   210  	}
   211  }
   212  
   213  func TestMountedTrueReponse(t *testing.T) {
   214  	d := newDriver(t)
   215  	defer os.RemoveAll(tmp)
   216  	defer d.Cleanup()
   217  
   218  	if err := d.Create("1", "", ""); err != nil {
   219  		t.Fatal(err)
   220  	}
   221  	if err := d.Create("2", "1", ""); err != nil {
   222  		t.Fatal(err)
   223  	}
   224  
   225  	_, err := d.Get("2", "")
   226  	if err != nil {
   227  		t.Fatal(err)
   228  	}
   229  
   230  	response, err := d.mounted(d.pathCache["2"])
   231  	if err != nil {
   232  		t.Fatal(err)
   233  	}
   234  
   235  	if response != true {
   236  		t.Fatalf("Response if dir id 2 is mounted should be true")
   237  	}
   238  }
   239  
   240  func TestMountWithParent(t *testing.T) {
   241  	d := newDriver(t)
   242  	defer os.RemoveAll(tmp)
   243  
   244  	if err := d.Create("1", "", ""); err != nil {
   245  		t.Fatal(err)
   246  	}
   247  	if err := d.Create("2", "1", ""); err != nil {
   248  		t.Fatal(err)
   249  	}
   250  
   251  	defer func() {
   252  		if err := d.Cleanup(); err != nil {
   253  			t.Fatal(err)
   254  		}
   255  	}()
   256  
   257  	mntPath, err := d.Get("2", "")
   258  	if err != nil {
   259  		t.Fatal(err)
   260  	}
   261  	if mntPath == "" {
   262  		t.Fatal("mntPath should not be empty string")
   263  	}
   264  
   265  	expected := path.Join(tmp, "mnt", "2")
   266  	if mntPath != expected {
   267  		t.Fatalf("Expected %s got %s", expected, mntPath)
   268  	}
   269  }
   270  
   271  func TestRemoveMountedDir(t *testing.T) {
   272  	d := newDriver(t)
   273  	defer os.RemoveAll(tmp)
   274  
   275  	if err := d.Create("1", "", ""); err != nil {
   276  		t.Fatal(err)
   277  	}
   278  	if err := d.Create("2", "1", ""); err != nil {
   279  		t.Fatal(err)
   280  	}
   281  
   282  	defer func() {
   283  		if err := d.Cleanup(); err != nil {
   284  			t.Fatal(err)
   285  		}
   286  	}()
   287  
   288  	mntPath, err := d.Get("2", "")
   289  	if err != nil {
   290  		t.Fatal(err)
   291  	}
   292  	if mntPath == "" {
   293  		t.Fatal("mntPath should not be empty string")
   294  	}
   295  
   296  	mounted, err := d.mounted(d.pathCache["2"])
   297  	if err != nil {
   298  		t.Fatal(err)
   299  	}
   300  
   301  	if !mounted {
   302  		t.Fatalf("Dir id 2 should be mounted")
   303  	}
   304  
   305  	if err := d.Remove("2"); err != nil {
   306  		t.Fatal(err)
   307  	}
   308  }
   309  
   310  func TestCreateWithInvalidParent(t *testing.T) {
   311  	d := newDriver(t)
   312  	defer os.RemoveAll(tmp)
   313  
   314  	if err := d.Create("1", "docker", ""); err == nil {
   315  		t.Fatalf("Error should not be nil with parent does not exist")
   316  	}
   317  }
   318  
   319  func TestGetDiff(t *testing.T) {
   320  	d := newDriver(t)
   321  	defer os.RemoveAll(tmp)
   322  
   323  	if err := d.Create("1", "", ""); err != nil {
   324  		t.Fatal(err)
   325  	}
   326  
   327  	diffPath, err := d.Get("1", "")
   328  	if err != nil {
   329  		t.Fatal(err)
   330  	}
   331  
   332  	// Add a file to the diff path with a fixed size
   333  	size := int64(1024)
   334  
   335  	f, err := os.Create(path.Join(diffPath, "test_file"))
   336  	if err != nil {
   337  		t.Fatal(err)
   338  	}
   339  	if err := f.Truncate(size); err != nil {
   340  		t.Fatal(err)
   341  	}
   342  	f.Close()
   343  
   344  	a, err := d.Diff("1", "")
   345  	if err != nil {
   346  		t.Fatal(err)
   347  	}
   348  	if a == nil {
   349  		t.Fatalf("Archive should not be nil")
   350  	}
   351  }
   352  
   353  func TestChanges(t *testing.T) {
   354  	d := newDriver(t)
   355  	defer os.RemoveAll(tmp)
   356  
   357  	if err := d.Create("1", "", ""); err != nil {
   358  		t.Fatal(err)
   359  	}
   360  	if err := d.Create("2", "1", ""); err != nil {
   361  		t.Fatal(err)
   362  	}
   363  
   364  	defer func() {
   365  		if err := d.Cleanup(); err != nil {
   366  			t.Fatal(err)
   367  		}
   368  	}()
   369  
   370  	mntPoint, err := d.Get("2", "")
   371  	if err != nil {
   372  		t.Fatal(err)
   373  	}
   374  
   375  	// Create a file to save in the mountpoint
   376  	f, err := os.Create(path.Join(mntPoint, "test.txt"))
   377  	if err != nil {
   378  		t.Fatal(err)
   379  	}
   380  
   381  	if _, err := f.WriteString("testline"); err != nil {
   382  		t.Fatal(err)
   383  	}
   384  	if err := f.Close(); err != nil {
   385  		t.Fatal(err)
   386  	}
   387  
   388  	changes, err := d.Changes("2", "")
   389  	if err != nil {
   390  		t.Fatal(err)
   391  	}
   392  	if len(changes) != 1 {
   393  		t.Fatalf("Dir 2 should have one change from parent got %d", len(changes))
   394  	}
   395  	change := changes[0]
   396  
   397  	expectedPath := "/test.txt"
   398  	if change.Path != expectedPath {
   399  		t.Fatalf("Expected path %s got %s", expectedPath, change.Path)
   400  	}
   401  
   402  	if change.Kind != archive.ChangeAdd {
   403  		t.Fatalf("Change kind should be ChangeAdd got %s", change.Kind)
   404  	}
   405  
   406  	if err := d.Create("3", "2", ""); err != nil {
   407  		t.Fatal(err)
   408  	}
   409  	mntPoint, err = d.Get("3", "")
   410  	if err != nil {
   411  		t.Fatal(err)
   412  	}
   413  
   414  	// Create a file to save in the mountpoint
   415  	f, err = os.Create(path.Join(mntPoint, "test2.txt"))
   416  	if err != nil {
   417  		t.Fatal(err)
   418  	}
   419  
   420  	if _, err := f.WriteString("testline"); err != nil {
   421  		t.Fatal(err)
   422  	}
   423  	if err := f.Close(); err != nil {
   424  		t.Fatal(err)
   425  	}
   426  
   427  	changes, err = d.Changes("3", "")
   428  	if err != nil {
   429  		t.Fatal(err)
   430  	}
   431  
   432  	if len(changes) != 1 {
   433  		t.Fatalf("Dir 2 should have one change from parent got %d", len(changes))
   434  	}
   435  	change = changes[0]
   436  
   437  	expectedPath = "/test2.txt"
   438  	if change.Path != expectedPath {
   439  		t.Fatalf("Expected path %s got %s", expectedPath, change.Path)
   440  	}
   441  
   442  	if change.Kind != archive.ChangeAdd {
   443  		t.Fatalf("Change kind should be ChangeAdd got %s", change.Kind)
   444  	}
   445  }
   446  
   447  func TestDiffSize(t *testing.T) {
   448  	d := newDriver(t)
   449  	defer os.RemoveAll(tmp)
   450  
   451  	if err := d.Create("1", "", ""); err != nil {
   452  		t.Fatal(err)
   453  	}
   454  
   455  	diffPath, err := d.Get("1", "")
   456  	if err != nil {
   457  		t.Fatal(err)
   458  	}
   459  
   460  	// Add a file to the diff path with a fixed size
   461  	size := int64(1024)
   462  
   463  	f, err := os.Create(path.Join(diffPath, "test_file"))
   464  	if err != nil {
   465  		t.Fatal(err)
   466  	}
   467  	if err := f.Truncate(size); err != nil {
   468  		t.Fatal(err)
   469  	}
   470  	s, err := f.Stat()
   471  	if err != nil {
   472  		t.Fatal(err)
   473  	}
   474  	size = s.Size()
   475  	if err := f.Close(); err != nil {
   476  		t.Fatal(err)
   477  	}
   478  
   479  	diffSize, err := d.DiffSize("1", "")
   480  	if err != nil {
   481  		t.Fatal(err)
   482  	}
   483  	if diffSize != size {
   484  		t.Fatalf("Expected size to be %d got %d", size, diffSize)
   485  	}
   486  }
   487  
   488  func TestChildDiffSize(t *testing.T) {
   489  	d := newDriver(t)
   490  	defer os.RemoveAll(tmp)
   491  	defer d.Cleanup()
   492  
   493  	if err := d.Create("1", "", ""); err != nil {
   494  		t.Fatal(err)
   495  	}
   496  
   497  	diffPath, err := d.Get("1", "")
   498  	if err != nil {
   499  		t.Fatal(err)
   500  	}
   501  
   502  	// Add a file to the diff path with a fixed size
   503  	size := int64(1024)
   504  
   505  	f, err := os.Create(path.Join(diffPath, "test_file"))
   506  	if err != nil {
   507  		t.Fatal(err)
   508  	}
   509  	if err := f.Truncate(size); err != nil {
   510  		t.Fatal(err)
   511  	}
   512  	s, err := f.Stat()
   513  	if err != nil {
   514  		t.Fatal(err)
   515  	}
   516  	size = s.Size()
   517  	if err := f.Close(); err != nil {
   518  		t.Fatal(err)
   519  	}
   520  
   521  	diffSize, err := d.DiffSize("1", "")
   522  	if err != nil {
   523  		t.Fatal(err)
   524  	}
   525  	if diffSize != size {
   526  		t.Fatalf("Expected size to be %d got %d", size, diffSize)
   527  	}
   528  
   529  	if err := d.Create("2", "1", ""); err != nil {
   530  		t.Fatal(err)
   531  	}
   532  
   533  	diffSize, err = d.DiffSize("2", "")
   534  	if err != nil {
   535  		t.Fatal(err)
   536  	}
   537  	// The diff size for the child should be zero
   538  	if diffSize != 0 {
   539  		t.Fatalf("Expected size to be %d got %d", 0, diffSize)
   540  	}
   541  }
   542  
   543  func TestExists(t *testing.T) {
   544  	d := newDriver(t)
   545  	defer os.RemoveAll(tmp)
   546  	defer d.Cleanup()
   547  
   548  	if err := d.Create("1", "", ""); err != nil {
   549  		t.Fatal(err)
   550  	}
   551  
   552  	if d.Exists("none") {
   553  		t.Fatal("id name should not exist in the driver")
   554  	}
   555  
   556  	if !d.Exists("1") {
   557  		t.Fatal("id 1 should exist in the driver")
   558  	}
   559  }
   560  
   561  func TestStatus(t *testing.T) {
   562  	d := newDriver(t)
   563  	defer os.RemoveAll(tmp)
   564  	defer d.Cleanup()
   565  
   566  	if err := d.Create("1", "", ""); err != nil {
   567  		t.Fatal(err)
   568  	}
   569  
   570  	status := d.Status()
   571  	if status == nil || len(status) == 0 {
   572  		t.Fatal("Status should not be nil or empty")
   573  	}
   574  	rootDir := status[0]
   575  	dirs := status[2]
   576  	if rootDir[0] != "Root Dir" {
   577  		t.Fatalf("Expected Root Dir got %s", rootDir[0])
   578  	}
   579  	if rootDir[1] != d.rootPath() {
   580  		t.Fatalf("Expected %s got %s", d.rootPath(), rootDir[1])
   581  	}
   582  	if dirs[0] != "Dirs" {
   583  		t.Fatalf("Expected Dirs got %s", dirs[0])
   584  	}
   585  	if dirs[1] != "1" {
   586  		t.Fatalf("Expected 1 got %s", dirs[1])
   587  	}
   588  }
   589  
   590  func TestApplyDiff(t *testing.T) {
   591  	d := newDriver(t)
   592  	defer os.RemoveAll(tmp)
   593  	defer d.Cleanup()
   594  
   595  	if err := d.Create("1", "", ""); err != nil {
   596  		t.Fatal(err)
   597  	}
   598  
   599  	diffPath, err := d.Get("1", "")
   600  	if err != nil {
   601  		t.Fatal(err)
   602  	}
   603  
   604  	// Add a file to the diff path with a fixed size
   605  	size := int64(1024)
   606  
   607  	f, err := os.Create(path.Join(diffPath, "test_file"))
   608  	if err != nil {
   609  		t.Fatal(err)
   610  	}
   611  	if err := f.Truncate(size); err != nil {
   612  		t.Fatal(err)
   613  	}
   614  	f.Close()
   615  
   616  	diff, err := d.Diff("1", "")
   617  	if err != nil {
   618  		t.Fatal(err)
   619  	}
   620  
   621  	if err := d.Create("2", "", ""); err != nil {
   622  		t.Fatal(err)
   623  	}
   624  	if err := d.Create("3", "2", ""); err != nil {
   625  		t.Fatal(err)
   626  	}
   627  
   628  	if err := d.applyDiff("3", diff); err != nil {
   629  		t.Fatal(err)
   630  	}
   631  
   632  	// Ensure that the file is in the mount point for id 3
   633  
   634  	mountPoint, err := d.Get("3", "")
   635  	if err != nil {
   636  		t.Fatal(err)
   637  	}
   638  	if _, err := os.Stat(path.Join(mountPoint, "test_file")); err != nil {
   639  		t.Fatal(err)
   640  	}
   641  }
   642  
   643  func hash(c string) string {
   644  	h := sha256.New()
   645  	fmt.Fprint(h, c)
   646  	return hex.EncodeToString(h.Sum(nil))
   647  }
   648  
   649  func testMountMoreThan42Layers(t *testing.T, mountPath string) {
   650  	if err := os.MkdirAll(mountPath, 0755); err != nil {
   651  		t.Fatal(err)
   652  	}
   653  
   654  	defer os.RemoveAll(mountPath)
   655  	d := testInit(mountPath, t).(*Driver)
   656  	defer d.Cleanup()
   657  	var last string
   658  	var expected int
   659  
   660  	for i := 1; i < 127; i++ {
   661  		expected++
   662  		var (
   663  			parent  = fmt.Sprintf("%d", i-1)
   664  			current = fmt.Sprintf("%d", i)
   665  		)
   666  
   667  		if parent == "0" {
   668  			parent = ""
   669  		} else {
   670  			parent = hash(parent)
   671  		}
   672  		current = hash(current)
   673  
   674  		if err := d.Create(current, parent, ""); err != nil {
   675  			t.Logf("Current layer %d", i)
   676  			t.Error(err)
   677  		}
   678  		point, err := d.Get(current, "")
   679  		if err != nil {
   680  			t.Logf("Current layer %d", i)
   681  			t.Error(err)
   682  		}
   683  		f, err := os.Create(path.Join(point, current))
   684  		if err != nil {
   685  			t.Logf("Current layer %d", i)
   686  			t.Error(err)
   687  		}
   688  		f.Close()
   689  
   690  		if i%10 == 0 {
   691  			if err := os.Remove(path.Join(point, parent)); err != nil {
   692  				t.Logf("Current layer %d", i)
   693  				t.Error(err)
   694  			}
   695  			expected--
   696  		}
   697  		last = current
   698  	}
   699  
   700  	// Perform the actual mount for the top most image
   701  	point, err := d.Get(last, "")
   702  	if err != nil {
   703  		t.Error(err)
   704  	}
   705  	files, err := ioutil.ReadDir(point)
   706  	if err != nil {
   707  		t.Error(err)
   708  	}
   709  	if len(files) != expected {
   710  		t.Errorf("Expected %d got %d", expected, len(files))
   711  	}
   712  }
   713  
   714  func TestMountMoreThan42Layers(t *testing.T) {
   715  	os.RemoveAll(tmpOuter)
   716  	testMountMoreThan42Layers(t, tmp)
   717  }
   718  
   719  func TestMountMoreThan42LayersMatchingPathLength(t *testing.T) {
   720  	defer os.RemoveAll(tmpOuter)
   721  	zeroes := "0"
   722  	for {
   723  		// This finds a mount path so that when combined into aufs mount options
   724  		// 4096 byte boundary would be in between the paths or in permission
   725  		// section. For '/tmp' it will use '/tmp/aufs-tests/00000000/aufs'
   726  		mountPath := path.Join(tmpOuter, zeroes, "aufs")
   727  		pathLength := 77 + len(mountPath)
   728  
   729  		if mod := 4095 % pathLength; mod == 0 || mod > pathLength-2 {
   730  			t.Logf("Using path: %s", mountPath)
   731  			testMountMoreThan42Layers(t, mountPath)
   732  			return
   733  		}
   734  		zeroes += "0"
   735  	}
   736  }
   737  
   738  func BenchmarkConcurrentAccess(b *testing.B) {
   739  	b.StopTimer()
   740  	b.ResetTimer()
   741  
   742  	d := newDriver(b)
   743  	defer os.RemoveAll(tmp)
   744  	defer d.Cleanup()
   745  
   746  	numConcurent := 256
   747  	// create a bunch of ids
   748  	var ids []string
   749  	for i := 0; i < numConcurent; i++ {
   750  		ids = append(ids, stringid.GenerateNonCryptoID())
   751  	}
   752  
   753  	if err := d.Create(ids[0], "", ""); err != nil {
   754  		b.Fatal(err)
   755  	}
   756  
   757  	if err := d.Create(ids[1], ids[0], ""); err != nil {
   758  		b.Fatal(err)
   759  	}
   760  
   761  	parent := ids[1]
   762  	ids = append(ids[2:])
   763  
   764  	chErr := make(chan error, numConcurent)
   765  	var outerGroup sync.WaitGroup
   766  	outerGroup.Add(len(ids))
   767  	b.StartTimer()
   768  
   769  	// here's the actual bench
   770  	for _, id := range ids {
   771  		go func(id string) {
   772  			defer outerGroup.Done()
   773  			if err := d.Create(id, parent, ""); err != nil {
   774  				b.Logf("Create %s failed", id)
   775  				chErr <- err
   776  				return
   777  			}
   778  			var innerGroup sync.WaitGroup
   779  			for i := 0; i < b.N; i++ {
   780  				innerGroup.Add(1)
   781  				go func() {
   782  					d.Get(id, "")
   783  					d.Put(id)
   784  					innerGroup.Done()
   785  				}()
   786  			}
   787  			innerGroup.Wait()
   788  			d.Remove(id)
   789  		}(id)
   790  	}
   791  
   792  	outerGroup.Wait()
   793  	b.StopTimer()
   794  	close(chErr)
   795  	for err := range chErr {
   796  		if err != nil {
   797  			b.Log(err)
   798  			b.Fail()
   799  		}
   800  	}
   801  }