def _rm_block(self, uid: BlockUid) -> BlockUid: key = uid.storage_object_to_path() metadata_key = key + self._META_SUFFIX try: self._rm_object(key) except FileNotFoundError as exception: raise BlockNotFoundError('Block UID {} not found on storage.'.format(str(uid)), uid) from exception finally: try: self._rm_object(metadata_key) except FileNotFoundError: pass return uid
def test_write_rm_async(self): NUM_BLOBS = 15 BLOB_SIZE = 4096 saved_uids = list(self.storage.list_blocks()) self.assertEqual(0, len(saved_uids)) blocks = [ Block(uid=BlockUid(i + 1, i + 100), size=BLOB_SIZE, checksum='0000000000000000') for i in range(NUM_BLOBS) ] data_by_uid = {} for block in blocks: data = self.random_bytes(BLOB_SIZE) self.assertEqual(BLOB_SIZE, len(data)) self.storage.write_block_async(block, data) data_by_uid[block.uid] = data self.storage.wait_writes_finished() for _ in self.storage.write_get_completed(timeout=1): pass saved_uids = list(self.storage.list_blocks()) self.assertEqual(NUM_BLOBS, len(saved_uids)) uids_set = {block.uid for block in blocks} saved_uids_set = set(saved_uids) self.assertEqual(NUM_BLOBS, len(uids_set)) self.assertEqual(NUM_BLOBS, len(saved_uids_set)) self.assertEqual(0, len(uids_set.symmetric_difference(saved_uids_set))) for block in blocks: self.storage.read_block_async(block) for block, data, metadata in self.storage.read_get_completed( timeout=5): self.assertEqual(data_by_uid[block.uid], data) self.assertEqual( [], [future for future in self.storage.read_get_completed(timeout=5)]) for block in blocks: self.storage.rm_block_async(block.uid) self.storage.wait_rms_finished() saved_uids = list(self.storage.list_blocks()) self.assertEqual(0, len(saved_uids))
def test_not_exists(self): block = Block(uid=BlockUid(1, 2), size=15, checksum='00000000000000000000') self.storage.write_block(block, b'test_not_exists') data = self.storage.read_block(block) self.assertTrue(len(data) > 0) self.storage.rm_block(block.uid) self.assertRaises(BlockNotFoundError, lambda: self.storage.rm_block(block.uid)) self.assertRaises(InvalidBlockException, lambda: self.storage.read_block(block))
def test_storage_stats(self): NUM_BLOBS = 15 BLOB_SIZE = 4096 saved_uids = list(self.storage.list_blocks()) self.assertEqual(0, len(saved_uids)) blocks = [ Block(uid=BlockUid(i + 1, i + 100), size=BLOB_SIZE, checksum='0000000000000000') for i in range(NUM_BLOBS) ] for block in blocks: data = self.random_bytes(BLOB_SIZE) self.assertEqual(BLOB_SIZE, len(data)) self.storage.write_block(block, data) objects_count, objects_size = self.storage.storage_stats() logger.debug(f'Storage stats: {objects_count} objects using {objects_size} bytes.') self.assertEqual(NUM_BLOBS * 2, objects_count) # Also counts the metadata objects self.assertGreater(objects_size, 0) for block in blocks: self.storage.rm_block(block.uid)
def test_write_rm_sync(self): NUM_BLOBS = 15 BLOB_SIZE = 4096 saved_uids = list(self.storage.list_blocks()) self.assertEqual(0, len(saved_uids)) blocks = [ Block(uid=BlockUid(i + 1, i + 100), size=BLOB_SIZE, checksum='0000000000000000') for i in range(NUM_BLOBS) ] data_by_uid = {} for block in blocks: data = self.random_bytes(BLOB_SIZE) self.assertEqual(BLOB_SIZE, len(data)) self.storage.write_block(block, data) data_by_uid[block.uid] = data saved_uids = list(self.storage.list_blocks()) self.assertEqual(NUM_BLOBS, len(saved_uids)) uids_set = {block.uid for block in blocks} saved_uids_set = set(saved_uids) self.assertEqual(NUM_BLOBS, len(uids_set)) self.assertEqual(NUM_BLOBS, len(saved_uids_set)) self.assertEqual(0, len(uids_set.symmetric_difference(saved_uids_set))) for block in blocks: data = self.storage.read_block(block) self.assertEqual(data_by_uid[block.uid], data) for block in blocks: self.storage.rm_block(block.uid) saved_uids = list(self.storage.list_blocks()) self.assertEqual(0, len(saved_uids))
def test_block(self): version = self.database_backend.create_version( version_name='name-' + self.random_string(12), snapshot_name='snapshot-name-' + self.random_string(12), size=256 * 1024 * 4096, block_size=1024 * 4096, storage_id=1) checksums = [] uids = [] num_blocks = 256 blocks: List[Dict[str, Any]] = [] for id in range(num_blocks): checksums.append(self.random_hex(64)) uids.append(BlockUid(1, id)) blocks.append({ 'id': id, 'version_uid': version.uid, 'uid_left': uids[id].left, 'uid_right': uids[id].right, 'checksum': checksums[id], 'size': 1024 * 4096, 'valid': True }) self.database_backend.create_blocks(blocks=blocks) self.database_backend.commit() for id, checksum in enumerate(checksums): block = self.database_backend.get_block_by_checksum(checksum, 1) self.assertEqual(id, block.id) self.assertEqual(version.uid, block.version_uid) self.assertEqual(uids[id], block.uid) self.assertEqual(checksum, block.checksum) self.assertEqual(1024 * 4096, block.size) self.assertTrue(block.valid) for id, uid in enumerate(uids): block = self.database_backend.get_block(uid) self.assertEqual(id, block.id) self.assertEqual(version.uid, block.version_uid) self.assertEqual(uid, block.uid) self.assertEqual(checksums[id], block.checksum) self.assertEqual(1024 * 4096, block.size) self.assertTrue(block.valid) for id, uid in enumerate(uids): block = self.database_backend.get_block_by_id(version.uid, id) self.assertEqual(id, block.id) self.assertEqual(version.uid, block.version_uid) self.assertEqual(uid, block.uid) self.assertEqual(checksums[id], block.checksum) self.assertEqual(1024 * 4096, block.size) self.assertTrue(block.valid) blocks_iter = self.database_backend.get_blocks_by_version(version.uid) blocks_count = self.database_backend.get_blocks_count_by_version( version.uid) sparse_blocks_count = self.database_backend.get_blocks_count_by_version( version.uid, sparse_only=True) self.assertEqual(num_blocks, len(list(blocks_iter))) self.assertEqual(num_blocks, blocks_count) self.assertEqual(0, sparse_blocks_count) blocks_iter = self.database_backend.get_blocks_by_version(version.uid) for id, block in enumerate(blocks_iter): self.assertEqual(id, block.id) self.assertEqual(version.uid, block.version_uid) self.assertEqual(uids[id], block.uid) self.assertEqual(checksums[id], block.checksum) self.assertEqual(1024 * 4096, block.size) self.assertTrue(block.valid) blocks_iter = self.database_backend.get_blocks_by_version(version.uid) for id, block in enumerate(blocks_iter): dereferenced_block = block.deref() self.assertEqual(id, dereferenced_block.id) self.assertEqual(version.uid, dereferenced_block.version_uid) self.assertEqual(uids[id].left, dereferenced_block.uid.left) self.assertEqual(uids[id].right, dereferenced_block.uid.right) self.assertEqual(checksums[id], dereferenced_block.checksum) self.assertEqual(1024 * 4096, dereferenced_block.size) self.assertTrue(dereferenced_block.valid) self.database_backend.rm_version(version.uid) blocks_iter = self.database_backend.get_blocks_by_version(version.uid) blocks_count = self.database_backend.get_blocks_count_by_version( version.uid) self.assertEqual(0, len(list(blocks_iter))) self.assertEqual(0, blocks_count) deleted_count = 0 for uids_deleted in self.database_backend.get_delete_candidates(-1): for storage in uids_deleted.values(): for uid in storage: self.assertIn(uid, uids) deleted_count += 1 self.assertEqual(num_blocks, deleted_count)
def test_block(self): Storage.sync('s-1', storage_id=1) version = Version.create(version_uid=VersionUid('v1'), volume='name-' + self.random_string(12), snapshot='snapshot-name-' + self.random_string(12), size=256 * 1024 * 4096, block_size=1024 * 4096, storage_id=1) checksums = [] uids = [] num_blocks = 256 blocks: List[Dict[str, Any]] = [] for idx in range(num_blocks): checksums.append(self.random_hex(64)) uids.append(BlockUid(1, idx)) blocks.append({ 'idx': idx, 'uid_left': uids[idx].left, 'uid_right': uids[idx].right, 'checksum': checksums[idx], 'size': 1024 * 4096, 'valid': True }) version.create_blocks(blocks=blocks) for idx, checksum in enumerate(checksums): block = version.get_block_by_checksum(checksum) self.assertEqual(idx, block.idx) self.assertEqual(version.id, block.version_id) self.assertEqual(uids[idx], block.uid) self.assertEqual(checksum, block.checksum) self.assertEqual(1024 * 4096, block.size) self.assertTrue(block.valid) for idx, uid in enumerate(uids): block = version.get_block_by_idx(idx) self.assertEqual(idx, block.idx) self.assertEqual(version.id, block.version_id) self.assertEqual(uid, block.uid) self.assertEqual(checksums[idx], block.checksum) self.assertEqual(1024 * 4096, block.size) self.assertTrue(block.valid) self.assertEqual(num_blocks, len(list(version.blocks))) self.assertEqual(num_blocks, version.blocks_count) self.assertEqual(0, version.sparse_blocks_count) for idx, block in enumerate(version.blocks): self.assertEqual(idx, block.idx) self.assertEqual(version.id, block.version_id) self.assertEqual(uids[idx], block.uid) self.assertEqual(checksums[idx], block.checksum) self.assertEqual(1024 * 4096, block.size) self.assertTrue(block.valid) for idx, block in enumerate(version.blocks): dereferenced_block = block.deref() self.assertEqual(idx, dereferenced_block.idx) self.assertEqual(version.id, dereferenced_block.version_id) self.assertEqual(uids[idx].left, dereferenced_block.uid.left) self.assertEqual(uids[idx].right, dereferenced_block.uid.right) self.assertEqual(checksums[idx], dereferenced_block.checksum) self.assertEqual(1024 * 4096, dereferenced_block.size) self.assertTrue(dereferenced_block.valid) version.remove() deleted_count = 0 for uids_deleted in DeletedBlock.get_unused_block_uids(-1): for storage in uids_deleted.values(): for uid in storage: self.assertIn(uid, uids) deleted_count += 1 self.assertEqual(num_blocks, deleted_count)
def test_storage_usage(self): Storage.sync('s-1', storage_id=1) for i in range(2): version = Version.create(version_uid=VersionUid(f'v{i + 1}'), volume='backup-name', snapshot='snapshot-name.{}'.format(i), size=32 * 1024 * 4096, storage_id=1, block_size=1024 * 4096) blocks: List[Dict[str, Any]] = [] # shared for idx in range(0, 6): uid = BlockUid(1, idx) blocks.append({ 'idx': idx, 'uid_left': uid.left, 'uid_right': uid.right, 'checksum': None, 'size': 1024 * 4096, 'valid': True }) # sparse for idx in range(6, 13): uid = BlockUid(i + 1, idx) blocks.append({ 'idx': idx, 'uid_left': None, 'uid_right': None, 'checksum': None, 'size': 1024 * 4096, 'valid': True }) # exclusive for idx in range(13, 25): uid = BlockUid(i + 1, idx) blocks.append({ 'idx': idx, 'uid_left': uid.left, 'uid_right': uid.right, 'checksum': None, 'size': 1024 * 4096, 'valid': True }) # exclusive deduplicated for idx in range(25, 32): uid = BlockUid(i + 1, idx - 7) blocks.append({ 'idx': idx, 'uid_left': uid.left, 'uid_right': uid.right, 'checksum': None, 'size': 1024 * 4096, 'valid': True }) version.create_blocks(blocks=blocks) for uid in ('v1', 'v2'): usage = Version.storage_usage(f'uid == "{uid}"') self.assertIsInstance(usage.get('s-1', None), dict) usage_s_1 = usage['s-1'] check_value_matrix = ( ('virtual', 32 * 1024 * 4096), ('shared', 6 * 1024 * 4096), ('sparse', 7 * 1024 * 4096), ('exclusive', 19 * 1024 * 4096), ('deduplicated_exclusive', (19 - 7) * 1024 * 4096), ) for field, amount in check_value_matrix: value = usage_s_1.get(field, None) self.assertIsInstance(value, int) self.assertEqual(amount, value)
def test_block(self): version = self.database_backend.create_version( version_name='name-' + self.random_string(12), snapshot_name='snapshot-name-' + self.random_string(12), size=256 * 1024 * 4096, block_size=1024 * 4096, storage_id=1) self.database_backend.commit() checksums = [] uids = [] num_blocks = 256 for id in range(num_blocks): checksums.append(self.random_hex(64)) uids.append(BlockUid(1, id)) self.database_backend.set_block(id=id, version_uid=version.uid, block_uid=uids[id], checksum=checksums[id], size=1024 * 4096, valid=True) self.database_backend.commit() for id, checksum in enumerate(checksums): block = self.database_backend.get_block_by_checksum(checksum, 1) self.assertEqual(id, block.id) self.assertEqual(version.uid, block.version_uid) self.assertEqual(uids[id], block.uid) self.assertEqual(checksum, block.checksum) self.assertEqual(1024 * 4096, block.size) self.assertTrue(block.valid) for id, uid in enumerate(uids): block = self.database_backend.get_block(uid) self.assertEqual(id, block.id) self.assertEqual(version.uid, block.version_uid) self.assertEqual(uid, block.uid) self.assertEqual(checksums[id], block.checksum) self.assertEqual(1024 * 4096, block.size) self.assertTrue(block.valid) blocks = self.database_backend.get_blocks_by_version(version.uid) self.assertEqual(num_blocks, len(blocks)) for id, block in enumerate(blocks): self.assertEqual(id, block.id) self.assertEqual(version.uid, block.version_uid) self.assertEqual(uids[id], block.uid) self.assertEqual(checksums[id], block.checksum) self.assertEqual(1024 * 4096, block.size) self.assertTrue(block.valid) for id, block in enumerate(blocks): dereferenced_block = block.deref() self.assertEqual(id, dereferenced_block.id) self.assertEqual(version.uid, dereferenced_block.version_uid) self.assertEqual(uids[id].left, dereferenced_block.uid.left) self.assertEqual(uids[id].right, dereferenced_block.uid.right) self.assertEqual(checksums[id], dereferenced_block.checksum) self.assertEqual(1024 * 4096, dereferenced_block.size) self.assertTrue(dereferenced_block.valid) self.database_backend.rm_version(version.uid) self.database_backend.commit() blocks = self.database_backend.get_blocks_by_version(version.uid) self.assertEqual(0, len(blocks)) count = 0 for uids_deleted in self.database_backend.get_delete_candidates(-1): for storage in uids_deleted.values(): for uid in storage: self.assertIn(uid, uids) count += 1 self.assertEqual(num_blocks, count)