/// <summary> /// Add a new bloc or update an existing one (depending if the primary key is already stored) /// </summary> /// <param name="data"></param> /// <param name="primaryKey"></param> /// <param name="transactionId"></param> public void StoreBlock(byte[] data, string primaryKey, int transactionId) { var block = new PersistentBlock { RawData = data, PrimaryKey = primaryKey, BlockStatus = BlockStatus.Active, LastTransactionId = transactionId, UsedDataSize = data.Length }; if (BlockInfoByPrimaryKey.TryGetValue(primaryKey, out var blockInfo)) { // load the old version of the block StorageStream.Seek(blockInfo.Offset, SeekOrigin.Begin); var reader = new BinaryReader(StorageStream); var oldBlock = new PersistentBlock(); oldBlock.Read(reader); // if enough space is available do in-place update if (oldBlock.ReservedDataSize > block.UsedDataSize) { block.ReservedDataSize = oldBlock.ReservedDataSize; WriteBlock(block, blockInfo.Offset); } else // the old block is marked as deleted and the new version is added at the end of the stream { oldBlock.BlockStatus = BlockStatus.Dirty; InactiveBlockCount++; WriteBlock(oldBlock, blockInfo.Offset); block.ReservedDataSize = (int)(block.UsedDataSize * 1.5); StorageSize = WriteBlock(block, StorageSize); BlockInfoByPrimaryKey[primaryKey] = new BlockInfo(StorageSize, block.LastTransactionId); } } else // a new object not already in the persistent storage { // reserve some more space to allow for in-place updating block.ReservedDataSize = (int)(block.UsedDataSize * 1.5); var offset = StorageSize; StorageSize = WriteBlock(block, StorageSize); BlockInfoByPrimaryKey[primaryKey] = new BlockInfo(offset, block.LastTransactionId); } _backupStorage?.StoreBlock(data, primaryKey, transactionId); }
/// <summary> /// For recovery tests only /// </summary> /// <param name="primaryKey"></param> public void MakeCorruptedBlock(string primaryKey) { if (BlockInfoByPrimaryKey.TryGetValue(primaryKey, out var info)) { var block = new PersistentBlock(); StorageStream.Seek(info.Offset, SeekOrigin.Begin); var reader = new BinaryReader(StorageStream); block.Read(reader); block.Hash = block.Hash + 1; // the hash will not match so the block is corrupted StorageStream.Seek(info.Offset, SeekOrigin.Begin); var writer = new BinaryWriter(StorageStream); block.Write(writer); } }
public PersistentBlock ReadBlock(string primaryKey) { if (BlockInfoByPrimaryKey.TryGetValue(primaryKey, out var info)) { var block = new PersistentBlock(); StorageStream.Seek(info.Offset, SeekOrigin.Begin); var reader = new BinaryReader(StorageStream); block.Read(reader); return(block); } throw new NotSupportedException("primary key not found in backup storage"); }
public void DeleteBlock(string primaryKey, int transactionId) { if (BlockInfoByPrimaryKey.TryGetValue(primaryKey, out var blockInfo)) { StorageStream.Seek(blockInfo.Offset, SeekOrigin.Begin); var reader = new BinaryReader(StorageStream); var block = new PersistentBlock(); block.Read(reader); if (block.BlockStatus != BlockStatus.Active) { // if the same id ok. We are re executing a transaction that was not marked as Processed. // It happens if the server crashes during the update of persistence blocks. The transaction is simply played // again when the server is restarted if (block.LastTransactionId != transactionId) { throw new NotSupportedException( $"Trying to delete an inactive block for primary key {primaryKey}"); } } block.BlockStatus = BlockStatus.Deleted; block.LastTransactionId = transactionId; StorageStream.Seek(blockInfo.Offset, SeekOrigin.Begin); var writer = new BinaryWriter(StorageStream); block.Write(writer); writer.Flush(); BlockInfoByPrimaryKey.Remove(primaryKey); InactiveBlockCount++; _backupStorage?.DeleteBlock(primaryKey, transactionId); } else { throw new NotSupportedException($"Active block not found for primary key {primaryKey}"); } }