X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/7f0aacd7c9e055fd245b1c4516e5f7ec24ebf5e2..c980683a243903babe9cc09cabc71e1c6229fef1:/services/keepstore/volume_generic_test.go diff --git a/services/keepstore/volume_generic_test.go b/services/keepstore/volume_generic_test.go index 975202a327..95166c252f 100644 --- a/services/keepstore/volume_generic_test.go +++ b/services/keepstore/volume_generic_test.go @@ -78,6 +78,7 @@ func DoGenericVolumeTests(t TB, factory TestableVolumeFactory) { testPutFullBlock(t, factory) testTrashUntrash(t, factory) + testTrashEmptyTrashUntrash(t, factory) } // Put a test block, get it and verify content @@ -707,7 +708,7 @@ func testTrashUntrash(t TB, factory TestableVolumeFactory) { v := factory(t) defer v.Teardown() defer func() { - trashLifetime = 0 + trashLifetime = 0 * time.Second }() trashLifetime = 3600 * time.Second @@ -718,7 +719,10 @@ func testTrashUntrash(t TB, factory TestableVolumeFactory) { buf, err := v.Get(TestHash) if err != nil { - t.Errorf("got err %v, expected nil", err) + t.Fatal(err) + } + if bytes.Compare(buf, TestBlock) != 0 { + t.Errorf("Got data %+q, expected %+q", buf, TestBlock) } bufs.Put(buf) @@ -726,11 +730,11 @@ func testTrashUntrash(t TB, factory TestableVolumeFactory) { err = v.Trash(TestHash) if v.Writable() == false { if err != MethodDisabledError { - t.Errorf("Expected MethodDisabledError during Trash from a read-only volume") + t.Error(err) } } else if err != nil { if err != ErrNotImplemented { - t.Errorf("Error during trash: %v", err.Error()) + t.Error(err) } } else { _, err = v.Get(TestHash) @@ -741,19 +745,195 @@ func testTrashUntrash(t TB, factory TestableVolumeFactory) { // Untrash err = v.Untrash(TestHash) if err != nil { - t.Errorf("Error during untrash: %v", err.Error()) + t.Fatal(err) } } // Get the block - after trash and untrash sequence - // Backdating block is resulting in emptying out the block in s3 test setup - // Hence, compare the Get respose with buf (obtained after put, instead of TestBlock) - buf2, err := v.Get(TestHash) + buf, err = v.Get(TestHash) if err != nil { - t.Errorf("Error during Get: %v", err.Error()) - } else { - if bytes.Compare(buf2, buf) != 0 { - t.Errorf("Got data %+q, expected %+q", buf2, buf) + t.Fatal(err) + } + if bytes.Compare(buf, TestBlock) != 0 { + t.Errorf("Got data %+q, expected %+q", buf, TestBlock) + } + bufs.Put(buf) +} + +func testTrashEmptyTrashUntrash(t TB, factory TestableVolumeFactory) { + v := factory(t) + defer v.Teardown() + defer func(orig time.Duration) { + trashLifetime = orig + }(trashLifetime) + + checkGet := func() error { + buf, err := v.Get(TestHash) + if err != nil { + return err } + if bytes.Compare(buf, TestBlock) != 0 { + t.Fatalf("Got data %+q, expected %+q", buf, TestBlock) + } + bufs.Put(buf) + return nil + } + + // First set: EmptyTrash before reaching the trash deadline. + + trashLifetime = 1 * time.Hour + + v.PutRaw(TestHash, TestBlock) + v.TouchWithDate(TestHash, time.Now().Add(-2*blobSignatureTTL)) + + err := checkGet() + if err != nil { + t.Fatal(err) + } + + err = v.Trash(TestHash) + if err == MethodDisabledError || err == ErrNotImplemented { + // Skip the trash tests for read-only volumes, and + // volume types that don't support trashLifetime>0. + return + } + + err = checkGet() + if err == nil || !os.IsNotExist(err) { + t.Fatalf("os.IsNotExist(%v) should have been true", err) + } + + v.EmptyTrash() + + // Even after emptying the trash, we can untrash our block + // because the deadline hasn't been reached. + err = v.Untrash(TestHash) + if err != nil { + t.Fatal(err) + } + err = checkGet() + if err != nil { + t.Fatal(err) + } + + // Untrash should fail if the only block in the trash has + // already been untrashed. + err = v.Untrash(TestHash) + if err == nil || !os.IsNotExist(err) { + t.Fatalf("os.IsNotExist(%v) should have been true", err) + } + + // The failed Untrash should not interfere with our + // already-untrashed copy. + err = checkGet() + if err != nil { + t.Fatal(err) + } + + // Second set: EmptyTrash after the trash deadline has passed. + + trashLifetime = 1 * time.Nanosecond + + err = v.Trash(TestHash) + if err != nil { + t.Fatal(err) + } + err = checkGet() + if err == nil || !os.IsNotExist(err) { + t.Fatalf("os.IsNotExist(%v) should have been true", err) + } + + // Even though 1ns has passed, we can untrash because we + // haven't called EmptyTrash yet. + err = v.Untrash(TestHash) + if err != nil { + t.Fatal(err) + } + err = checkGet() + if err != nil { + t.Fatal(err) + } + + // Trash it again, and this time call EmptyTrash so it really + // goes away. + err = v.Trash(TestHash) + err = checkGet() + if err == nil || !os.IsNotExist(err) { + t.Errorf("os.IsNotExist(%v) should have been true", err) + } + v.EmptyTrash() + + // Untrash won't find it + err = v.Untrash(TestHash) + if err == nil || !os.IsNotExist(err) { + t.Fatalf("os.IsNotExist(%v) should have been true", err) + } + + // Get block won't find it + err = checkGet() + if err == nil || !os.IsNotExist(err) { + t.Fatalf("os.IsNotExist(%v) should have been true", err) + } + + // Third set: If the same data block gets written again after + // being trashed, and then the trash gets emptied, the newer + // un-trashed copy doesn't get deleted along with it. + + v.PutRaw(TestHash, TestBlock) + v.TouchWithDate(TestHash, time.Now().Add(-2*blobSignatureTTL)) + + trashLifetime = time.Nanosecond + err = v.Trash(TestHash) + if err != nil { + t.Fatal(err) + } + err = checkGet() + if err == nil || !os.IsNotExist(err) { + t.Fatalf("os.IsNotExist(%v) should have been true", err) + } + + v.PutRaw(TestHash, TestBlock) + v.TouchWithDate(TestHash, time.Now().Add(-2*blobSignatureTTL)) + + // EmptyTrash should not delete the untrashed copy. + v.EmptyTrash() + err = checkGet() + if err != nil { + t.Fatal(err) + } + + // Fourth set: If the same data block gets trashed twice with + // different deadlines A and C, and then the trash is emptied + // at intermediate time B (A < B < C), it is still possible to + // untrash the block whose deadline is "C". + + v.PutRaw(TestHash, TestBlock) + v.TouchWithDate(TestHash, time.Now().Add(-2*blobSignatureTTL)) + + trashLifetime = time.Nanosecond + err = v.Trash(TestHash) + if err != nil { + t.Fatal(err) + } + + v.PutRaw(TestHash, TestBlock) + v.TouchWithDate(TestHash, time.Now().Add(-2*blobSignatureTTL)) + + trashLifetime = time.Hour + err = v.Trash(TestHash) + if err != nil { + t.Fatal(err) + } + + // EmptyTrash should not prevent us from recovering the + // time.Hour ("C") trash + v.EmptyTrash() + err = v.Untrash(TestHash) + if err != nil { + t.Fatal(err) + } + err = checkGet() + if err != nil { + t.Fatal(err) } }