private static void NotifyResourceOfCommit(ITransactionalResource resource, long transaction, MultiCompletionSource completionSource) { resource.Commit(transaction) .ContinueWith( (result, state) => { var completion = (MultiCompletionSource)state; if (result.Exception != null) { completion.SetException(result.Exception); } else { completion.SetOneResult(); } }, completionSource, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); }
private async Task <bool> Checkpoint(Dictionary <ITransactionalResource, long> resources, List <Transaction> transactions) { // Rather than continue processing forever, only process the number of transactions which were waiting at invocation time. var total = this.checkpointRetryQueue.Count + checkpointQueue.Count; // The maximum number of transactions checkpointed in each batch. int batchSize = Math.Min(total, MaxCheckpointBatchSize); long lsn = 0; try { var processed = 0; while (processed < total && (this.checkpointRetryQueue.Count > 0 || !checkpointQueue.IsEmpty)) { resources.Clear(); transactions.Clear(); // Take a batch of transactions to checkpoint. var currentBatchSize = 0; while (currentBatchSize < batchSize) { currentBatchSize++; Transaction tx; // If some previous operation had failed, retry it before proceeding with new work. if (this.checkpointRetryQueue.Count > 0) { tx = this.checkpointRetryQueue.Dequeue(); } else if (!checkpointQueue.TryDequeue(out tx)) { break; } foreach (var resource in tx.Info.WriteSet.Keys) { resources[resource] = tx.Info.TransactionId; } lsn = Math.Max(lsn, tx.LSN); transactions.Add(tx); } processed += currentBatchSize; // If the transaction involved writes, send a commit notification to each resource which performed // a write and wait for acknowledgement. if (resources.Count > 0) { // Send commit notifications to all of the resources involved. var completion = new MultiCompletionSource(resources.Count); foreach (var resource in resources) { NotifyResourceOfCommit(resource.Key, resource.Value, completion); } // Wait for the commit notifications to be acknowledged by the resources. await completion.Task; } // Mark the transactions as checkpointed. foreach (var tx in transactions) { lock (tx) { tx.State = TransactionState.Checkpointed; tx.HighestActiveTransactionIdAtCheckpoint = activeTransactionsTracker.GetHighestActiveTransactionId(); } } // Allow the transaction log to be truncated. this.checkpointedLSN = lsn; } } catch (Exception e) { // Retry all failed checkpoint operations. foreach (var tx in transactions) { this.checkpointRetryQueue.Enqueue(tx); } this.Logger.Error(0, "Failure during checkpoint", e); throw; } return(total > 0); }