mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
65d79e3e5e
Instead of implementing refcounts at each graphdriver, implement this in the layer package which is what the engine actually interacts with now. This means interacting directly with the graphdriver is no longer explicitly safe with regard to Get/Put calls being refcounted. In addition, with the containerd, layers may still be mounted after a daemon restart since we will no longer explicitly kill containers when we shutdown or startup engine. Because of this ref counts would need to be repopulated. Signed-off-by: Brian Goff <cpuguy83@gmail.com>
801 lines
15 KiB
Go
801 lines
15 KiB
Go
// +build linux
|
|
|
|
package aufs
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"path"
|
|
"sync"
|
|
"testing"
|
|
|
|
"github.com/docker/docker/daemon/graphdriver"
|
|
"github.com/docker/docker/pkg/archive"
|
|
"github.com/docker/docker/pkg/reexec"
|
|
"github.com/docker/docker/pkg/stringid"
|
|
)
|
|
|
|
var (
|
|
tmpOuter = path.Join(os.TempDir(), "aufs-tests")
|
|
tmp = path.Join(tmpOuter, "aufs")
|
|
)
|
|
|
|
func init() {
|
|
reexec.Init()
|
|
}
|
|
|
|
func testInit(dir string, t testing.TB) graphdriver.Driver {
|
|
d, err := Init(dir, nil, nil, nil)
|
|
if err != nil {
|
|
if err == graphdriver.ErrNotSupported {
|
|
t.Skip(err)
|
|
} else {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
return d
|
|
}
|
|
|
|
func newDriver(t testing.TB) *Driver {
|
|
if err := os.MkdirAll(tmp, 0755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
d := testInit(tmp, t)
|
|
return d.(*Driver)
|
|
}
|
|
|
|
func TestNewDriver(t *testing.T) {
|
|
if err := os.MkdirAll(tmp, 0755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
d := testInit(tmp, t)
|
|
defer os.RemoveAll(tmp)
|
|
if d == nil {
|
|
t.Fatalf("Driver should not be nil")
|
|
}
|
|
}
|
|
|
|
func TestAufsString(t *testing.T) {
|
|
d := newDriver(t)
|
|
defer os.RemoveAll(tmp)
|
|
|
|
if d.String() != "aufs" {
|
|
t.Fatalf("Expected aufs got %s", d.String())
|
|
}
|
|
}
|
|
|
|
func TestCreateDirStructure(t *testing.T) {
|
|
newDriver(t)
|
|
defer os.RemoveAll(tmp)
|
|
|
|
paths := []string{
|
|
"mnt",
|
|
"layers",
|
|
"diff",
|
|
}
|
|
|
|
for _, p := range paths {
|
|
if _, err := os.Stat(path.Join(tmp, p)); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// We should be able to create two drivers with the same dir structure
|
|
func TestNewDriverFromExistingDir(t *testing.T) {
|
|
if err := os.MkdirAll(tmp, 0755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
testInit(tmp, t)
|
|
testInit(tmp, t)
|
|
os.RemoveAll(tmp)
|
|
}
|
|
|
|
func TestCreateNewDir(t *testing.T) {
|
|
d := newDriver(t)
|
|
defer os.RemoveAll(tmp)
|
|
|
|
if err := d.Create("1", "", ""); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestCreateNewDirStructure(t *testing.T) {
|
|
d := newDriver(t)
|
|
defer os.RemoveAll(tmp)
|
|
|
|
if err := d.Create("1", "", ""); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
paths := []string{
|
|
"mnt",
|
|
"diff",
|
|
"layers",
|
|
}
|
|
|
|
for _, p := range paths {
|
|
if _, err := os.Stat(path.Join(tmp, p, "1")); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestRemoveImage(t *testing.T) {
|
|
d := newDriver(t)
|
|
defer os.RemoveAll(tmp)
|
|
|
|
if err := d.Create("1", "", ""); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if err := d.Remove("1"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
paths := []string{
|
|
"mnt",
|
|
"diff",
|
|
"layers",
|
|
}
|
|
|
|
for _, p := range paths {
|
|
if _, err := os.Stat(path.Join(tmp, p, "1")); err == nil {
|
|
t.Fatalf("Error should not be nil because dirs with id 1 should be delted: %s", p)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestGetWithoutParent(t *testing.T) {
|
|
d := newDriver(t)
|
|
defer os.RemoveAll(tmp)
|
|
|
|
if err := d.Create("1", "", ""); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
diffPath, err := d.Get("1", "")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
expected := path.Join(tmp, "diff", "1")
|
|
if diffPath != expected {
|
|
t.Fatalf("Expected path %s got %s", expected, diffPath)
|
|
}
|
|
}
|
|
|
|
func TestCleanupWithNoDirs(t *testing.T) {
|
|
d := newDriver(t)
|
|
defer os.RemoveAll(tmp)
|
|
|
|
if err := d.Cleanup(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestCleanupWithDir(t *testing.T) {
|
|
d := newDriver(t)
|
|
defer os.RemoveAll(tmp)
|
|
|
|
if err := d.Create("1", "", ""); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if err := d.Cleanup(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestMountedFalseResponse(t *testing.T) {
|
|
d := newDriver(t)
|
|
defer os.RemoveAll(tmp)
|
|
|
|
if err := d.Create("1", "", ""); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
response, err := d.mounted(d.getDiffPath("1"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if response != false {
|
|
t.Fatalf("Response if dir id 1 is mounted should be false")
|
|
}
|
|
}
|
|
|
|
func TestMountedTrueReponse(t *testing.T) {
|
|
d := newDriver(t)
|
|
defer os.RemoveAll(tmp)
|
|
defer d.Cleanup()
|
|
|
|
if err := d.Create("1", "", ""); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := d.Create("2", "1", ""); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
_, err := d.Get("2", "")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
response, err := d.mounted(d.pathCache["2"])
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if response != true {
|
|
t.Fatalf("Response if dir id 2 is mounted should be true")
|
|
}
|
|
}
|
|
|
|
func TestMountWithParent(t *testing.T) {
|
|
d := newDriver(t)
|
|
defer os.RemoveAll(tmp)
|
|
|
|
if err := d.Create("1", "", ""); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := d.Create("2", "1", ""); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
defer func() {
|
|
if err := d.Cleanup(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}()
|
|
|
|
mntPath, err := d.Get("2", "")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if mntPath == "" {
|
|
t.Fatal("mntPath should not be empty string")
|
|
}
|
|
|
|
expected := path.Join(tmp, "mnt", "2")
|
|
if mntPath != expected {
|
|
t.Fatalf("Expected %s got %s", expected, mntPath)
|
|
}
|
|
}
|
|
|
|
func TestRemoveMountedDir(t *testing.T) {
|
|
d := newDriver(t)
|
|
defer os.RemoveAll(tmp)
|
|
|
|
if err := d.Create("1", "", ""); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := d.Create("2", "1", ""); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
defer func() {
|
|
if err := d.Cleanup(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}()
|
|
|
|
mntPath, err := d.Get("2", "")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if mntPath == "" {
|
|
t.Fatal("mntPath should not be empty string")
|
|
}
|
|
|
|
mounted, err := d.mounted(d.pathCache["2"])
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if !mounted {
|
|
t.Fatalf("Dir id 2 should be mounted")
|
|
}
|
|
|
|
if err := d.Remove("2"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestCreateWithInvalidParent(t *testing.T) {
|
|
d := newDriver(t)
|
|
defer os.RemoveAll(tmp)
|
|
|
|
if err := d.Create("1", "docker", ""); err == nil {
|
|
t.Fatalf("Error should not be nil with parent does not exist")
|
|
}
|
|
}
|
|
|
|
func TestGetDiff(t *testing.T) {
|
|
d := newDriver(t)
|
|
defer os.RemoveAll(tmp)
|
|
|
|
if err := d.Create("1", "", ""); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
diffPath, err := d.Get("1", "")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Add a file to the diff path with a fixed size
|
|
size := int64(1024)
|
|
|
|
f, err := os.Create(path.Join(diffPath, "test_file"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := f.Truncate(size); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
f.Close()
|
|
|
|
a, err := d.Diff("1", "")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if a == nil {
|
|
t.Fatalf("Archive should not be nil")
|
|
}
|
|
}
|
|
|
|
func TestChanges(t *testing.T) {
|
|
d := newDriver(t)
|
|
defer os.RemoveAll(tmp)
|
|
|
|
if err := d.Create("1", "", ""); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := d.Create("2", "1", ""); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
defer func() {
|
|
if err := d.Cleanup(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}()
|
|
|
|
mntPoint, err := d.Get("2", "")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Create a file to save in the mountpoint
|
|
f, err := os.Create(path.Join(mntPoint, "test.txt"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if _, err := f.WriteString("testline"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := f.Close(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
changes, err := d.Changes("2", "")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(changes) != 1 {
|
|
t.Fatalf("Dir 2 should have one change from parent got %d", len(changes))
|
|
}
|
|
change := changes[0]
|
|
|
|
expectedPath := "/test.txt"
|
|
if change.Path != expectedPath {
|
|
t.Fatalf("Expected path %s got %s", expectedPath, change.Path)
|
|
}
|
|
|
|
if change.Kind != archive.ChangeAdd {
|
|
t.Fatalf("Change kind should be ChangeAdd got %s", change.Kind)
|
|
}
|
|
|
|
if err := d.Create("3", "2", ""); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
mntPoint, err = d.Get("3", "")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Create a file to save in the mountpoint
|
|
f, err = os.Create(path.Join(mntPoint, "test2.txt"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if _, err := f.WriteString("testline"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := f.Close(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
changes, err = d.Changes("3", "")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if len(changes) != 1 {
|
|
t.Fatalf("Dir 2 should have one change from parent got %d", len(changes))
|
|
}
|
|
change = changes[0]
|
|
|
|
expectedPath = "/test2.txt"
|
|
if change.Path != expectedPath {
|
|
t.Fatalf("Expected path %s got %s", expectedPath, change.Path)
|
|
}
|
|
|
|
if change.Kind != archive.ChangeAdd {
|
|
t.Fatalf("Change kind should be ChangeAdd got %s", change.Kind)
|
|
}
|
|
}
|
|
|
|
func TestDiffSize(t *testing.T) {
|
|
d := newDriver(t)
|
|
defer os.RemoveAll(tmp)
|
|
|
|
if err := d.Create("1", "", ""); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
diffPath, err := d.Get("1", "")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Add a file to the diff path with a fixed size
|
|
size := int64(1024)
|
|
|
|
f, err := os.Create(path.Join(diffPath, "test_file"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := f.Truncate(size); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
s, err := f.Stat()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
size = s.Size()
|
|
if err := f.Close(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
diffSize, err := d.DiffSize("1", "")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if diffSize != size {
|
|
t.Fatalf("Expected size to be %d got %d", size, diffSize)
|
|
}
|
|
}
|
|
|
|
func TestChildDiffSize(t *testing.T) {
|
|
d := newDriver(t)
|
|
defer os.RemoveAll(tmp)
|
|
defer d.Cleanup()
|
|
|
|
if err := d.Create("1", "", ""); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
diffPath, err := d.Get("1", "")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Add a file to the diff path with a fixed size
|
|
size := int64(1024)
|
|
|
|
f, err := os.Create(path.Join(diffPath, "test_file"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := f.Truncate(size); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
s, err := f.Stat()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
size = s.Size()
|
|
if err := f.Close(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
diffSize, err := d.DiffSize("1", "")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if diffSize != size {
|
|
t.Fatalf("Expected size to be %d got %d", size, diffSize)
|
|
}
|
|
|
|
if err := d.Create("2", "1", ""); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
diffSize, err = d.DiffSize("2", "")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// The diff size for the child should be zero
|
|
if diffSize != 0 {
|
|
t.Fatalf("Expected size to be %d got %d", 0, diffSize)
|
|
}
|
|
}
|
|
|
|
func TestExists(t *testing.T) {
|
|
d := newDriver(t)
|
|
defer os.RemoveAll(tmp)
|
|
defer d.Cleanup()
|
|
|
|
if err := d.Create("1", "", ""); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if d.Exists("none") {
|
|
t.Fatal("id name should not exist in the driver")
|
|
}
|
|
|
|
if !d.Exists("1") {
|
|
t.Fatal("id 1 should exist in the driver")
|
|
}
|
|
}
|
|
|
|
func TestStatus(t *testing.T) {
|
|
d := newDriver(t)
|
|
defer os.RemoveAll(tmp)
|
|
defer d.Cleanup()
|
|
|
|
if err := d.Create("1", "", ""); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
status := d.Status()
|
|
if status == nil || len(status) == 0 {
|
|
t.Fatal("Status should not be nil or empty")
|
|
}
|
|
rootDir := status[0]
|
|
dirs := status[2]
|
|
if rootDir[0] != "Root Dir" {
|
|
t.Fatalf("Expected Root Dir got %s", rootDir[0])
|
|
}
|
|
if rootDir[1] != d.rootPath() {
|
|
t.Fatalf("Expected %s got %s", d.rootPath(), rootDir[1])
|
|
}
|
|
if dirs[0] != "Dirs" {
|
|
t.Fatalf("Expected Dirs got %s", dirs[0])
|
|
}
|
|
if dirs[1] != "1" {
|
|
t.Fatalf("Expected 1 got %s", dirs[1])
|
|
}
|
|
}
|
|
|
|
func TestApplyDiff(t *testing.T) {
|
|
d := newDriver(t)
|
|
defer os.RemoveAll(tmp)
|
|
defer d.Cleanup()
|
|
|
|
if err := d.Create("1", "", ""); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
diffPath, err := d.Get("1", "")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Add a file to the diff path with a fixed size
|
|
size := int64(1024)
|
|
|
|
f, err := os.Create(path.Join(diffPath, "test_file"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := f.Truncate(size); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
f.Close()
|
|
|
|
diff, err := d.Diff("1", "")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if err := d.Create("2", "", ""); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := d.Create("3", "2", ""); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if err := d.applyDiff("3", diff); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Ensure that the file is in the mount point for id 3
|
|
|
|
mountPoint, err := d.Get("3", "")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := os.Stat(path.Join(mountPoint, "test_file")); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func hash(c string) string {
|
|
h := sha256.New()
|
|
fmt.Fprint(h, c)
|
|
return hex.EncodeToString(h.Sum(nil))
|
|
}
|
|
|
|
func testMountMoreThan42Layers(t *testing.T, mountPath string) {
|
|
if err := os.MkdirAll(mountPath, 0755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
defer os.RemoveAll(mountPath)
|
|
d := testInit(mountPath, t).(*Driver)
|
|
defer d.Cleanup()
|
|
var last string
|
|
var expected int
|
|
|
|
for i := 1; i < 127; i++ {
|
|
expected++
|
|
var (
|
|
parent = fmt.Sprintf("%d", i-1)
|
|
current = fmt.Sprintf("%d", i)
|
|
)
|
|
|
|
if parent == "0" {
|
|
parent = ""
|
|
} else {
|
|
parent = hash(parent)
|
|
}
|
|
current = hash(current)
|
|
|
|
if err := d.Create(current, parent, ""); err != nil {
|
|
t.Logf("Current layer %d", i)
|
|
t.Error(err)
|
|
}
|
|
point, err := d.Get(current, "")
|
|
if err != nil {
|
|
t.Logf("Current layer %d", i)
|
|
t.Error(err)
|
|
}
|
|
f, err := os.Create(path.Join(point, current))
|
|
if err != nil {
|
|
t.Logf("Current layer %d", i)
|
|
t.Error(err)
|
|
}
|
|
f.Close()
|
|
|
|
if i%10 == 0 {
|
|
if err := os.Remove(path.Join(point, parent)); err != nil {
|
|
t.Logf("Current layer %d", i)
|
|
t.Error(err)
|
|
}
|
|
expected--
|
|
}
|
|
last = current
|
|
}
|
|
|
|
// Perform the actual mount for the top most image
|
|
point, err := d.Get(last, "")
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
files, err := ioutil.ReadDir(point)
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
if len(files) != expected {
|
|
t.Errorf("Expected %d got %d", expected, len(files))
|
|
}
|
|
}
|
|
|
|
func TestMountMoreThan42Layers(t *testing.T) {
|
|
os.RemoveAll(tmpOuter)
|
|
testMountMoreThan42Layers(t, tmp)
|
|
}
|
|
|
|
func TestMountMoreThan42LayersMatchingPathLength(t *testing.T) {
|
|
defer os.RemoveAll(tmpOuter)
|
|
zeroes := "0"
|
|
for {
|
|
// This finds a mount path so that when combined into aufs mount options
|
|
// 4096 byte boundary would be in between the paths or in permission
|
|
// section. For '/tmp' it will use '/tmp/aufs-tests/00000000/aufs'
|
|
mountPath := path.Join(tmpOuter, zeroes, "aufs")
|
|
pathLength := 77 + len(mountPath)
|
|
|
|
if mod := 4095 % pathLength; mod == 0 || mod > pathLength-2 {
|
|
t.Logf("Using path: %s", mountPath)
|
|
testMountMoreThan42Layers(t, mountPath)
|
|
return
|
|
}
|
|
zeroes += "0"
|
|
}
|
|
}
|
|
|
|
func BenchmarkConcurrentAccess(b *testing.B) {
|
|
b.StopTimer()
|
|
b.ResetTimer()
|
|
|
|
d := newDriver(b)
|
|
defer os.RemoveAll(tmp)
|
|
defer d.Cleanup()
|
|
|
|
numConcurent := 256
|
|
// create a bunch of ids
|
|
var ids []string
|
|
for i := 0; i < numConcurent; i++ {
|
|
ids = append(ids, stringid.GenerateNonCryptoID())
|
|
}
|
|
|
|
if err := d.Create(ids[0], "", ""); err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
|
|
if err := d.Create(ids[1], ids[0], ""); err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
|
|
parent := ids[1]
|
|
ids = append(ids[2:])
|
|
|
|
chErr := make(chan error, numConcurent)
|
|
var outerGroup sync.WaitGroup
|
|
outerGroup.Add(len(ids))
|
|
b.StartTimer()
|
|
|
|
// here's the actual bench
|
|
for _, id := range ids {
|
|
go func(id string) {
|
|
defer outerGroup.Done()
|
|
if err := d.Create(id, parent, ""); err != nil {
|
|
b.Logf("Create %s failed", id)
|
|
chErr <- err
|
|
return
|
|
}
|
|
var innerGroup sync.WaitGroup
|
|
for i := 0; i < b.N; i++ {
|
|
innerGroup.Add(1)
|
|
go func() {
|
|
d.Get(id, "")
|
|
d.Put(id)
|
|
innerGroup.Done()
|
|
}()
|
|
}
|
|
innerGroup.Wait()
|
|
d.Remove(id)
|
|
}(id)
|
|
}
|
|
|
|
outerGroup.Wait()
|
|
b.StopTimer()
|
|
close(chErr)
|
|
for err := range chErr {
|
|
if err != nil {
|
|
b.Log(err)
|
|
b.Fail()
|
|
}
|
|
}
|
|
}
|