Merge branch 'master' into 5720-ajax-loading-error
[arvados.git] / services / keepstore / trash_worker_test.go
1 package main
2
3 import (
4         "container/list"
5         "testing"
6         "time"
7 )
8
9 type TrashWorkerTestData struct {
10         Locator1    string
11         Block1      []byte
12         BlockMtime1 int64
13
14         Locator2    string
15         Block2      []byte
16         BlockMtime2 int64
17
18         CreateData      bool
19         CreateInVolume1 bool
20
21         UseTrashLifeTime bool
22         DifferentMtimes  bool
23
24         DeleteLocator string
25
26         ExpectLocator1 bool
27         ExpectLocator2 bool
28 }
29
30 /* Delete block that does not exist in any of the keep volumes.
31    Expect no errors.
32 */
33 func TestTrashWorkerIntegration_GetNonExistingLocator(t *testing.T) {
34         testData := TrashWorkerTestData{
35                 Locator1: "5d41402abc4b2a76b9719d911017c592",
36                 Block1:   []byte("hello"),
37
38                 Locator2: "5d41402abc4b2a76b9719d911017c592",
39                 Block2:   []byte("hello"),
40
41                 CreateData: false,
42
43                 DeleteLocator: "5d41402abc4b2a76b9719d911017c592",
44
45                 ExpectLocator1: false,
46                 ExpectLocator2: false,
47         }
48         performTrashWorkerTest(testData, t)
49 }
50
51 /* Delete a block that exists on volume 1 of the keep servers.
52    Expect the second locator in volume 2 to be unaffected.
53 */
54 func TestTrashWorkerIntegration_LocatorInVolume1(t *testing.T) {
55         testData := TrashWorkerTestData{
56                 Locator1: TEST_HASH,
57                 Block1:   TEST_BLOCK,
58
59                 Locator2: TEST_HASH_2,
60                 Block2:   TEST_BLOCK_2,
61
62                 CreateData: true,
63
64                 DeleteLocator: TEST_HASH, // first locator
65
66                 ExpectLocator1: false,
67                 ExpectLocator2: true,
68         }
69         performTrashWorkerTest(testData, t)
70 }
71
72 /* Delete a block that exists on volume 2 of the keep servers.
73    Expect the first locator in volume 1 to be unaffected.
74 */
75 func TestTrashWorkerIntegration_LocatorInVolume2(t *testing.T) {
76         testData := TrashWorkerTestData{
77                 Locator1: TEST_HASH,
78                 Block1:   TEST_BLOCK,
79
80                 Locator2: TEST_HASH_2,
81                 Block2:   TEST_BLOCK_2,
82
83                 CreateData: true,
84
85                 DeleteLocator: TEST_HASH_2, // locator 2
86
87                 ExpectLocator1: true,
88                 ExpectLocator2: false,
89         }
90         performTrashWorkerTest(testData, t)
91 }
92
93 /* Delete a block with matching mtime for locator in both volumes.
94    Expect locator to be deleted from both volumes.
95 */
96 func TestTrashWorkerIntegration_LocatorInBothVolumes(t *testing.T) {
97         testData := TrashWorkerTestData{
98                 Locator1: TEST_HASH,
99                 Block1:   TEST_BLOCK,
100
101                 Locator2: TEST_HASH,
102                 Block2:   TEST_BLOCK,
103
104                 CreateData: true,
105
106                 DeleteLocator: TEST_HASH,
107
108                 ExpectLocator1: false,
109                 ExpectLocator2: false,
110         }
111         performTrashWorkerTest(testData, t)
112 }
113
114 /* Same locator with different Mtimes exists in both volumes.
115    Delete the second and expect the first to be still around.
116 */
117 func TestTrashWorkerIntegration_MtimeMatchesForLocator1ButNotForLocator2(t *testing.T) {
118         testData := TrashWorkerTestData{
119                 Locator1: TEST_HASH,
120                 Block1:   TEST_BLOCK,
121
122                 Locator2: TEST_HASH,
123                 Block2:   TEST_BLOCK,
124
125                 CreateData:      true,
126                 DifferentMtimes: true,
127
128                 DeleteLocator: TEST_HASH,
129
130                 ExpectLocator1: true,
131                 ExpectLocator2: false,
132         }
133         performTrashWorkerTest(testData, t)
134 }
135
136 /* Two different locators in volume 1.
137    Delete one of them.
138    Expect the other unaffected.
139 */
140 func TestTrashWorkerIntegration_TwoDifferentLocatorsInVolume1(t *testing.T) {
141         testData := TrashWorkerTestData{
142                 Locator1: TEST_HASH,
143                 Block1:   TEST_BLOCK,
144
145                 Locator2: TEST_HASH_2,
146                 Block2:   TEST_BLOCK_2,
147
148                 CreateData:      true,
149                 CreateInVolume1: true,
150
151                 DeleteLocator: TEST_HASH, // locator 1
152
153                 ExpectLocator1: false,
154                 ExpectLocator2: true,
155         }
156         performTrashWorkerTest(testData, t)
157 }
158
159 /* Allow default Trash Life time to be used. Thus, the newly created block
160    will not be deleted becuase its Mtime is within the trash life time.
161 */
162 func TestTrashWorkerIntegration_SameLocatorInTwoVolumesWithDefaultTrashLifeTime(t *testing.T) {
163         testData := TrashWorkerTestData{
164                 Locator1: TEST_HASH,
165                 Block1:   TEST_BLOCK,
166
167                 Locator2: TEST_HASH_2,
168                 Block2:   TEST_BLOCK_2,
169
170                 CreateData:      true,
171                 CreateInVolume1: true,
172
173                 UseTrashLifeTime: true,
174
175                 DeleteLocator: TEST_HASH, // locator 1
176
177                 // Since trash life time is in effect, block won't be deleted.
178                 ExpectLocator1: true,
179                 ExpectLocator2: true,
180         }
181         performTrashWorkerTest(testData, t)
182 }
183
184 /* Perform the test */
185 func performTrashWorkerTest(testData TrashWorkerTestData, t *testing.T) {
186         // Create Keep Volumes
187         KeepVM = MakeTestVolumeManager(2)
188         defer KeepVM.Close()
189
190         // Put test content
191         vols := KeepVM.AllWritable()
192         if testData.CreateData {
193                 vols[0].Put(testData.Locator1, testData.Block1)
194                 vols[0].Put(testData.Locator1+".meta", []byte("metadata"))
195
196                 if testData.CreateInVolume1 {
197                         vols[0].Put(testData.Locator2, testData.Block2)
198                         vols[0].Put(testData.Locator2+".meta", []byte("metadata"))
199                 } else {
200                         vols[1].Put(testData.Locator2, testData.Block2)
201                         vols[1].Put(testData.Locator2+".meta", []byte("metadata"))
202                 }
203         }
204
205         oldBlockTime := time.Now().Add(-blob_signature_ttl - time.Minute)
206
207         // Create TrashRequest for the test
208         trashRequest := TrashRequest{
209                 Locator:    testData.DeleteLocator,
210                 BlockMtime: oldBlockTime.Unix(),
211         }
212
213         // Run trash worker and put the trashRequest on trashq
214         trashList := list.New()
215         trashList.PushBack(trashRequest)
216         trashq = NewWorkQueue()
217         defer trashq.Close()
218
219         if !testData.UseTrashLifeTime {
220                 // Trash worker would not delete block if its Mtime is
221                 // within trash life time. Back-date the block to
222                 // allow the deletion to succeed.
223                 for _, v := range vols {
224                         v.(*MockVolume).Timestamps[testData.DeleteLocator] = oldBlockTime
225                         if testData.DifferentMtimes {
226                                 oldBlockTime = oldBlockTime.Add(time.Second)
227                         }
228                 }
229         }
230         go RunTrashWorker(trashq)
231
232         trashq.ReplaceQueue(trashList)
233         time.Sleep(10 * time.Millisecond) // give a moment to finish processing the list
234
235         // Verify Locator1 to be un/deleted as expected
236         data, _ := GetBlock(testData.Locator1, false)
237         if testData.ExpectLocator1 {
238                 if len(data) == 0 {
239                         t.Errorf("Expected Locator1 to be still present: %s", testData.Locator1)
240                 }
241         } else {
242                 if len(data) > 0 {
243                         t.Errorf("Expected Locator1 to be deleted: %s", testData.Locator1)
244                 }
245         }
246
247         // Verify Locator2 to be un/deleted as expected
248         if testData.Locator1 != testData.Locator2 {
249                 data, _ = GetBlock(testData.Locator2, false)
250                 if testData.ExpectLocator2 {
251                         if len(data) == 0 {
252                                 t.Errorf("Expected Locator2 to be still present: %s", testData.Locator2)
253                         }
254                 } else {
255                         if len(data) > 0 {
256                                 t.Errorf("Expected Locator2 to be deleted: %s", testData.Locator2)
257                         }
258                 }
259         }
260
261         // The DifferentMtimes test puts the same locator in two
262         // different volumes, but only one copy has an Mtime matching
263         // the trash request.
264         if testData.DifferentMtimes {
265                 locatorFoundIn := 0
266                 for _, volume := range KeepVM.AllReadable() {
267                         if _, err := volume.Get(testData.Locator1); err == nil {
268                                 locatorFoundIn = locatorFoundIn + 1
269                         }
270                 }
271                 if locatorFoundIn != 1 {
272                         t.Errorf("Found %d copies of %s, expected 1", locatorFoundIn, testData.Locator1)
273                 }
274         }
275 }