private async Task <MongoDbTransaction> GetLastTransactionInternal() { // look for potential last transaction var res = await TransactionCollection.Find(x => true) .SortByDescending(x => x.Timestamp) .FirstOrDefaultAsync(); if (res == null) { return(null); } // look if a pending transaction var resp = await PendingTransactionCollection.Find(x => true).SortByDescending(x => x.Timestamp).FirstOrDefaultAsync(); if (resp == null) // no pending transaction { // return last transaction return(res); } else { // else return last transaction no younger than pending transaction res = await TransactionCollection.Find(x => x.Timestamp < resp.Timestamp) .SortByDescending(x => x.Timestamp) .FirstOrDefaultAsync(); return(res); } }
public async Task RollbackAllPendingTransactions(DateTime limit) { var res = await PendingTransactionCollection.Find(x => x.LockTimestamp < limit).ToListAsync(); foreach (var t in res) { await RollbackTransaction(t.TransactionHash); } }
private async Task RollbackTransaction(byte[] hash) { // Rollback is idempotent && reentrant : may be call twice even at the same time try { #if DEBUG Logger.LogDebug($"Rollbacking transaction {new ByteString(hash)}"); #endif // get affected records var trn = await PendingTransactionCollection.Find(x => x.TransactionHash.Equals(hash)).SingleOrDefaultAsync(); if (trn != null) { // revert records values & version foreach (var r in trn.InitialRecords) { await RecordCollection.FindOneAndUpdateAsync( x => x.Key.Equals(r.Key) && x.TransactionLock.Equals(trn.LockToken), Builders <MongoDbRecord> .Update.Set(x => x.Value, r.Value).Set(x => x.Version, r.Version).Unset(x => x.TransactionLock) ); } foreach (var r in trn.AddedRecords) { await RecordCollection.FindOneAndDeleteAsync( x => x.Key.Equals(r) && x.TransactionLock.Equals(trn.LockToken) ); } // remove transaction await TransactionCollection.DeleteOneAsync(x => x.TransactionHash.Equals(hash)); await RecordCollection.UpdateOneAsync( x => x.TransactionLock == trn.LockToken, Builders <MongoDbRecord> .Update .Unset(x => x.TransactionLock) ); // remove pending transaction await PendingTransactionCollection.DeleteOneAsync(x => x.TransactionHash.Equals(hash)); Logger.LogInformation($"Transaction {new ByteString(hash)} rollbacked"); } } catch (Exception ex) { var msg = "Error rollbacking transaction : " + new ByteString(hash).ToString(); Logger.LogCritical(msg, ex); throw new Exception(msg, ex); } }