/// <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);
        }
Exemple #2
0
        /// <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);
            }
        }
Exemple #3
0
        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");
        }
Exemple #4
0
        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}");
            }
        }