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(); } }