1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
21 "git.arvados.org/arvados.git/sdk/go/arvados"
22 "git.arvados.org/arvados.git/sdk/go/arvadostest"
23 "git.arvados.org/arvados.git/sdk/go/ctxlog"
24 "github.com/prometheus/client_golang/prometheus"
25 dto "github.com/prometheus/client_model/go"
26 "github.com/sirupsen/logrus"
30 Error(args ...interface{})
31 Errorf(format string, args ...interface{})
35 Fatal(args ...interface{})
36 Fatalf(format string, args ...interface{})
37 Log(args ...interface{})
38 Logf(format string, args ...interface{})
41 // A TestableVolumeFactory returns a new TestableVolume. The factory
42 // function, and the TestableVolume it returns, can use "t" to write
43 // logs, fail the current test, etc.
44 type TestableVolumeFactory func(t TB, params newVolumeParams) TestableVolume
46 // DoGenericVolumeTests runs a set of tests that every TestableVolume
47 // is expected to pass. It calls factory to create a new TestableVolume
48 // for each test case, to avoid leaking state between tests.
49 func DoGenericVolumeTests(t TB, readonly bool, factory TestableVolumeFactory) {
50 var s genericVolumeSuite
51 s.volume.ReadOnly = readonly
54 s.testGetNoSuchBlock(t, factory)
57 s.testPutBlockWithSameContent(t, factory, TestHash, TestBlock)
58 s.testPutBlockWithSameContent(t, factory, EmptyHash, EmptyBlock)
59 s.testPutBlockWithDifferentContent(t, factory, arvadostest.MD5CollisionMD5, arvadostest.MD5CollisionData[0], arvadostest.MD5CollisionData[1])
60 s.testPutBlockWithDifferentContent(t, factory, arvadostest.MD5CollisionMD5, EmptyBlock, arvadostest.MD5CollisionData[0])
61 s.testPutBlockWithDifferentContent(t, factory, arvadostest.MD5CollisionMD5, arvadostest.MD5CollisionData[0], EmptyBlock)
62 s.testPutBlockWithDifferentContent(t, factory, EmptyHash, EmptyBlock, arvadostest.MD5CollisionData[0])
63 s.testPutMultipleBlocks(t, factory)
65 s.testPutAndTouch(t, factory)
67 s.testTouchNoSuchBlock(t, factory)
69 s.testMtimeNoSuchBlock(t, factory)
71 s.testIndex(t, factory)
74 s.testDeleteNewBlock(t, factory)
75 s.testDeleteOldBlock(t, factory)
77 s.testDeleteNoSuchBlock(t, factory)
79 s.testMetrics(t, readonly, factory)
81 s.testGetConcurrent(t, factory)
83 s.testPutConcurrent(t, factory)
84 s.testPutFullBlock(t, factory)
85 s.testTrashUntrash(t, readonly, factory)
86 s.testTrashEmptyTrashUntrash(t, factory)
90 type genericVolumeSuite struct {
91 cluster *arvados.Cluster
93 logger logrus.FieldLogger
94 metrics *volumeMetricsVecs
95 registry *prometheus.Registry
96 bufferPool *bufferPool
99 func (s *genericVolumeSuite) setup(t TB) {
100 s.cluster = testCluster(t)
101 s.logger = ctxlog.TestLogger(t)
102 s.registry = prometheus.NewRegistry()
103 s.metrics = newVolumeMetricsVecs(s.registry)
104 s.bufferPool = newBufferPool(s.logger, 8, s.registry)
107 func (s *genericVolumeSuite) newVolume(t TB, factory TestableVolumeFactory) TestableVolume {
108 return factory(t, newVolumeParams{
109 UUID: "zzzzz-nyw5e-999999999999999",
111 ConfigVolume: s.volume,
113 MetricsVecs: s.metrics,
114 BufferPool: s.bufferPool,
118 // Put a test block, get it and verify content
119 // Test should pass for both writable and read-only volumes
120 func (s *genericVolumeSuite) testGet(t TB, factory TestableVolumeFactory) {
122 v := s.newVolume(t, factory)
125 err := v.BlockWrite(context.Background(), TestHash, TestBlock)
130 buf := bytes.NewBuffer(nil)
131 _, err = v.BlockRead(context.Background(), TestHash, buf)
135 if bytes.Compare(buf.Bytes(), TestBlock) != 0 {
136 t.Errorf("expected %s, got %s", "foo", buf.String())
140 // Invoke get on a block that does not exist in volume; should result in error
141 // Test should pass for both writable and read-only volumes
142 func (s *genericVolumeSuite) testGetNoSuchBlock(t TB, factory TestableVolumeFactory) {
144 v := s.newVolume(t, factory)
147 if _, err := v.BlockRead(context.Background(), barHash, io.Discard); err == nil {
148 t.Errorf("Expected error while getting non-existing block %v", barHash)
152 // Put a block and put again with same content
153 // Test is intended for only writable volumes
154 func (s *genericVolumeSuite) testPutBlockWithSameContent(t TB, factory TestableVolumeFactory, testHash string, testData []byte) {
156 v := s.newVolume(t, factory)
159 err := v.BlockWrite(context.Background(), testHash, testData)
161 t.Errorf("Got err putting block %q: %q, expected nil", TestBlock, err)
164 err = v.BlockWrite(context.Background(), testHash, testData)
166 t.Errorf("Got err putting block second time %q: %q, expected nil", TestBlock, err)
170 // Put a block and put again with different content
171 // Test is intended for only writable volumes
172 func (s *genericVolumeSuite) testPutBlockWithDifferentContent(t TB, factory TestableVolumeFactory, testHash string, testDataA, testDataB []byte) {
174 v := s.newVolume(t, factory)
177 v.BlockWrite(context.Background(), testHash, testDataA)
179 putErr := v.BlockWrite(context.Background(), testHash, testDataB)
180 buf := bytes.NewBuffer(nil)
181 _, getErr := v.BlockRead(context.Background(), testHash, buf)
183 // Put must not return a nil error unless it has
184 // overwritten the existing data.
185 if buf.String() != string(testDataB) {
186 t.Errorf("Put succeeded but Get returned %+q, expected %+q", buf, testDataB)
189 // It is permissible for Put to fail, but it must
190 // leave us with either the original data, the new
191 // data, or nothing at all.
192 if getErr == nil && buf.String() != string(testDataA) && buf.String() != string(testDataB) {
193 t.Errorf("Put failed but Get returned %+q, which is neither %+q nor %+q", buf, testDataA, testDataB)
198 // Put and get multiple blocks
199 // Test is intended for only writable volumes
200 func (s *genericVolumeSuite) testPutMultipleBlocks(t TB, factory TestableVolumeFactory) {
202 v := s.newVolume(t, factory)
205 err := v.BlockWrite(context.Background(), TestHash, TestBlock)
207 t.Errorf("Got err putting block %q: %q, expected nil", TestBlock, err)
210 err = v.BlockWrite(context.Background(), TestHash2, TestBlock2)
212 t.Errorf("Got err putting block %q: %q, expected nil", TestBlock2, err)
215 err = v.BlockWrite(context.Background(), TestHash3, TestBlock3)
217 t.Errorf("Got err putting block %q: %q, expected nil", TestBlock3, err)
220 buf := bytes.NewBuffer(nil)
221 _, err = v.BlockRead(context.Background(), TestHash, buf)
225 if bytes.Compare(buf.Bytes(), TestBlock) != 0 {
226 t.Errorf("Block present, but got %+q, expected %+q", buf, TestBlock)
231 _, err = v.BlockRead(context.Background(), TestHash2, buf)
235 if bytes.Compare(buf.Bytes(), TestBlock2) != 0 {
236 t.Errorf("Block present, but got %+q, expected %+q", buf, TestBlock2)
241 _, err = v.BlockRead(context.Background(), TestHash3, buf)
245 if bytes.Compare(buf.Bytes(), TestBlock3) != 0 {
246 t.Errorf("Block present, but to %+q, expected %+q", buf, TestBlock3)
251 // testPutAndTouch checks that when applying PUT to a block that
252 // already exists, the block's modification time is updated. Intended
253 // for only writable volumes.
254 func (s *genericVolumeSuite) testPutAndTouch(t TB, factory TestableVolumeFactory) {
256 v := s.newVolume(t, factory)
259 if err := v.BlockWrite(context.Background(), TestHash, TestBlock); err != nil {
263 // We'll verify { t0 < threshold < t1 }, where t0 is the
264 // existing block's timestamp on disk before BlockWrite() and t1 is
265 // its timestamp after BlockWrite().
266 threshold := time.Now().Add(-time.Second)
268 // Set the stored block's mtime far enough in the past that we
269 // can see the difference between "timestamp didn't change"
270 // and "timestamp granularity is too low".
271 v.TouchWithDate(TestHash, time.Now().Add(-20*time.Second))
273 // Make sure v.Mtime() agrees the above Utime really worked.
274 if t0, err := v.Mtime(TestHash); err != nil || t0.IsZero() || !t0.Before(threshold) {
275 t.Errorf("Setting mtime failed: %v, %v", t0, err)
278 // Write the same block again.
279 if err := v.BlockWrite(context.Background(), TestHash, TestBlock); err != nil {
283 // Verify threshold < t1
284 if t1, err := v.Mtime(TestHash); err != nil {
286 } else if t1.Before(threshold) {
287 t.Errorf("t1 %v should be >= threshold %v after v.Put ", t1, threshold)
291 // Touching a non-existing block should result in error.
292 // Test should pass for both writable and read-only volumes
293 func (s *genericVolumeSuite) testTouchNoSuchBlock(t TB, factory TestableVolumeFactory) {
295 v := s.newVolume(t, factory)
298 if err := v.BlockTouch(TestHash); err == nil {
299 t.Error("Expected error when attempted to touch a non-existing block")
303 // Invoking Mtime on a non-existing block should result in error.
304 // Test should pass for both writable and read-only volumes
305 func (s *genericVolumeSuite) testMtimeNoSuchBlock(t TB, factory TestableVolumeFactory) {
307 v := s.newVolume(t, factory)
310 if _, err := v.Mtime("12345678901234567890123456789012"); err == nil {
311 t.Error("Expected error when updating Mtime on a non-existing block")
315 // Put a few blocks and invoke Index with:
318 // * with no such prefix
319 // Test should pass for both writable and read-only volumes
320 func (s *genericVolumeSuite) testIndex(t TB, factory TestableVolumeFactory) {
322 v := s.newVolume(t, factory)
325 // minMtime and maxMtime are the minimum and maximum
326 // acceptable values the index can report for our test
327 // blocks. 1-second precision is acceptable.
328 minMtime := time.Now().UTC().UnixNano()
329 minMtime -= minMtime % 1e9
331 v.BlockWrite(context.Background(), TestHash, TestBlock)
332 v.BlockWrite(context.Background(), TestHash2, TestBlock2)
333 v.BlockWrite(context.Background(), TestHash3, TestBlock3)
335 maxMtime := time.Now().UTC().UnixNano()
336 if maxMtime%1e9 > 0 {
337 maxMtime -= maxMtime % 1e9
341 // Blocks whose names aren't Keep hashes should be omitted from
343 v.BlockWrite(context.Background(), "fffffffffnotreallyahashfffffffff", nil)
344 v.BlockWrite(context.Background(), "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", nil)
345 v.BlockWrite(context.Background(), "f0000000000000000000000000000000f", nil)
346 v.BlockWrite(context.Background(), "f00", nil)
348 buf := new(bytes.Buffer)
349 v.Index(context.Background(), "", buf)
350 indexRows := strings.Split(string(buf.Bytes()), "\n")
351 sort.Strings(indexRows)
352 sortedIndex := strings.Join(indexRows, "\n")
353 m := regexp.MustCompile(
354 `^\n` + TestHash + `\+\d+ (\d+)\n` +
355 TestHash3 + `\+\d+ \d+\n` +
356 TestHash2 + `\+\d+ \d+$`,
357 ).FindStringSubmatch(sortedIndex)
359 t.Errorf("Got index %q for empty prefix", sortedIndex)
361 mtime, err := strconv.ParseInt(m[1], 10, 64)
364 } else if mtime < minMtime || mtime > maxMtime {
365 t.Errorf("got %d for TestHash timestamp, expected %d <= t <= %d",
366 mtime, minMtime, maxMtime)
370 for _, prefix := range []string{"f", "f15", "f15ac"} {
371 buf = new(bytes.Buffer)
372 v.Index(context.Background(), prefix, buf)
374 m, err := regexp.MatchString(`^`+TestHash2+`\+\d+ \d+\n$`, string(buf.Bytes()))
378 t.Errorf("Got index %q for prefix %s", string(buf.Bytes()), prefix)
382 for _, prefix := range []string{"zero", "zip", "zilch"} {
383 buf = new(bytes.Buffer)
384 err := v.Index(context.Background(), prefix, buf)
386 t.Errorf("Got error on Index with no such prefix %v", err.Error())
387 } else if buf.Len() != 0 {
388 t.Errorf("Expected empty list for Index with no such prefix %s", prefix)
393 // Calling Delete() for a block immediately after writing it (not old enough)
394 // should neither delete the data nor return an error.
395 // Test is intended for only writable volumes
396 func (s *genericVolumeSuite) testDeleteNewBlock(t TB, factory TestableVolumeFactory) {
398 s.cluster.Collections.BlobSigningTTL.Set("5m")
399 v := s.newVolume(t, factory)
402 v.BlockWrite(context.Background(), TestHash, TestBlock)
404 if err := v.BlockTrash(TestHash); err != nil {
407 buf := bytes.NewBuffer(nil)
408 _, err := v.BlockRead(context.Background(), TestHash, buf)
411 } else if buf.String() != string(TestBlock) {
412 t.Errorf("Got data %+q, expected %+q", buf.String(), TestBlock)
416 // Calling Delete() for a block with a timestamp older than
417 // BlobSigningTTL seconds in the past should delete the data. Test is
418 // intended for only writable volumes
419 func (s *genericVolumeSuite) testDeleteOldBlock(t TB, factory TestableVolumeFactory) {
421 s.cluster.Collections.BlobSigningTTL.Set("5m")
422 v := s.newVolume(t, factory)
425 v.BlockWrite(context.Background(), TestHash, TestBlock)
426 v.TouchWithDate(TestHash, time.Now().Add(-2*s.cluster.Collections.BlobSigningTTL.Duration()))
428 if err := v.BlockTrash(TestHash); err != nil {
431 if _, err := v.BlockRead(context.Background(), TestHash, io.Discard); err == nil || !os.IsNotExist(err) {
432 t.Errorf("os.IsNotExist(%v) should have been true", err)
435 _, err := v.Mtime(TestHash)
436 if err == nil || !os.IsNotExist(err) {
437 t.Errorf("os.IsNotExist(%v) should have been true", err)
440 indexBuf := new(bytes.Buffer)
441 v.Index(context.Background(), "", indexBuf)
442 if strings.Contains(string(indexBuf.Bytes()), TestHash) {
443 t.Errorf("Found trashed block in Index")
446 err = v.BlockTouch(TestHash)
447 if err == nil || !os.IsNotExist(err) {
448 t.Errorf("os.IsNotExist(%v) should have been true", err)
452 // Calling Delete() for a block that does not exist should result in error.
453 // Test should pass for both writable and read-only volumes
454 func (s *genericVolumeSuite) testDeleteNoSuchBlock(t TB, factory TestableVolumeFactory) {
456 v := s.newVolume(t, factory)
459 if err := v.BlockTrash(TestHash2); err == nil {
460 t.Errorf("Expected error when attempting to delete a non-existing block")
464 func getValueFrom(cv *prometheus.CounterVec, lbls prometheus.Labels) float64 {
465 c, _ := cv.GetMetricWith(lbls)
468 return pb.GetCounter().GetValue()
471 func (s *genericVolumeSuite) testMetrics(t TB, readonly bool, factory TestableVolumeFactory) {
475 v := s.newVolume(t, factory)
478 opsC, _, ioC := s.metrics.getCounterVecsFor(prometheus.Labels{"device_id": v.DeviceID()})
481 t.Error("ioBytes CounterVec is nil")
485 if getValueFrom(ioC, prometheus.Labels{"direction": "out"})+
486 getValueFrom(ioC, prometheus.Labels{"direction": "in"}) > 0 {
487 t.Error("ioBytes counter should be zero")
491 t.Error("opsCounter CounterVec is nil")
495 var c, writeOpCounter, readOpCounter float64
497 readOpType, writeOpType := v.ReadWriteOperationLabelValues()
498 writeOpCounter = getValueFrom(opsC, prometheus.Labels{"operation": writeOpType})
499 readOpCounter = getValueFrom(opsC, prometheus.Labels{"operation": readOpType})
501 // Test Put if volume is writable
503 err = v.BlockWrite(context.Background(), TestHash, TestBlock)
505 t.Errorf("Got err putting block %q: %q, expected nil", TestBlock, err)
507 // Check that the write operations counter increased
508 c = getValueFrom(opsC, prometheus.Labels{"operation": writeOpType})
509 if c <= writeOpCounter {
510 t.Error("Operation(s) not counted on Put")
512 // Check that bytes counter is > 0
513 if getValueFrom(ioC, prometheus.Labels{"direction": "out"}) == 0 {
514 t.Error("ioBytes{direction=out} counter shouldn't be zero")
517 v.BlockWrite(context.Background(), TestHash, TestBlock)
520 _, err = v.BlockRead(context.Background(), TestHash, io.Discard)
525 // Check that the operations counter increased
526 c = getValueFrom(opsC, prometheus.Labels{"operation": readOpType})
527 if c <= readOpCounter {
528 t.Error("Operation(s) not counted on Get")
530 // Check that the bytes "in" counter is > 0
531 if getValueFrom(ioC, prometheus.Labels{"direction": "in"}) == 0 {
532 t.Error("ioBytes{direction=in} counter shouldn't be zero")
536 // Launch concurrent Gets
537 // Test should pass for both writable and read-only volumes
538 func (s *genericVolumeSuite) testGetConcurrent(t TB, factory TestableVolumeFactory) {
540 v := s.newVolume(t, factory)
543 v.BlockWrite(context.Background(), TestHash, TestBlock)
544 v.BlockWrite(context.Background(), TestHash2, TestBlock2)
545 v.BlockWrite(context.Background(), TestHash3, TestBlock3)
547 sem := make(chan int)
549 buf := bytes.NewBuffer(nil)
550 _, err := v.BlockRead(context.Background(), TestHash, buf)
552 t.Errorf("err1: %v", err)
554 if buf.String() != string(TestBlock) {
555 t.Errorf("buf should be %s, is %s", TestBlock, buf)
561 buf := bytes.NewBuffer(nil)
562 _, err := v.BlockRead(context.Background(), TestHash2, buf)
564 t.Errorf("err2: %v", err)
566 if buf.String() != string(TestBlock2) {
567 t.Errorf("buf should be %s, is %s", TestBlock2, buf)
573 buf := bytes.NewBuffer(nil)
574 _, err := v.BlockRead(context.Background(), TestHash3, buf)
576 t.Errorf("err3: %v", err)
578 if buf.String() != string(TestBlock3) {
579 t.Errorf("buf should be %s, is %s", TestBlock3, buf)
584 // Wait for all goroutines to finish
585 for done := 0; done < 3; done++ {
590 // Launch concurrent Puts
591 // Test is intended for only writable volumes
592 func (s *genericVolumeSuite) testPutConcurrent(t TB, factory TestableVolumeFactory) {
594 v := s.newVolume(t, factory)
601 {hash: TestHash, data: TestBlock},
602 {hash: TestHash2, data: TestBlock2},
603 {hash: TestHash3, data: TestBlock3},
606 var wg sync.WaitGroup
607 for _, blk := range blks {
612 err := v.BlockWrite(context.Background(), blk.hash, blk.data)
614 t.Errorf("%s: %v", blk.hash, err)
620 // Check that we actually wrote the blocks.
621 for _, blk := range blks {
622 buf := bytes.NewBuffer(nil)
623 _, err := v.BlockRead(context.Background(), blk.hash, buf)
625 t.Errorf("get %s: %v", blk.hash, err)
626 } else if buf.String() != string(blk.data) {
627 t.Errorf("get %s: expected %s, got %s", blk.hash, blk.data, buf)
632 // Write and read back a full size block
633 func (s *genericVolumeSuite) testPutFullBlock(t TB, factory TestableVolumeFactory) {
635 v := s.newVolume(t, factory)
638 wdata := make([]byte, BlockSize)
640 wdata[BlockSize-1] = 'z'
641 hash := fmt.Sprintf("%x", md5.Sum(wdata))
642 err := v.BlockWrite(context.Background(), hash, wdata)
647 buf := bytes.NewBuffer(nil)
648 _, err = v.BlockRead(context.Background(), hash, buf)
652 if buf.String() != string(wdata) {
653 t.Error("buf %+q != wdata %+q", buf, wdata)
657 // With BlobTrashLifetime != 0, perform:
658 // Trash an old block - which either raises ErrNotImplemented or succeeds
659 // Untrash - which either raises ErrNotImplemented or succeeds
660 // Get - which must succeed
661 func (s *genericVolumeSuite) testTrashUntrash(t TB, readonly bool, factory TestableVolumeFactory) {
663 s.cluster.Collections.BlobTrashLifetime.Set("1h")
664 v := s.newVolume(t, factory)
667 // put block and backdate it
668 v.BlockWrite(context.Background(), TestHash, TestBlock)
669 v.TouchWithDate(TestHash, time.Now().Add(-2*s.cluster.Collections.BlobSigningTTL.Duration()))
671 buf := bytes.NewBuffer(nil)
672 _, err := v.BlockRead(context.Background(), TestHash, buf)
676 if buf.String() != string(TestBlock) {
677 t.Errorf("Got data %+q, expected %+q", buf, TestBlock)
681 err = v.BlockTrash(TestHash)
687 _, err = v.BlockRead(context.Background(), TestHash, buf)
688 if err == nil || !os.IsNotExist(err) {
689 t.Errorf("os.IsNotExist(%v) should have been true", err)
693 err = v.BlockUntrash(TestHash)
698 // Get the block - after trash and untrash sequence
700 _, err = v.BlockRead(context.Background(), TestHash, buf)
704 if buf.String() != string(TestBlock) {
705 t.Errorf("Got data %+q, expected %+q", buf, TestBlock)
709 func (s *genericVolumeSuite) testTrashEmptyTrashUntrash(t TB, factory TestableVolumeFactory) {
711 v := s.newVolume(t, factory)
714 checkGet := func() error {
715 buf := bytes.NewBuffer(nil)
716 _, err := v.BlockRead(context.Background(), TestHash, buf)
720 if buf.String() != string(TestBlock) {
721 t.Errorf("Got data %+q, expected %+q", buf, TestBlock)
724 _, err = v.Mtime(TestHash)
729 indexBuf := new(bytes.Buffer)
730 v.Index(context.Background(), "", indexBuf)
731 if !strings.Contains(string(indexBuf.Bytes()), TestHash) {
732 return os.ErrNotExist
738 // First set: EmptyTrash before reaching the trash deadline.
740 s.cluster.Collections.BlobTrashLifetime.Set("1h")
742 v.BlockWrite(context.Background(), TestHash, TestBlock)
743 v.TouchWithDate(TestHash, time.Now().Add(-2*s.cluster.Collections.BlobSigningTTL.Duration()))
751 err = v.BlockTrash(TestHash)
757 if err == nil || !os.IsNotExist(err) {
758 t.Errorf("os.IsNotExist(%v) should have been true", err)
761 err = v.BlockTouch(TestHash)
762 if err == nil || !os.IsNotExist(err) {
763 t.Errorf("os.IsNotExist(%v) should have been true", err)
768 // Even after emptying the trash, we can untrash our block
769 // because the deadline hasn't been reached.
770 err = v.BlockUntrash(TestHash)
780 err = v.BlockTouch(TestHash)
785 // Because we Touch'ed, need to backdate again for next set of tests
786 v.TouchWithDate(TestHash, time.Now().Add(-2*s.cluster.Collections.BlobSigningTTL.Duration()))
788 // If the only block in the trash has already been untrashed,
789 // most volumes will fail a subsequent Untrash with a 404, but
790 // it's also acceptable for Untrash to succeed.
791 err = v.BlockUntrash(TestHash)
792 if err != nil && !os.IsNotExist(err) {
793 t.Errorf("Expected success or os.IsNotExist(), but got: %v", err)
796 // The additional Untrash should not interfere with our
797 // already-untrashed copy.
803 // Untrash might have updated the timestamp, so backdate again
804 v.TouchWithDate(TestHash, time.Now().Add(-2*s.cluster.Collections.BlobSigningTTL.Duration()))
806 // Second set: EmptyTrash after the trash deadline has passed.
808 s.cluster.Collections.BlobTrashLifetime.Set("1ns")
810 err = v.BlockTrash(TestHash)
815 if err == nil || !os.IsNotExist(err) {
816 t.Errorf("os.IsNotExist(%v) should have been true", err)
819 // Even though 1ns has passed, we can untrash because we
820 // haven't called EmptyTrash yet.
821 err = v.BlockUntrash(TestHash)
830 // Trash it again, and this time call EmptyTrash so it really
832 // (In Azure volumes, un/trash changes Mtime, so first backdate again)
833 v.TouchWithDate(TestHash, time.Now().Add(-2*s.cluster.Collections.BlobSigningTTL.Duration()))
834 _ = v.BlockTrash(TestHash)
836 if err == nil || !os.IsNotExist(err) {
837 t.Errorf("os.IsNotExist(%v) should have been true", err)
841 // Untrash won't find it
842 err = v.BlockUntrash(TestHash)
843 if err == nil || !os.IsNotExist(err) {
844 t.Errorf("os.IsNotExist(%v) should have been true", err)
847 // Get block won't find it
849 if err == nil || !os.IsNotExist(err) {
850 t.Errorf("os.IsNotExist(%v) should have been true", err)
853 // Third set: If the same data block gets written again after
854 // being trashed, and then the trash gets emptied, the newer
855 // un-trashed copy doesn't get deleted along with it.
857 v.BlockWrite(context.Background(), TestHash, TestBlock)
858 v.TouchWithDate(TestHash, time.Now().Add(-2*s.cluster.Collections.BlobSigningTTL.Duration()))
860 s.cluster.Collections.BlobTrashLifetime.Set("1ns")
861 err = v.BlockTrash(TestHash)
866 if err == nil || !os.IsNotExist(err) {
867 t.Errorf("os.IsNotExist(%v) should have been true", err)
870 v.BlockWrite(context.Background(), TestHash, TestBlock)
871 v.TouchWithDate(TestHash, time.Now().Add(-2*s.cluster.Collections.BlobSigningTTL.Duration()))
873 // EmptyTrash should not delete the untrashed copy.
880 // Fourth set: If the same data block gets trashed twice with
881 // different deadlines A and C, and then the trash is emptied
882 // at intermediate time B (A < B < C), it is still possible to
883 // untrash the block whose deadline is "C".
885 v.BlockWrite(context.Background(), TestHash, TestBlock)
886 v.TouchWithDate(TestHash, time.Now().Add(-2*s.cluster.Collections.BlobSigningTTL.Duration()))
888 s.cluster.Collections.BlobTrashLifetime.Set("1ns")
889 err = v.BlockTrash(TestHash)
894 v.BlockWrite(context.Background(), TestHash, TestBlock)
895 v.TouchWithDate(TestHash, time.Now().Add(-2*s.cluster.Collections.BlobSigningTTL.Duration()))
897 s.cluster.Collections.BlobTrashLifetime.Set("1h")
898 err = v.BlockTrash(TestHash)
903 // EmptyTrash should not prevent us from recovering the
904 // time.Hour ("C") trash
906 err = v.BlockUntrash(TestHash)