internal static IEnumerable <ReplayProgress> Replay(DocumentDatabase database, Stream replayStream) { DocumentsOperationContext txCtx = null; IDisposable txDisposable = null; DocumentsTransaction previousTx = null; using (database.DocumentsStorage.ContextPool.AllocateOperationContext(out DocumentsOperationContext context)) using (context.GetManagedBuffer(out var buffer)) using (var gZipStream = new GZipStream(replayStream, CompressionMode.Decompress, leaveOpen: true)) { var peepingTomStream = new PeepingTomStream(gZipStream, context); var state = new JsonParserState(); var parser = new UnmanagedJsonParser(context, state, "file"); var commandsProgress = 0; var readers = UnmanagedJsonParserHelper.ReadArrayToMemory(context, peepingTomStream, parser, state, buffer); using (var readersItr = readers.GetEnumerator()) { ReadStartRecordingDetails(readersItr, context, peepingTomStream); while (readersItr.MoveNext()) { using (readersItr.Current) { if (readersItr.Current.TryGet(nameof(RecordingDetails.Type), out string strType) == false) { throw new ReplayTransactionsException($"Can't read {nameof(RecordingDetails.Type)} of replay detail", peepingTomStream); } if (Enum.TryParse <TxInstruction>(strType, true, out var type)) { switch (type) { case TxInstruction.BeginTx: txDisposable = database.DocumentsStorage.ContextPool.AllocateOperationContext(out txCtx); txCtx.OpenWriteTransaction(); break; case TxInstruction.Commit: txCtx.Transaction.Commit(); break; case TxInstruction.DisposeTx: txDisposable.Dispose(); break; case TxInstruction.BeginAsyncCommitAndStartNewTransaction: previousTx = txCtx.Transaction; txCtx.Transaction = txCtx.Transaction.BeginAsyncCommitAndStartNewTransaction(txCtx); txDisposable = txCtx.Transaction; break; case TxInstruction.EndAsyncCommit: previousTx.EndAsyncCommit(); break; case TxInstruction.DisposePrevTx: previousTx.Dispose(); break; } continue; } try { var cmd = DeserializeCommand(context, database, strType, readersItr.Current, peepingTomStream); commandsProgress += cmd.ExecuteDirectly(txCtx); TransactionOperationsMerger.UpdateGlobalReplicationInfoBeforeCommit(txCtx); } catch (Exception) { //TODO To accept exceptions that was thrown while recording txDisposable.Dispose(); throw; } yield return(new ReplayProgress { CommandsProgress = commandsProgress }); } } } } }
public void Dispose() { TxCtx?.Dispose(); PrevTx?.Dispose(); }
private void MergeTransactionsOnce() { var pendingOps = GetBufferForPendingOps(); using (_parent.DocumentsStorage.ContextPool.AllocateOperationContext(out DocumentsOperationContext context)) { DocumentsTransaction tx = null; try { try { tx = context.OpenWriteTransaction(); } catch (Exception e) { if (_operations.TryDequeue(out MergedTransactionCommand command)) { command.Exception = e; DoCommandNotification(command); } return; } PendingOperations result; try { var transactionMeter = TransactionPerformanceMetrics.MeterPerformanceRate(); try { result = ExecutePendingOperationsInTransaction(pendingOps, context, null, ref transactionMeter); UpdateGlobalReplicationInfoBeforeCommit(context); } finally { transactionMeter.Dispose(); } } catch (Exception e) { if (_log.IsInfoEnabled) { _log.Info( $"Failed to run merged transaction with {pendingOps.Count:#,#}, will retry independently", e); } tx.Dispose(); NotifyTransactionFailureAndRerunIndependently(pendingOps, e); return; } switch (result) { case PendingOperations.CompletedAll: case PendingOperations.ModifiedSystemDocuments: try { tx.Commit(); tx.Dispose(); } catch (Exception e) { foreach (var op in pendingOps) { op.Exception = e; } } finally { NotifyOnThreadPool(pendingOps); } return; case PendingOperations.HasMore: MergeTransactionsWithAsyncCommit(context, pendingOps); return; default: Debug.Assert(false, "Should never happen"); return; } } finally { tx?.Dispose(); } } }
private void MergeTransactionsOnce() { DocumentsOperationContext context = null; IDisposable returnContext = null; DocumentsTransaction tx = null; try { var pendingOps = GetBufferForPendingOps(); returnContext = _parent.DocumentsStorage.ContextPool.AllocateOperationContext(out context); { try { _recording.State?.Record(context, TxInstruction.BeginTx); tx = context.OpenWriteTransaction(); } catch (Exception e) { try { if (_operations.TryDequeue(out MergedTransactionCommand command)) { command.Exception = e; DoCommandNotification(command); } return; } finally { if (tx != null) { _recording.State?.Record(context, TxInstruction.DisposeTx, tx.Disposed == false); tx.Dispose(); } } } PendingOperations result; try { var transactionMeter = TransactionPerformanceMetrics.MeterPerformanceRate(); try { result = ExecutePendingOperationsInTransaction(pendingOps, context, null, ref transactionMeter); UpdateGlobalReplicationInfoBeforeCommit(context); } finally { transactionMeter.Dispose(); } } catch (Exception e) { // need to dispose here since we are going to open new tx for each operation if (tx != null) { _recording.State?.Record(context, TxInstruction.DisposeTx, tx.Disposed == false); tx.Dispose(); } if (e is HighDirtyMemoryException highDirtyMemoryException) { if (_log.IsInfoEnabled) { var errorMessage = $"{pendingOps.Count:#,#0} operations were cancelled because of high dirty memory, details: {highDirtyMemoryException.Message}"; _log.Info(errorMessage, highDirtyMemoryException); } NotifyHighDirtyMemoryFailure(pendingOps, highDirtyMemoryException); } else { if (_log.IsInfoEnabled) { _log.Info($"Failed to run merged transaction with {pendingOps.Count:#,#0}, will retry independently", e); } NotifyTransactionFailureAndRerunIndependently(pendingOps, e); } return; } switch (result) { case PendingOperations.CompletedAll: try { tx.InnerTransaction.LowLevelTransaction.RetrieveCommitStats(out var stats); _recording.State?.Record(context, TxInstruction.Commit); tx.Commit(); SlowWriteNotification.Notify(stats, _parent); _recording.State?.Record(context, TxInstruction.DisposeTx, tx.Disposed == false); tx.Dispose(); } catch (Exception e) { foreach (var op in pendingOps) { op.Exception = e; } } finally { NotifyOnThreadPool(pendingOps); } return; case PendingOperations.HasMore: MergeTransactionsWithAsyncCommit(ref context, ref returnContext, pendingOps); return; default: Debug.Assert(false, "Should never happen"); return; } } } finally { if (context?.Transaction != null) { using (_parent.DocumentsStorage.ContextPool.AllocateOperationContext(out DocumentsOperationContext ctx)) { _recording.State?.Record(ctx, TxInstruction.DisposeTx, context.Transaction.Disposed == false); } context.Transaction.Dispose(); } returnContext?.Dispose(); } }