919
919
self.assertEqual(old_keydir, db._keydir)
922
def test_build_keydir_build_hint_with_dead_rows(self):
923
"""Test that _build_keydir build the hint with TOMBSTONE rows."""
924
db = Tritcask(self.base_dir, dead_bytes_threshold=0.1)
927
db.put(i, 'foo%d' % (i,), 'bar%s' % (i,))
928
# create a inactive file with all the items
931
# delete half of the items
932
db = Tritcask(self.base_dir, dead_bytes_threshold=0.01)
934
db.delete(i, 'foo%d' % (i,))
935
# add one item so the hint file have at leats one
936
db.put(100, 'foo%d' % (100,), 'bar%s' % (100,))
937
assert len(db._keydir.keys()) == 51, len(db._keydir.keys())
938
old_keydir = db._keydir
940
# trigger a rotate and a hint build
941
db = Tritcask(self.base_dir, dead_bytes_threshold=0.01)
944
# trigger a hint build of the last rotated file
945
Tritcask(self.base_dir, dead_bytes_threshold=0.01).shutdown()
946
db = Tritcask(self.base_dir, dead_bytes_threshold=0.01)
947
self.assertEqual(sorted(old_keydir.keys()), sorted(db._keydir.keys()))
922
950
def test_should_rotate(self):
923
951
"""Test should_rotate method."""
924
952
db = Tritcask(self.base_dir)
949
977
self.assertTrue(db.should_merge(db._immutable))
980
def test_should_merge_max_files(self):
981
"""Test should_merge method based on max_im files ."""
982
db = Tritcask(self.base_dir)
984
# create 10 immutable files
987
db.put(i*j, 'foo%d' % (i*j+i,), 'bar%s' % (i,))
989
self.assertFalse(db.should_merge(db._immutable))
990
# create the 11th immutable file
991
db.put(200, 'foo%d' % (200,), 'bar%s' % (200,))
993
self.assertTrue(db.should_merge(db._immutable))
996
def test__rotate_and_not_merge(self):
997
"""Test _rotate_and_merge method."""
998
db = Tritcask(self.base_dir)
999
# add the same data in 5 different data files
1002
db.put(i, 'foo%d' % (i,), 'bar%s' % (i,))
1006
db.put(i, 'foo%d' % (i,), 'bar%s' % (i,))
1009
self.patch(Tritcask, 'rotate', lambda *a, **k: called.append('rotate'))
1010
self.patch(Tritcask, 'merge', lambda *args: called.append('merge'))
1011
Tritcask(self.base_dir).shutdown()
1012
self.assertIn('rotate', called)
1013
self.assertNotIn('merge', called)
952
1015
def test__rotate_and_merge(self):
953
1016
"""Test _rotate_and_merge method."""
954
1017
db = Tritcask(self.base_dir)
955
# add the same data in 5 different data files
1018
# add the slightly different data in 5 different data files
958
1021
db.put(i, 'foo%d' % (i,), 'bar%s' % (i,))
962
1025
db.put(i, 'foo%d' % (i,), 'bar%s' % (i,))
965
self.patch(Tritcask, 'rotate', lambda _: called.append('rotate'))
1028
self.patch(Tritcask, 'rotate', lambda *a, **k: called.append('rotate'))
966
1029
self.patch(Tritcask, 'merge', lambda *args: called.append('merge'))
967
1030
Tritcask(self.base_dir).shutdown()
968
1031
self.assertIn('rotate', called)
1211
1275
self.assertIn(old_live_file_1.file_id, db._immutable)
1214
def test_merge_all_dead_entries_single_file(self):
1215
"""Test merge of a immutable file with 100% dead data.
1278
def test_merge_do_nothing_all_dead_entries_single_file(self):
1279
"""Test possible merge of a immutable file with 100% dead data.
1217
The expected side effect is that the file should be marked as dead."""
1281
The expected side effect is that the file should be marked as immutable,
1282
then merged and marked as dead. And on the next startup deleted.
1218
1284
db = Tritcask(self.base_dir)
1219
1285
self._add_data(db, 100)
1220
1286
# delete almost all entrie to trigger a merge
1221
1287
for i, k in enumerate(db._keydir.keys()):
1225
# at this moment we only have the live file
1291
# start tritcask without automerge and check
1292
db = Tritcask(self.base_dir, auto_merge=False)
1293
files = sorted(os.listdir(db.base_path))
1294
self.assertEqual(2, len(files))
1295
self.assertIn(INACTIVE, files[0])
1296
self.assertIn(LIVE, files[1])
1298
# trigger the rotation
1299
# now we should have a single immutable_file (previous live one)
1300
db = Tritcask(self.base_dir)
1301
self.assertEqual(0, len(db._immutable))
1302
files = sorted(os.listdir(db.base_path))
1303
self.assertEqual(3, len(files))
1304
self.assertIn(DEAD, files[0])
1305
self.assertIn(DEAD, files[1])
1306
self.assertIn(LIVE, files[2])
1308
# at this moment we have the live + imm_file files
1226
1309
# start a new Tritcask instance.
1227
1310
db = Tritcask(self.base_dir)
1228
self.assertEqual(1, len(db._immutable))
1229
imm_file = db._immutable.values()[0]
1311
files = sorted(os.path.join(self.base_dir,f ) \
1312
for f in os.listdir(db.base_path))
1313
#self.assertIn(imm_file.filename.replace(INACTIVE, DEAD), files)
1230
1314
# the immutable_file should be dead as there are no live entries
1231
self.assertIn(DEAD, imm_file.filename)
1232
1315
# there should be only 2 files, the dead and the live
1316
# dead + dead_hint + live
1233
1317
files = sorted(os.listdir(db.base_path))
1234
self.assertEqual(2, len(files), files)
1235
self.assertIn(DEAD, files[0])
1236
self.assertIn(LIVE, files[1])
1318
self.assertEqual(1, len(files), files)
1319
#self.assertIn(DEAD, files[0])
1320
#self.assertIn(DEAD, files[1])
1321
#self.assertIn(HINT, files[1])
1322
self.assertIn(LIVE, files[0])
1238
1324
# start a new Tritcask to check everything is ok after the
1245
1331
def test_merge_mixed_dead_entries(self):
1246
1332
"""Test merge of 2 immutable files one with 100% dead data.
1248
The expected side effect is that the file should be marked as dead."""
1334
The expected side effect is that the file should be marked as dead.
1249
1336
db = Tritcask(self.base_dir)
1250
1337
self._add_data(db, 100)
1251
1338
# delete almost all entrie to trigger a merge
1252
1339
for i, k in enumerate(db._keydir.keys()):
1342
db = Tritcask(self.base_dir, auto_merge=False)
1256
1343
self.assertEqual(1, len(db._immutable))
1257
1344
imm_file_1 = db._immutable.values()[0]
1258
self._add_data(db, 100)
1259
for i, k in enumerate(db._keydir.keys()):
1263
# at this moment we only have the live file
1264
# start a new Tritcask instance.
1265
1346
db = Tritcask(self.base_dir)
1266
1347
# the "empty" immutable_file should be dead as there are no live entries
1267
self.assertIn(DEAD, db._immutable[imm_file_1.file_id].filename)
1268
# there should be 6 files:
1269
# - the empty immutable as "dead"
1270
# - the immutable file just merged
1348
# but it's also removed from the _immutable dict
1349
files = sorted(os.path.join(self.base_dir,f ) \
1350
for f in os.listdir(db.base_path))
1351
self.assertIn(imm_file_1.filename.replace(INACTIVE, DEAD), files)
1352
self._add_data(db, 100)
1353
for i, k in enumerate(db._keydir.keys()):
1357
# at this moment we only have the live file
1358
# start a new Tritcask instance.
1359
db = Tritcask(self.base_dir)
1360
# there should be 5 files:
1361
# - the immutable file just merged as a DEAD file.
1271
1362
# - the new immutable_file
1272
1363
# - the current live file
1273
1364
files = sorted(os.listdir(db.base_path))
1274
self.assertEqual(6, len(files), files)
1365
self.assertEqual(4, len(files), files)
1275
1366
self.assertIn(DEAD, files[0])
1276
self.assertIn(DEAD, files[1])
1277
self.assertIn(HINT, files[1])
1278
self.assertIn(DEAD, files[2])
1367
self.assertIn(INACTIVE, files[1])
1368
self.assertIn(INACTIVE, files[2])
1369
self.assertIn(HINT, files[2])
1279
1370
self.assertIn(LIVE, files[3])
1280
self.assertIn(INACTIVE, files[4])
1281
self.assertIn(INACTIVE, files[5])
1282
self.assertIn(HINT, files[5])
1284
1372
# start a new Tritcask to check everything is ok after the
1287
1375
# check that the dead files were deleted
1288
1376
files = sorted(os.listdir(db.base_path))
1289
1377
self.assertEqual(3, len(files), files)
1290
self.assertIn(LIVE, files[0])
1291
self.assertIn(INACTIVE, files[1])
1292
self.assertIn(INACTIVE, files[2])
1378
self.assertIn(INACTIVE, files[0])
1379
self.assertIn(INACTIVE, files[1])
1380
self.assertIn(HINT, files[1])
1381
self.assertIn(LIVE, files[2])
1383
def test_merged_file_is_older_than_live(self):
1384
"""Test that merge creates and live file newer than the merge result.
1386
The expected side effect is that the live file should be newer than the
1387
merged immutable file.
1389
db = Tritcask(self.base_dir)
1390
self._add_data(db, 100)
1391
# delete almost all entrie to trigger a merge
1392
for i, k in enumerate(db._keydir.keys()):
1396
# trigger the rotation
1397
# now we should have a single immutable_file (previous live one)
1398
db = Tritcask(self.base_dir)
1399
self.assertEqual(1, len(db._immutable))
1400
dead_file = db._immutable.values()[0]
1401
self.assertIn(DEAD, dead_file.filename)
1402
files = sorted(os.listdir(db.base_path))
1403
self.assertEqual(4, len(files))
1404
self.assertIn(DEAD, files[0])
1405
self.assertIn(INACTIVE, files[1])
1293
1406
self.assertIn(HINT, files[2])
1407
self.assertIn(LIVE, files[3])
1408
self.assertEqual(db.live_file.size, 0)
1410
# at this moment we have the live + imm_file files
1411
# start a new Tritcask instance.
1412
db = Tritcask(self.base_dir)
1413
# the dead file should be fone
1414
# there should be only 2 files, the immutable and the live
1415
# immutable + hint + live
1416
files = sorted(os.listdir(db.base_path))
1417
self.assertEqual(3, len(files), files)
1418
self.assertIn(INACTIVE, files[0])
1419
self.assertIn(HINT, files[1])
1420
self.assertIn(LIVE, files[2])
1421
assert len(db._immutable) == 1, "More than 1 immutable file."
1422
live_id = int(db.live_file.file_id)
1423
imm_id = int(db._immutable.keys()[0])
1424
self.assertTrue(live_id > imm_id, "%d <= %d" % (live_id, imm_id))
1296
1427
class KeydirStatsTests(BaseTestCase):
1297
1428
"""Tests for the Keydir stats handling."""