public IEntryState <TId, TData> Store(IEntryState <TId, TData> entry, TData data, long transactionId) { if (entry == null) { throw new ArgumentNullException(nameof(entry)); } if (!entry.PendingTransactions.Contains(transactionId)) { throw new InvalidOperationException(); } if (entry.PendingOperations.Any(p => p.TransactionId == transactionId)) { throw new InvalidOperationException("A single transaction must modify an entry with a single operation only."); } var lastWriteTime = _dateTimeProvider.GetCurrentTime(); var pendingOperation = new PendingOperation(transactionId, originalData: ToSnapshot(entry), lastWriteTime); var pendingOperations = entry.PendingOperations.Add(pendingOperation); return(new Entry(entry.Id, data, entry.DataVersion + 1, entry.Version + 1, entry.CreatingTransaction, pendingOperations, entry.PendingTransactions, entry.CreationTime, lastWriteTime)); }
public IEntryState <TId, TData> AddPendingTransaction(IEntryState <TId, TData> entry, long transactionId) { if (entry == null) { throw new ArgumentNullException(nameof(entry)); } if (entry.PendingTransactions.Contains(transactionId)) { return(entry); } var lastWriteTime = _dateTimeProvider.GetCurrentTime(); var pendingTransactions = entry.PendingTransactions.Add(transactionId); return(new Entry(entry.Id, entry.Data, entry.DataVersion, entry.Version + 1, entry.CreatingTransaction, entry.PendingOperations, pendingTransactions, entry.CreationTime, lastWriteTime)); }
public IEntryState <TId, TData> Commit(IEntryState <TId, TData> entry, long transactionId) { if (entry == null) { throw new ArgumentNullException(nameof(entry)); } var lastWriteTime = _dateTimeProvider.GetCurrentTime(); var pendingOperations = entry.PendingOperations.RemoveAll(op => op.TransactionId == transactionId); var pendingTransactions = entry.PendingTransactions.Remove(transactionId); entry = new Entry(entry.Id, entry.Data, entry.DataVersion, entry.Version + 1, entry.CreatingTransaction, pendingOperations, pendingTransactions, entry.CreationTime, lastWriteTime); Assert(!entry.PendingOperations.Any(p => p.TransactionId == transactionId)); return(entry); }
public IEntrySnapshot <TId, TData> ToSnapshot(IEntryState <TId, TData> entry) { if (entry == null) { throw new ArgumentNullException(nameof(entry)); } return(new EntrySnapshot(entry.Id, entry.Data, entry.DataVersion, entry.LastWriteTime)); }
private static StoredEntry AsStoredEntry(IEntryState <TId, TData> entry) { if (entry == null) { return(null); } if (entry is StoredEntry storedEntry) { return(storedEntry); } return(new StoredEntry(entry)); }
public Entry(IEntryState <TId, TData> entry) { Assert(entry.Data != null); Assert(entry.PendingOperations != null); Assert(entry.CreationTime <= entry.LastWriteTime); Id = entry.Id; Data = entry.Data; DataVersion = entry.DataVersion; Version = entry.Version; CreatingTransaction = entry.CreatingTransaction; PendingOperations = entry.PendingOperations; CreationTime = entry.CreationTime; LastWriteTime = entry.LastWriteTime; PendingTransactions = entry.PendingTransactions; }
public StoredEntry(IEntryState <TId, TData> entry) { DataVersion = entry.DataVersion; LastWriteTime = entry.LastWriteTime; Version = entry.Version; CreationTime = entry.CreationTime; CreatingTransaction = entry.CreatingTransaction; PendingOperations = new List <StoredPendingOperation>(entry.PendingOperations.Select(p => AsStoredPendingOperation(p))); for (var i = 0; i < entry.PendingOperations.Count; i++) { Assert(entry.PendingOperations[i].TransactionId == PendingOperations[i].TransactionId); } PendingTransactions = new List <long>(entry.PendingTransactions); Id = entry.Id; Data = entry.Data; }
public static IEntryState <TId, TData> CommitAll <TId, TData>(this IEntryStateTransformer <TId, TData> entryManager, IEntryState <TId, TData> entry, IEnumerable <long> transactionIds) where TData : class { if (entryManager == null) { throw new ArgumentNullException(nameof(entryManager)); } if (transactionIds == null) { throw new ArgumentNullException(nameof(transactionIds)); } return(transactionIds.Aggregate(seed: entry, (current, transactionId) => entryManager.Commit(current, transactionId))); }
public static ValueTask <(IEntryState <TId, TData> entry, T result)> UpdateEntryAsync <TId, TData, T>(this IEntryStateStorage <TId, TData> entryStorage, IEntryState <TId, TData> entry, Func <IEntryState <TId, TData>, (IEntryState <TId, TData> entry, T result)> update,
public static async ValueTask <IEntryState <TId, TData> > UpdateEntryAsync <TId, TData>(this IEntryStateStorage <TId, TData> entryStorage, IEntryState <TId, TData> entry, Func <IEntryState <TId, TData>, IEntryState <TId, TData> > update, Func <IEntryState <TId, TData>, bool> condition, CancellationToken cancellation) where TData : class { if (entryStorage == null) { throw new ArgumentNullException(nameof(entryStorage)); } if (entry == null) { throw new ArgumentNullException(nameof(entry)); } if (update == null) { throw new ArgumentNullException(nameof(update)); } if (condition == null) { throw new ArgumentNullException(nameof(condition)); } var id = entry.Id; while (condition(entry)) { var desired = update(entry); if (desired == entry) { return(entry); } if (await entryStorage.CompareExchangeAsync(desired, entry, cancellation)) { return(entry = desired); } entry = await entryStorage.GetEntryAsync(id, cancellation); if (entry == null) { return(null); } } return(entry); }
public static ValueTask <IEntryState <TId, TData> > UpdateEntryAsync <TId, TData>(this IEntryStateStorage <TId, TData> entryStorage, IEntryState <TId, TData> entry, Func <IEntryState <TId, TData>, IEntryState <TId, TData> > update, CancellationToken cancellation) where TData : class { return(UpdateEntryAsync(entryStorage, entry, update, condition: e => true, cancellation)); }
public static async ValueTask <IEntryState <TId, TData> > GetEntryAsync <TId, TData>(this IEntryStateStorage <TId, TData> entryStorage, IEntryState <TId, TData> comparand, CancellationToken cancellation = default) where TData : class { if (entryStorage == null) { throw new ArgumentNullException(nameof(entryStorage)); } if (comparand == null) { throw new ArgumentNullException(nameof(comparand)); } var entries = await entryStorage.GetEntriesAsync(DataPropertyHelper.BuildPredicate(comparand), cancellation); return(entries.FirstOrDefault()); }
public Task <bool> CompareExchangeAsync(IEntryState <TId, TData> entry, IEntryState <TId, TData> comparand, CancellationToken cancellation = default) { return(_database.CompareExchangeAsync(AsStoredEntry(entry), AsStoredEntry(comparand), p => p.Version, cancellation)); }
public IEntryState <TId, TData> Abort(IEntryState <TId, TData> entry, long transactionId) { if (entry == null) { throw new ArgumentNullException(nameof(entry)); } var pendingTransactions = entry.PendingTransactions.Remove(transactionId); var i = entry.PendingOperations.FindIndex(p => p.TransactionId == transactionId); // We abort the operation that happend last if (i >= 0 && i == entry.PendingOperations.Count() - 1) { var originalData = entry.PendingOperations[i].OriginalData; entry = new Entry(entry.Id, originalData.Data, originalData.DataVersion, entry.Version + 1, entry.CreatingTransaction, entry.PendingOperations.RemoveAt(entry.PendingOperations.Count - 1), pendingTransactions, entry.CreationTime, originalData.LastWriteTime ?? entry.CreationTime); } else if (i >= 0) { #if DEBUG Assert(entry.PendingOperations.Count(p => p.TransactionId == transactionId) == 1); #endif var pendingOperations = entry.PendingOperations; var originalData = pendingOperations[i].OriginalData; pendingOperations = pendingOperations.RemoveAt(i); // The operations after the removed one now have an index that is one smaller var operationToReplace = pendingOperations[i]; // index i + 1 is now index i var replacement = new PendingOperation(operationToReplace.TransactionId, originalData, operationToReplace.OperationTime); pendingOperations = pendingOperations.Replace(operationToReplace, replacement); entry = new Entry(entry.Id, entry.Data, entry.DataVersion, entry.Version + 1, entry.CreatingTransaction, pendingOperations, pendingTransactions, entry.CreationTime, entry.LastWriteTime); } else if (entry.PendingTransactions.Contains(transactionId)) { entry = new Entry(entry.Id, entry.Data, entry.DataVersion, entry.Version + 1, entry.CreatingTransaction, entry.PendingOperations, pendingTransactions, entry.CreationTime, entry.LastWriteTime); } Assert(!entry.PendingOperations.Any(p => p.TransactionId == transactionId)); return(entry); }