f, err = os.Open(blockFilename)
if err != nil {
- log.Printf("%s: opening %s: %s\n", vol, blockFilename, err)
+ if !os.IsNotExist(err) {
+ // A block is stored on only one Keep disk,
+ // so os.IsNotExist is expected. Report any other errors.
+ log.Printf("%s: opening %s: %s\n", vol, blockFilename, err)
+ }
continue
}
The MD5 checksum of the block must be identical to the content id HASH.
If not, an error is returned.
- PutBlock stores the BLOCK in each of the available Keep volumes.
- If any volume fails, an error is signaled on the back end. A write
- error is returned only if all volumes fail.
+ PutBlock stores the BLOCK on the first Keep volume with free space.
+ A failure code is returned to the user only if all volumes fail.
On success, PutBlock returns nil.
On failure, it returns a KeepError with one of the following codes:
return &KeepError{401, errors.New("MD5Fail")}
}
- // any_success will be set to true upon a successful block write.
- any_success := false
for _, vol := range KeepVolumes {
+ // TODO(twp): check for a full volume here before trying to write.
+
blockDir := fmt.Sprintf("%s/%s", vol, hash[0:3])
if err := os.MkdirAll(blockDir, 0755); err != nil {
log.Printf("%s: could not create directory %s: %s",
blockFilename := fmt.Sprintf("%s/%s", blockDir, hash)
f, err := os.OpenFile(blockFilename, os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
- // if the block already exists, just skip to the next volume.
+ // if the block already exists, just return success.
+ // TODO(twp): should we check here whether the file on disk
+ // matches the file we were asked to store?
if os.IsExist(err) {
- any_success = true
- continue
+ return nil
} else {
// Open failed for some other reason.
log.Printf("%s: creating %s: %s\n", vol, blockFilename, err)
if _, err := f.Write(block); err == nil {
f.Close()
- any_success = true
- continue
+ return nil
} else {
log.Printf("%s: writing to %s: %s\n", vol, blockFilename, err)
continue
}
}
- if any_success {
- return nil
- } else {
- log.Printf("all Keep volumes failed")
- return &KeepError{500, errors.New("Fail")}
- }
+ // All volumes failed; report the failure and return an error.
+ //
+ log.Printf("all Keep volumes failed")
+ return &KeepError{500, errors.New("Fail")}
}
// GetBlock tests.
// ========================================
-// TestGetBlockOK
-// Test that a simple block read can be executed successfully.
+// TestGetBlock
+// Test that simple block reads succeed.
//
-func TestGetBlockOK(t *testing.T) {
+func TestGetBlock(t *testing.T) {
defer teardown()
- // Create two test Keep volumes and store a block in each of them.
- KeepVolumes = setup(t, 2)
-
- for _, vol := range KeepVolumes {
- store(t, vol, TEST_HASH, TEST_BLOCK)
- }
-
- // Check that GetBlock returns success.
- result, err := GetBlock(TEST_HASH)
- if err != nil {
- t.Errorf("GetBlock error: %s", err)
- }
- if fmt.Sprint(result) != fmt.Sprint(TEST_BLOCK) {
- t.Errorf("expected %s, got %s", TEST_BLOCK, result)
- }
-}
-
-// TestGetBlockOneKeepOK
-// Test that block reads succeed even when the block is found only
-// on one Keep volume.
-//
-func TestGetBlockOneKeepOK(t *testing.T) {
- defer teardown()
-
- // Two test Keep volumes, only the second has a block.
+ // Prepare two test Keep volumes. Our block is stored on the second volume.
KeepVolumes = setup(t, 2)
store(t, KeepVolumes[1], TEST_HASH, TEST_BLOCK)