public async Task Store(CapturedTransportOperation operation, Action triggerAdvance, SqlConnection conn, SqlTransaction trans) { var localState = linkState; var seq = await sequence.GetNextValue(conn, trans).ConfigureAwait(false); InterlocedEx.ExchangeIfGreaterThan(ref highestSeq, seq); if (localState.IsStale(seq)) { var freshLinkState = await linkStateTable.Get(operation.Destination, conn, trans).ConfigureAwait(false); UpdateCachedLinkState(freshLinkState); localState = linkState; } if (localState.ShouldAdvance(seq)) { triggerAdvance(); } if (localState.IsStale(seq)) { throw new ProcessCurrentMessageLaterException("Link state is stale. Processing current message later."); } var tableName = localState.GetTableName(seq); var table = new OutboxTable(tableName); try { operation.OutgoingMessage.Headers[RouterDeduplicationHeaders.SequenceNumber] = seq.ToString(); operation.OutgoingMessage.Headers[RouterDeduplicationHeaders.SequenceKey] = sourceKey; operation.AssignTable(tableName); operation.AssignSequence(seq); var persistentOperation = Convert(operation); await table.Insert(persistentOperation, seq, conn, trans).ConfigureAwait(false); } catch (SqlException e) { if (e.Number == 547) //Constraint violation. We used very old seq and that value cannot be used any more because the epoch has advanced. { var freshLinkState = await linkStateTable.Get(operation.Destination, conn, trans).ConfigureAwait(false); UpdateCachedLinkState(freshLinkState); throw new ProcessCurrentMessageLaterException("Link state is stale. Processing current message later."); } throw; } catch (Exception ex) { log.Debug($"Unhandled exception while storing outbox operation with sequence {seq}", ex); throw; } }
public async Task Uninstall(string destinationKey, SqlConnection conn, SqlTransaction trans) { var leftTable = new OutboxTable(OutboxTable.Left(sourceKey, destinationKey)); var rightTable = new OutboxTable(OutboxTable.Right(sourceKey, destinationKey)); var sequence = new OutboxSequence(sourceKey, destinationKey); var linkStateTable = new LinkStateTable(sourceKey); await linkStateTable.Drop(conn, trans).ConfigureAwait(false); await leftTable.Drop(conn, trans).ConfigureAwait(false); await rightTable.Drop(conn, trans).ConfigureAwait(false); await sequence.Drop(conn, trans).ConfigureAwait(false); }
async Task <LinkState> InitializeLinkState(LinkState lockedLinkState, SqlConnection conn, SqlTransaction initTransaction) { if (!lockedLinkState.Initialized) { var initializedState = lockedLinkState.Initialize( OutboxTable.Left(sourceKey, destinationKey), OutboxTable.Right(sourceKey, destinationKey), epochSize); await initializedState.HeadSession.CreateConstraint(conn, initTransaction); await initializedState.TailSession.CreateConstraint(conn, initTransaction); await linkStateTable.Update(destinationKey, initializedState, conn, initTransaction).ConfigureAwait(false); return(initializedState); } return(lockedLinkState); }
async Task <LinkState> Advance(Func <OutgoingMessage, Task> dispatch, SqlConnection conn) { //Let's actually check if our values are correct. var queriedLinkState = await linkStateTable.Get(destinationKey, conn); if (!queriedLinkState.ShouldAdvance(highestSeq)) { return(queriedLinkState); } log.Debug($"Attempting advance epoch for destination {destinationKey} based on link state {linkState}."); if (!queriedLinkState.IsEpochAnnounced) { return(await AnnounceAdvance(queriedLinkState, dispatch, conn).ConfigureAwait(false)); } var tableName = queriedLinkState.TailSession.Table; var table = new OutboxTable(tableName); if (await table.HasHoles(queriedLinkState.TailSession, conn).ConfigureAwait(false)) { var holes = await table.FindHoles(queriedLinkState.TailSession, conn).ConfigureAwait(false); if (holes.Any()) { await PlugHoles(table, holes, dispatch, conn).ConfigureAwait(false); } } var(advanced, newState) = await TryAdvanceEpochInDatabase(conn, tableName, queriedLinkState); if (advanced) { return(await AnnounceAdvance(newState, dispatch, conn).ConfigureAwait(false)); } return(newState); }
public Task DropConstraint(SqlConnection conn, SqlTransaction trans) { var table = new OutboxTable(Table); return(table.DropConstraint(Lo, Hi, conn, trans)); }
async Task PlugHoles(OutboxTable table, List <(long Id, HoleType Type)> holes, Func <OutgoingMessage, Task> dispatch, SqlConnection conn)