Esempio n. 1
0
func TestStoreBasics(t *testing.T, s store.Store) {
	t.Logf("TestStoreBasics()")

	ShouldFullList(t, s, nil)
	ShouldGetMiss(t, s, "hello")
	ShouldCAS(t, s, "hello", store.MissingV, store.MissingV)
	ShouldGetMiss(t, s, "hello")
	ShouldCAS(t, s, "hello", store.MissingV, store.DataV([]byte("world")))
	ShouldGet(t, s, "hello", []byte("world"))
	ShouldStatNoTime(t, s, "hello", store.Stat{SHA256: sha256.Sum256([]byte("world")), Size: 5})
	ShouldCAS(t, s, "a", store.AnyV, store.MissingV)
	ShouldFullList(t, s, []string{"hello"})
	ShouldCAS(t, s, "b", store.AnyV, store.DataV([]byte("beta")))
	ShouldCAS(t, s, "b", store.AnyV, store.DataV([]byte("other")))
	ShouldGet(t, s, "b", []byte("other"))
	ShouldFullList(t, s, []string{"b", "hello"})
	ShouldCASFail(t, s, "b", store.MissingV, store.MissingV)
	ShouldCAS(t, s, "hello", store.AnyV, store.MissingV)
	ShouldFullList(t, s, []string{"b"})
	ShouldCAS(t, s, "empty", store.AnyV, store.DataV(nil))
	ShouldGet(t, s, "empty", nil)
	ShouldCAS(t, s, "empty", store.DataV(nil), store.DataV([]byte("one")))
	ShouldCAS(t, s, "empty", store.DataV([]byte("one")), store.DataV([]byte("two")))
	ShouldGet(t, s, "empty", []byte("two"))

	ShouldCAS(t, s, "b", store.AnyV, store.MissingV)
	ShouldCAS(t, s, "empty", store.AnyV, store.MissingV)
	ShouldFullList(t, s, nil)

	ShouldFreeSpace(t, s)
}
Esempio n. 2
0
func TestMultiScrubRemovesUnreferencedChunks(t *testing.T) {
	killers, multi, mocks, done := prepareMultiTest(t, 1, 2, 2)
	defer done()

	storetests.ShouldCAS(t, multi, "a", store.MissingV, store.DataV([]byte("data")))
	killers[0].setKilled(true)
	storetests.ShouldCAS(t, multi, "a", store.DataV([]byte("data")), store.MissingV)
	killers[0].setKilled(false)
	storetests.ShouldListCount(t, multi, 0)
	storetests.ShouldListCount(t, mocks[0], 1)
	multi.finder.Rescan()
	multi.scrubAll()
	storetests.ShouldListCount(t, mocks[0], 0)
}
Esempio n. 3
0
func TestMultiScrubChangeRedundancy(t *testing.T) {
	killers, multi, _, done := prepareMultiTest(t, 2, 3, 5)
	defer done()

	data := []byte("who knows where the wind goes")

	for i := 0; i < 10; i++ {
		storetests.ShouldCAS(t, multi,
			strconv.FormatInt(int64(i), 10),
			store.MissingV, store.DataV(data))
	}

	err := multi.SetRedundancy(2, 5)
	if err != nil {
		t.Fatalf("Couldn't adjust redundancy: %v", err)
	}

	multi.scrubAll()

	killers[0].setKilled(true)
	killers[1].setKilled(true)
	killers[2].setKilled(true)

	for i := 0; i < 10; i++ {
		storetests.ShouldGet(t, multi,
			strconv.FormatInt(int64(i), 10),
			data)
	}
}
Esempio n. 4
0
func TestMultiScrubRecreatesMissing(t *testing.T) {
	_, multi, mocks, done := prepareMultiTest(t, 2, 3, 3)
	defer done()

	data := []byte("hello world! this is some test data.")

	storetests.ShouldCAS(t, multi, "key", store.MissingV, store.DataV(data))

	names, err := mocks[0].List("", 1, nil)
	if err != nil {
		t.Fatalf("Couldn't list first mock: %v", err)
	}
	if len(names) != 1 {
		t.Fatalf("Didn't get a name from mock")
	}

	storetests.ShouldCAS(t, mocks[0], names[0], store.AnyV, store.MissingV)

	multi.scrubAll()

	found := 0
	for _, mock := range mocks {
		names, err := mock.List("", 1, nil)
		if err != nil {
			t.Fatalf("Couldn't list mock: %v", err)
		}
		if len(names) == 1 {
			found++
		}
	}

	if found != 3 {
		t.Fatalf("scrubAll didn't recreate missing chunk")
	}
}
Esempio n. 5
0
func TestMultiHungStoreDoesntBlock(t *testing.T) {
	killers, multi, _, done := prepareMultiTest(t, 2, 4, 4)
	defer done()

	oldTimeout := dataOnlyTimeout
	dataOnlyTimeout = 10 * time.Millisecond
	defer func() { dataOnlyTimeout = oldTimeout }()

	storetests.ShouldCAS(t, multi, "a", store.MissingV, store.DataV([]byte("data")))

	for _, k := range killers {
		k.setBlocked(true)

		result := make(chan error)
		go func() {
			_, _, err := multi.Get("a", store.GetOptions{})
			result <- err
		}()

		select {
		case err := <-result:
			if err != nil {
				t.Errorf("couldn't get key: %v", err)
			}
		case <-time.After(100 * time.Millisecond):
			t.Errorf("timed out waiting for read")
		}

		k.setBlocked(false)
	}
}
Esempio n. 6
0
func TestStoreRangeRead(t *testing.T, s store.RangeReadStore) {
	t.Logf("TestStoreRangeRead()")

	data := make([]byte, 1024)
	for i := range data {
		data[i] = byte(rand.Int31())
	}

	ShouldFullList(t, s, nil)
	ShouldCAS(t, s, "key", store.AnyV, store.DataV(data))
	ShouldGet(t, s, "key", data)
	ShouldGetPartial(t, s, "key", 0, int64(len(data)), data)
	ShouldGetPartial(t, s, "key", 1, int64(len(data)), data[1:])
	ShouldGetPartial(t, s, "key", 0, -1, data)
	ShouldGetPartial(t, s, "key", 1, -1, data[1:])
	ShouldGetPartial(t, s, "key", 128, -1, data[128:])
	ShouldGetPartial(t, s, "key", 128, 128, data[128:256])
	ShouldGetPartial(t, s, "key", 555, 1, data[555:556])
	ShouldGetPartial(t, s, "key", 1020, -1, data[1020:])
	ShouldGetPartial(t, s, "key", 1023, -1, data[1023:])
	ShouldGetPartial(t, s, "key", 1024, -1, nil)
	ShouldGetPartial(t, s, "key", 1023, 1, data[1023:])
	ShouldGetPartial(t, s, "key", 1024, 1, nil)
	ShouldGetPartial(t, s, "key", 1023, 0, nil)
	ShouldGetPartial(t, s, "key", 1024, 0, nil)
	ShouldGetPartial(t, s, "key", 5555, -1, nil)
	ShouldGetPartial(t, s, "key", 1000, 60, data[1000:])
	ShouldCAS(t, s, "key", store.AnyV, store.MissingV)
}
Esempio n. 7
0
func TestCacheGCWorks(t *testing.T) {
	inner := &CountingStore{MockStore: storetests.NewMockStore(0)}
	cache := New(1024, inner)

	for i := 0; i < 1024; i++ {
		key := strconv.FormatInt(int64(i), 10)
		storetests.ShouldCAS(t, cache, key, store.AnyV, store.DataV([]byte(key)))
	}

	cache.assertUsedIsCorrect()
	t.Logf("cache.used after writes is %v", cache.used)

	for i := 0; i < 1024; i++ {
		key := strconv.FormatInt(int64(i), 10)
		storetests.ShouldGet(t, cache, key, []byte(key))
	}

	cache.assertUsedIsCorrect()
	t.Logf("cache.used after first reads is %v", cache.used)
	getsBefore := inner.gets

	for i := 0; i < 1024; i++ {
		key := strconv.FormatInt(int64(i), 10)
		storetests.ShouldGet(t, cache, key, []byte(key))
	}

	cache.assertUsedIsCorrect()
	t.Logf("cache.used after second reads is %v", cache.used)
	getsAfter := inner.gets

	if getsAfter != getsBefore+1024 {
		t.Errorf("wanted %v gets after, got %v", getsBefore+1024, getsAfter)
	}
}
Esempio n. 8
0
func TestMultiCanReplaceDeadKeys(t *testing.T) {
	killers, multi, _, done := prepareMultiTest(t, 3, 4, 4)
	defer done()

	storetests.ShouldCAS(t, multi, "a", store.MissingV, store.DataV([]byte("hello")))
	killers[0].setKilled(true)

	err := multi.SetRedundancy(1, 2)
	if err != nil {
		t.Fatalf("Couldn't adjust redundancy: %v", err)
	}

	storetests.ShouldCAS(t, multi, "a", store.DataV([]byte("hello")), store.DataV([]byte("there")))
	killers[1].setKilled(true)
	storetests.ShouldCAS(t, multi, "a", store.DataV([]byte("there")), store.MissingV)
}
Esempio n. 9
0
func TestMultiDuplicateContent(t *testing.T) {
	_, multi, _, done := prepareMultiTest(t, 1, 1, 1)
	defer done()

	data := []byte("this is some test data")

	for i := 0; i < 10; i++ {
		storetests.ShouldCAS(t, multi,
			strconv.FormatInt(int64(i), 10),
			store.MissingV, store.DataV(data))
	}

	for i := 0; i < 10; i++ {
		storetests.ShouldGet(t, multi,
			strconv.FormatInt(int64(i), 10),
			data)
	}

	for i := 0; i < 5; i++ {
		storetests.ShouldCAS(t, multi,
			strconv.FormatInt(int64(i), 10),
			store.AnyV, store.MissingV)
	}

	for i := 5; i < 10; i++ {
		storetests.ShouldGet(t, multi,
			strconv.FormatInt(int64(i), 10),
			data)
	}
}
Esempio n. 10
0
func TestMultiScrubRemovesWeirdChunks(t *testing.T) {
	_, multi, mocks, done := prepareMultiTest(t, 1, 1, 1)
	defer done()

	storetests.ShouldCAS(t, mocks[0], "a", store.MissingV, store.DataV([]byte("data")))
	multi.scrubAll()
	storetests.ShouldGetMiss(t, mocks[0], "a")
}
Esempio n. 11
0
func TestDirectoryCorruption(t *testing.T) {
	ds, tmpDir := makeTestingDirectory(t)
	defer os.RemoveAll(tmpDir)
	defer ds.Close()

	storetests.ShouldCAS(t, ds, "hello", store.AnyV, store.DataV([]byte("world")))
	shouldHashcheck(t, ds, 1, 0)
	shouldCorrupt(t, filepath.Join(tmpDir, "data", "1", "aGVsbG8="))
	storetests.ShouldGetError(t, ds, "hello", ErrCorruptObject)
	storetests.ShouldGetMiss(t, ds, "hello")
	shouldFileExist(t, filepath.Join(tmpDir, "quarantine", "aGVsbG8="))

	storetests.ShouldCAS(t, ds, "other", store.AnyV, store.DataV([]byte("werld")))
	shouldHashcheck(t, ds, 1, 0)
	shouldCorrupt(t, filepath.Join(tmpDir, "data", "1", "b3RoZXI="))
	shouldHashcheck(t, ds, 0, 1)
	storetests.ShouldGetMiss(t, ds, "other")
	shouldHashcheck(t, ds, 0, 0)
	shouldFileExist(t, filepath.Join(tmpDir, "quarantine", "b3RoZXI="))
}
Esempio n. 12
0
func TestCacheUpdatesOnUnknownChanges(t *testing.T) {
	inner := &CountingStore{MockStore: storetests.NewMockStore(0)}
	cache := New(1024, inner)

	storetests.ShouldCAS(t, cache, "asdf", store.AnyV, store.DataV([]byte("hello")))
	cache.Clear()
	storetests.ShouldGet(t, cache, "asdf", []byte("hello"))
	storetests.ShouldCAS(t, inner, "asdf", store.AnyV, store.DataV([]byte("world")))
	storetests.ShouldGet(t, cache, "asdf", []byte("world"))

	if inner.gets != 2 {
		t.Errorf("wanted 2 inner gets, got %v", inner.gets)
	}

	if inner.stats != 1 {
		t.Errorf("wanted 1 inner Stats, got %v", inner.stats)
	}

	cache.assertUsedIsCorrect()
}
Esempio n. 13
0
func TestCacheUpdatesOnStats(t *testing.T) {
	inner := &CountingStore{MockStore: storetests.NewMockStore(0)}
	cache := New(1024, inner)

	storetests.ShouldCAS(t, cache, "asdf", store.AnyV, store.DataV([]byte("hello")))
	storetests.ShouldGet(t, cache, "asdf", []byte("hello"))
	newData := store.DataV([]byte("world"))
	storetests.ShouldCAS(t, inner, "asdf", store.AnyV, newData)
	storetests.ShouldStatNoTime(t, cache, "asdf", store.Stat{Size: 5, SHA256: newData.SHA256})
	storetests.ShouldGet(t, cache, "asdf", []byte("world"))

	if inner.gets != 1 {
		t.Errorf("wanted 1 inner gets, got %v", inner.gets)
	}

	if inner.stats != 2 {
		t.Errorf("wanted 2 inner Stats, got %v", inner.stats)
	}

	cache.assertUsedIsCorrect()
}
Esempio n. 14
0
func TestHTTPRange(t *testing.T) {
	mock := storetests.NewMockStore(0)

	srv := NewServer(mock)

	storetests.ShouldCAS(t, mock, "key", store.AnyV, store.DataV([]byte("hello world!")))

	tests := []struct {
		RangeRequest   string
		Body           string
		RangeResponse  string
		NotSatisfiable bool
	}{
		{"bytes=5-", " world!", "bytes 5-11/12", false},
		{"bytes=5-12", " world!", "bytes 5-11/12", false},
		{"bytes=11-", "!", "bytes 11-11/12", false},
		{"bytes=0-", "hello world!", "bytes 0-11/12", false},
		{"bytes=1-1", "e", "bytes 1-1/12", false},
		{"bytes=4-4", "o", "bytes 4-4/12", false},
		{"bytes=12-", "", "", true},
		{"bytes=12341234-44", "", "", true},
	}

	for _, test := range tests {
		w := httptest.NewRecorder()
		r, err := http.NewRequest("GET", "/key", nil)
		if err != nil {
			t.Fatal(err)
		}
		r.Header.Set("Range", test.RangeRequest)
		srv.ServeHTTP(w, r)

		if test.NotSatisfiable {
			if w.Code != http.StatusRequestedRangeNotSatisfiable {
				t.Errorf("Wanted code %#v in response to %#v, got %#v",
					http.StatusRequestedRangeNotSatisfiable, test.RangeRequest,
					w.Code)
			}
		} else {
			if w.Body.String() != test.Body {
				t.Errorf("Wanted %#v in response to %#v, got %#v",
					test.Body, test.RangeRequest, w.Body.String())
			}
			if w.HeaderMap.Get("Content-Range") != test.RangeResponse {
				t.Errorf(`Wanted Content-Range %#v in response to %#v, got %#v`,
					test.RangeResponse, test.RangeRequest,
					w.HeaderMap.Get("Content-Range"))
			}
		}
	}
}
Esempio n. 15
0
func TestResplit(t *testing.T) {
	ds, tmpDir := makeTestingDirectory(t)
	defer os.RemoveAll(tmpDir)
	defer ds.Close()

	ds.mu.Lock()
	ds.minSplitSize = 2
	ds.maxSplitSize = 8
	ds.mu.Unlock()

	const itemCount = 50

	for i := 0; i < itemCount; i++ {
		key := strconv.FormatInt(int64(i), 3)
		storetests.ShouldCAS(t, ds, key, store.AnyV, store.DataV([]byte(key)))

		ds.resplit()

		for j := 0; j < itemCount; j++ {
			key := strconv.FormatInt(int64(j), 3)
			if j <= i {
				storetests.ShouldGet(t, ds, key, []byte(key))
			} else {
				storetests.ShouldGetMiss(t, ds, key)
			}
		}

		checkSplitCountSizes(t, "insertion", tmpDir, 2, 8)
	}

	for i := 0; i < itemCount; i++ {
		key := strconv.FormatInt(int64(i), 3)
		storetests.ShouldCAS(t, ds, key, store.AnyV, store.MissingV)

		ds.resplit()

		for j := 0; j < itemCount; j++ {
			key := strconv.FormatInt(int64(j), 3)
			if j <= i {
				storetests.ShouldGetMiss(t, ds, key)
			} else {
				storetests.ShouldGet(t, ds, key, []byte(key))
			}
		}

		checkSplitCountSizes(t, "deletion", tmpDir, 2, 8)
	}
}
Esempio n. 16
0
func TestCacheDoesntCacheErrors(t *testing.T) {
	inner := &ErrorStore{MockStore: storetests.NewMockStore(0)}
	cache := New(1024, inner)

	storetests.ShouldCAS(t, cache, "asdf", store.AnyV, store.DataV([]byte("hello")))
	cache.Clear()

	inner.isErroring = true
	for i := 0; i < 10; i++ {
		storetests.ShouldGetError(t, cache, "asdf", ErrErrorStore)
	}

	cache.assertUsedIsCorrect()

	inner.isErroring = false
	storetests.ShouldGet(t, cache, "asdf", []byte("hello"))

	cache.assertUsedIsCorrect()
}
Esempio n. 17
0
func TestStoreWriteTime(t *testing.T, s store.Store) {
	t.Logf("TestStoreWriteTime()")

	ShouldCAS(t, s, "key", store.AnyV, store.DataV([]byte("one")))
	now := time.Now().Unix()

	st, err := s.Stat("key", nil)
	if err != nil {
		t.Fatalf("Couldn't stat key: %v", err)
	}
	diff := st.WriteTime - now
	if diff < 0 {
		diff = -diff
	}
	if diff > 2 {
		t.Fatalf("Store returned timestamp %v, but wanted %v", st.WriteTime, now)
	}

	ShouldCAS(t, s, "key", store.AnyV, store.MissingV)
}
Esempio n. 18
0
func TestCacheCachesGets(t *testing.T) {
	inner := &CountingStore{MockStore: storetests.NewMockStore(0)}
	cache := New(1024, inner)

	storetests.ShouldCAS(t, inner, "asdf", store.AnyV, store.DataV([]byte("hello")))
	cache.assertUsedIsCorrect()

	for i := 0; i < 10; i++ {
		storetests.ShouldGet(t, cache, "asdf", []byte("hello"))
		cache.assertUsedIsCorrect()
	}

	if inner.gets != 1 {
		t.Errorf("wanted 1 inner Get, got %v", inner.gets)
	}

	if inner.stats != 9 {
		t.Errorf("wanted 9 inner Stats, got %v", inner.stats)
	}

	cache.assertUsedIsCorrect()
}
Esempio n. 19
0
func TestMultiRecovery(t *testing.T) {
	for total := 4; total < 6; total++ {
		killers, multi, _, done := prepareMultiTest(t, 3, total, 6)
		defer done()

		for i := 0; i < 20; i++ {
			key := strconv.FormatInt(int64(i), 10)
			var value []byte
			for j := 0; j < i; j++ {
				value = append(value, []byte(key)...)
			}

			storetests.ShouldCAS(t, multi, key, store.MissingV, store.DataV(value))
		}

		for i := 0; i < total-3; i++ {
			for {
				k := killers[rand.Intn(len(killers))]
				if k.isKilled() {
					continue
				}
				k.setKilled(true)
				break
			}
		}

		for i := 0; i < 20; i++ {
			key := strconv.FormatInt(int64(i), 10)
			var value []byte
			for j := 0; j < i; j++ {
				value = append(value, []byte(key)...)
			}

			storetests.ShouldGet(t, multi, key, value)
		}
	}
}
Esempio n. 20
0
func TestStoreGoroutineLeaks(t *testing.T, s store.Store) {
	t.Logf("TestStoreGoroutineLeaks()")

	// do the operations twice, only check goroutine counts on the second iteration
	// the implementation may start helper routines, which are not an issue unless
	// they leak for each operation.
	for i := 0; i < 2; i++ {
		routinesAtStart := runtime.NumGoroutine()

		ShouldCAS(t, s, "hello", store.MissingV, store.DataV([]byte("world")))
		ShouldGet(t, s, "hello", []byte("world"))
		ShouldFullList(t, s, []string{"hello"})
		ShouldFreeSpace(t, s)
		ShouldCAS(t, s, "hello", store.AnyV, store.MissingV)

		if i == 1 {
			routinesAtEnd := runtime.NumGoroutine()

			if routinesAtStart != routinesAtEnd {
				t.Errorf("Had %v goroutines running, wanted %v", routinesAtEnd, routinesAtStart)
			}
		}
	}
}
Esempio n. 21
0
func TestStoreList(t *testing.T, s store.Store) {
	t.Logf("TestStoreList()")

	ShouldFullList(t, s, nil)
	ShouldCAS(t, s, "a", store.MissingV, store.DataV([]byte("a")))
	ShouldCAS(t, s, "x", store.MissingV, store.DataV([]byte("x")))
	ShouldCAS(t, s, "z", store.MissingV, store.DataV([]byte("z")))
	ShouldCAS(t, s, "c", store.MissingV, store.DataV([]byte("c")))
	ShouldCAS(t, s, "b", store.MissingV, store.DataV([]byte("b")))
	ShouldCAS(t, s, "y", store.MissingV, store.DataV([]byte("y")))
	ShouldList(t, s, "", 1, []string{"a"})
	ShouldList(t, s, "", 3, []string{"a", "b", "c"})
	ShouldList(t, s, "a", 3, []string{"b", "c", "x"})
	ShouldList(t, s, "c", 5, []string{"x", "y", "z"})
	ShouldList(t, s, "z", 3, nil)
	ShouldCAS(t, s, "a", store.AnyV, store.MissingV)
	ShouldCAS(t, s, "b", store.AnyV, store.MissingV)
	ShouldCAS(t, s, "c", store.AnyV, store.MissingV)
	ShouldCAS(t, s, "x", store.AnyV, store.MissingV)
	ShouldCAS(t, s, "y", store.AnyV, store.MissingV)
	ShouldCAS(t, s, "z", store.AnyV, store.MissingV)
	ShouldFullList(t, s, nil)
}
Esempio n. 22
0
func TestHTTPConditionalGetLastModified(t *testing.T) {
	mock := storetests.NewMockStore(0)
	srv := NewServer(mock)

	storetests.ShouldCAS(t, mock, "key", store.AnyV, store.DataV([]byte("some data")))

	mkReq := func(ims string) *http.Request {
		req, err := http.NewRequest("GET", "/key", nil)
		if err != nil {
			t.Fatalf("Couldn't create request: %v", err)
		}
		if ims != "" {
			req.Header.Set("If-Modified-Since", ims)
		}
		return req
	}

	// First request: no special headers
	resp := httptest.NewRecorder()
	req := mkReq("")
	srv.ServeHTTP(resp, req)
	if resp.Code != 200 {
		t.Fatalf("Couldn't GET /key, status code %v", resp.Code)
	}

	lmStr := resp.HeaderMap.Get("Last-Modified")
	if lmStr == "" {
		t.Fatalf("Last-Modified header was not returned from GET")
	}

	lm, err := time.Parse(http.TimeFormat, lmStr)
	if err != nil {
		t.Fatalf("Couldn't parse Last-Modified %#v: %v", lmStr, err)
	}

	// Second request: matching If-Modified-Since time
	resp = httptest.NewRecorder()
	req = mkReq(lm.Format(http.TimeFormat))
	srv.ServeHTTP(resp, req)
	if resp.Code != 304 {
		t.Fatalf("Equal If-Modified-Since response did not return partial, got status %v",
			resp.Code)
	}

	// Third request: If-Modified-Since time in the past
	resp = httptest.NewRecorder()
	req = mkReq(lm.Add(-time.Second).Format(http.TimeFormat))
	srv.ServeHTTP(resp, req)
	if resp.Code != 200 {
		t.Fatalf("Past If-Modified-Since response did not return full, got status %v",
			resp.Code)
	}

	// Fourth request: If-Modified-Since time in the future
	resp = httptest.NewRecorder()
	req = mkReq(lm.Add(time.Second).Format(http.TimeFormat))
	srv.ServeHTTP(resp, req)
	if resp.Code != 304 {
		t.Fatalf("Equal If-Modified-Since response did not return partial, got status %v",
			resp.Code)
	}
}
Esempio n. 23
0
func (m *Multi) writeChunks(key string, data []byte, sha [32]byte, prefixid [16]byte) (*meta.File, error) {
	m.mu.Lock()
	conf := m.config
	m.mu.Unlock()

	stores, err := m.orderTargets()
	if err != nil {
		return nil, err
	}

	mapping, all := gf.MapToGF(data)
	parts := splitVector(all, conf.Need)
	parityParts := make([][]uint32, conf.Total-conf.Need)
	for i := range parityParts {
		parityParts[i] = rs.CreateParity(parts, i+len(parts), nil)
	}
	parts = append(parts, parityParts...)

	file := &meta.File{
		Path:         key,
		Size:         uint64(len(data)),
		WriteTime:    time.Now().Unix(),
		PrefixID:     prefixid,
		DataChunks:   uint16(conf.Need),
		MappingValue: mapping,
		SHA256:       sha,
	}

	storeCh := make(chan store.Store, len(stores))
	for _, st := range stores {
		storeCh <- st
	}
	close(storeCh)

	file.Locations = make([][16]byte, len(parts))
	errs := make(chan error)
	for i, part := range parts {
		go func(i int, part []uint32) {
			data := gf.MapFromGF(mapping, part)
			localKey := localKeyFor(file, i)
			dataV := store.DataV(data)
			for st := range storeCh {
				err := st.CAS(localKey, store.AnyV, dataV, nil)
				if err != nil {
					// TODO: log
					continue
				}

				file.Locations[i] = st.UUID()
				errs <- nil
				return
			}
			errs <- ErrInsufficientStores
		}(i, part)
	}

	var theError error
	for range parts {
		err := <-errs
		if err != nil && theError == nil {
			theError = err
		}
	}

	if theError != nil {
		// attempt to clean up any parts we wrote
		for i := range parts {
			st := m.finder.StoreFor(file.Locations[i])
			if st != nil {
				localKey := localKeyFor(file, i)
				st.CAS(localKey, store.AnyV, store.MissingV, nil)
			}
		}

		return nil, theError
	}

	return file, nil
}
Esempio n. 24
0
func TestStoreCASCountRace(t *testing.T, s store.Store) {
	t.Logf("TestStoreCASCountRace()")

	const (
		goroutines = 4
		iterations = 15
	)

	ShouldFullList(t, s, nil)

	ShouldCAS(t, s, "key", store.AnyV, store.DataV([]byte("0")))

	errs := make(chan error)
	casFailures := uint64(0)
	for i := 0; i < goroutines; i++ {
		go func(i int) {
			for j := 0; j < iterations; j++ {
				for {
					data, st, err := s.Get("key", store.GetOptions{})
					if err != nil {
						t.Logf("Routine %v: Couldn't get key: %v", i, err)
						errs <- err
						return
					}

					num, err := strconv.ParseInt(string(data), 10, 64)
					if err != nil {
						t.Logf("Routine %v: Couldn't parse int: %v", i, err)
						errs <- err
						return
					}

					num++

					data = strconv.AppendInt(data[:0], num, 10)

					err = s.CAS("key",
						store.CASV{Present: true, SHA256: st.SHA256},
						store.DataV(data),
						nil)
					if err != nil {
						if err == store.ErrCASFailure {
							atomic.AddUint64(&casFailures, 1)
							continue
						}
						t.Logf("Routine %v: Couldn't cas: %v", i, err)
						errs <- err
						return
					}

					break
				}
			}
			errs <- nil
		}(i)
	}
	for i := 0; i < goroutines; i++ {
		err := <-errs
		if err != nil {
			t.Errorf("Got error from goroutine: %v", err)
		}
	}

	t.Logf("%v cas failures", casFailures)

	ShouldGet(t, s, "key", []byte(strconv.FormatInt(goroutines*iterations, 10)))
	ShouldCAS(t, s, "key", store.AnyV, store.MissingV)
}