< Summary

Information
Class: AsiBackbone.Storage.InMemory.Outbox.InMemoryGovernanceOutboxStore
Assembly: AsiBackbone.Storage.InMemory
File(s): /home/runner/work/AsiBackbone/AsiBackbone/src/AsiBackbone.Storage.InMemory/Outbox/InMemoryGovernanceOutboxStore.cs
Line coverage
95%
Covered lines: 166
Uncovered lines: 8
Coverable lines: 174
Total lines: 476
Line coverage: 95.4%
Branch coverage
75%
Covered branches: 45
Total branches: 60
Branch coverage: 75%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor()100%11100%
EnqueueAsync(...)50%22100%
SaveAsync(...)100%11100%
FindByOutboxEntryIdAsync(...)100%11100%
FindPendingAsync(...)100%11100%
FindRetryReadyAsync(...)100%11100%
ClaimPendingAsync(...)100%11100%
ClaimRetryReadyAsync(...)100%11100%
MarkDeliveredAsync(...)100%11100%
MarkClaimDeliveredAsync(...)100%11100%
MarkFailedAsync(...)100%11100%
MarkClaimFailedAsync(...)100%11100%
MarkDeadLetteredAsync(...)100%11100%
MarkClaimDeadLetteredAsync(...)100%11100%
SaveClaimAsync(...)50%2290%
ReleaseClaimAsync(...)100%11100%
ClaimEntries(...)100%66100%
TryClaimEntry(...)66.67%6681.82%
SaveEntry(...)75%9875%
UpdateExistingEntry(...)66.67%6688.89%
UpdateClaimedEntry(...)80%111080%
ReleaseClaim(...)100%88100%
CreateClaim(...)50%88100%
IsTerminal(...)100%22100%
NormalizeMaxCount(...)50%22100%

File(s)

/home/runner/work/AsiBackbone/AsiBackbone/src/AsiBackbone.Storage.InMemory/Outbox/InMemoryGovernanceOutboxStore.cs

#LineLine coverage
 1using System.Collections.Concurrent;
 2using AsiBackbone.Core.Emissions;
 3using AsiBackbone.Core.Outbox;
 4
 5namespace AsiBackbone.Storage.InMemory.Outbox;
 6
 7/// <summary>
 8/// In-memory outbox store for tests, samples, and development hosts.
 9/// </summary>
 10/// <remarks>
 11/// This store is not durable across process restarts. Production hosts should use a durable provider such as EF Core or
 12/// Same-entry status transitions use single-process compare-and-swap updates so tests and local validation do not accid
 13/// </remarks>
 14public sealed class InMemoryGovernanceOutboxStore : IGovernanceOutboxClaimStore
 15{
 5816    private readonly ConcurrentDictionary<string, GovernanceOutboxEntry> entries = new(StringComparer.Ordinal);
 17
 18    /// <inheritdoc />
 19    public ValueTask<GovernanceOutboxEntry> EnqueueAsync(
 20        GovernanceEmissionEnvelope envelope,
 21        CancellationToken cancellationToken = default)
 22    {
 5523        ArgumentNullException.ThrowIfNull(envelope);
 5524        cancellationToken.ThrowIfCancellationRequested();
 25
 5526        var entry = GovernanceOutboxEntry.Create(envelope);
 27
 5528        return !entries.TryAdd(entry.OutboxEntryId, entry)
 5529            ? throw new InvalidOperationException($"Outbox entry '{entry.OutboxEntryId}' already exists.")
 5530            : ValueTask.FromResult(entry);
 31    }
 32
 33    /// <inheritdoc />
 34    public ValueTask<GovernanceOutboxEntry> SaveAsync(
 35        GovernanceOutboxEntry entry,
 36        CancellationToken cancellationToken = default)
 37    {
 238        ArgumentNullException.ThrowIfNull(entry);
 239        cancellationToken.ThrowIfCancellationRequested();
 40
 241        return ValueTask.FromResult(SaveEntry(entry, cancellationToken));
 42    }
 43
 44    /// <inheritdoc />
 45    public ValueTask<GovernanceOutboxEntry?> FindByOutboxEntryIdAsync(
 46        string outboxEntryId,
 47        CancellationToken cancellationToken = default)
 48    {
 1249        ArgumentException.ThrowIfNullOrWhiteSpace(outboxEntryId);
 1250        cancellationToken.ThrowIfCancellationRequested();
 51
 1252        _ = entries.TryGetValue(outboxEntryId.Trim(), out GovernanceOutboxEntry? entry);
 53
 1254        return ValueTask.FromResult(entry);
 55    }
 56
 57    /// <inheritdoc />
 58    public ValueTask<IReadOnlyList<GovernanceOutboxEntry>> FindPendingAsync(
 59        int maxCount = 100,
 60        CancellationToken cancellationToken = default)
 61    {
 3062        cancellationToken.ThrowIfCancellationRequested();
 63
 3064        int normalizedMaxCount = NormalizeMaxCount(maxCount);
 65
 3066        IReadOnlyList<GovernanceOutboxEntry> matches = [.. entries.Values
 3067            .Where(entry => entry.Status is GovernanceEmissionStatus.Pending)
 3068            .OrderBy(entry => entry.CreatedUtc)
 3069            .ThenBy(entry => entry.OutboxEntryId)
 3070            .Take(normalizedMaxCount)];
 71
 3072        return ValueTask.FromResult(matches);
 73    }
 74
 75    /// <inheritdoc />
 76    public ValueTask<IReadOnlyList<GovernanceOutboxEntry>> FindRetryReadyAsync(
 77        DateTimeOffset utcNow,
 78        int maxCount = 100,
 79        CancellationToken cancellationToken = default)
 80    {
 3281        cancellationToken.ThrowIfCancellationRequested();
 82
 3283        int normalizedMaxCount = NormalizeMaxCount(maxCount);
 3284        DateTimeOffset normalizedUtcNow = utcNow.ToUniversalTime();
 85
 3286        IReadOnlyList<GovernanceOutboxEntry> matches = [.. entries.Values
 3287            .Where(entry => entry.IsRetryReady(normalizedUtcNow))
 3288            .OrderBy(entry => entry.NextRetryUtc ?? entry.UpdatedUtc)
 3289            .ThenBy(entry => entry.OutboxEntryId)
 3290            .Take(normalizedMaxCount)];
 91
 3292        return ValueTask.FromResult(matches);
 93    }
 94
 95    /// <inheritdoc />
 96    public ValueTask<IReadOnlyList<GovernanceOutboxClaim>> ClaimPendingAsync(
 97        GovernanceOutboxClaimRequest request,
 98        CancellationToken cancellationToken = default)
 99    {
 43100        ArgumentNullException.ThrowIfNull(request);
 43101        cancellationToken.ThrowIfCancellationRequested();
 102
 43103        IReadOnlyList<GovernanceOutboxEntry> candidates = [.. entries.Values
 43104            .Where(entry => entry.Status is GovernanceEmissionStatus.Pending)
 43105            .Where(entry => entry.CanBeClaimed(request.UtcNow))
 43106            .OrderBy(entry => entry.CreatedUtc)
 43107            .ThenBy(entry => entry.OutboxEntryId)];
 108
 43109        return ValueTask.FromResult<IReadOnlyList<GovernanceOutboxClaim>>(ClaimEntries(candidates, request, cancellation
 110    }
 111
 112    /// <inheritdoc />
 113    public ValueTask<IReadOnlyList<GovernanceOutboxClaim>> ClaimRetryReadyAsync(
 114        GovernanceOutboxClaimRequest request,
 115        CancellationToken cancellationToken = default)
 116    {
 29117        ArgumentNullException.ThrowIfNull(request);
 29118        cancellationToken.ThrowIfCancellationRequested();
 119
 29120        IReadOnlyList<GovernanceOutboxEntry> candidates = [.. entries.Values
 29121            .Where(entry => entry.IsRetryReady(request.UtcNow))
 29122            .Where(entry => entry.CanBeClaimed(request.UtcNow))
 29123            .OrderBy(entry => entry.NextRetryUtc ?? entry.UpdatedUtc)
 29124            .ThenBy(entry => entry.OutboxEntryId)];
 125
 29126        return ValueTask.FromResult<IReadOnlyList<GovernanceOutboxClaim>>(ClaimEntries(candidates, request, cancellation
 127    }
 128
 129    /// <inheritdoc />
 130    public ValueTask<GovernanceOutboxEntry> MarkDeliveredAsync(
 131        string outboxEntryId,
 132        GovernanceEmissionResult result,
 133        CancellationToken cancellationToken = default)
 134    {
 5135        ArgumentException.ThrowIfNullOrWhiteSpace(outboxEntryId);
 5136        ArgumentNullException.ThrowIfNull(result);
 5137        cancellationToken.ThrowIfCancellationRequested();
 138
 5139        GovernanceOutboxEntry updatedEntry = UpdateExistingEntry(
 5140            outboxEntryId,
 5141            entry => IsTerminal(entry) ? entry : entry.MarkDelivered(result),
 5142            cancellationToken);
 143
 5144        return ValueTask.FromResult(updatedEntry);
 145    }
 146
 147    /// <inheritdoc />
 148    public ValueTask<GovernanceOutboxEntry> MarkClaimDeliveredAsync(
 149        GovernanceOutboxClaim claim,
 150        GovernanceEmissionResult result,
 151        CancellationToken cancellationToken = default)
 152    {
 12153        ArgumentNullException.ThrowIfNull(claim);
 12154        ArgumentNullException.ThrowIfNull(result);
 12155        cancellationToken.ThrowIfCancellationRequested();
 156
 12157        GovernanceOutboxEntry updatedEntry = UpdateClaimedEntry(
 12158            claim,
 12159            entry => IsTerminal(entry) ? entry : entry.MarkDelivered(result),
 12160            cancellationToken);
 161
 12162        return ValueTask.FromResult(updatedEntry);
 163    }
 164
 165    /// <inheritdoc />
 166    public ValueTask<GovernanceOutboxEntry> MarkFailedAsync(
 167        string outboxEntryId,
 168        GovernanceEmissionError governanceEmissionError,
 169        DateTimeOffset? nextRetryUtc = null,
 170        CancellationToken cancellationToken = default)
 171    {
 35172        ArgumentException.ThrowIfNullOrWhiteSpace(outboxEntryId);
 35173        ArgumentNullException.ThrowIfNull(governanceEmissionError);
 35174        cancellationToken.ThrowIfCancellationRequested();
 175
 35176        GovernanceOutboxEntry updatedEntry = UpdateExistingEntry(
 35177            outboxEntryId,
 35178            entry => IsTerminal(entry) ? entry : entry.MarkFailed(governanceEmissionError, nextRetryUtc),
 35179            cancellationToken);
 180
 35181        return ValueTask.FromResult(updatedEntry);
 182    }
 183
 184    /// <inheritdoc />
 185    public ValueTask<GovernanceOutboxEntry> MarkClaimFailedAsync(
 186        GovernanceOutboxClaim claim,
 187        GovernanceEmissionError governanceEmissionError,
 188        DateTimeOffset? nextRetryUtc = null,
 189        CancellationToken cancellationToken = default)
 190    {
 9191        ArgumentNullException.ThrowIfNull(claim);
 9192        ArgumentNullException.ThrowIfNull(governanceEmissionError);
 9193        cancellationToken.ThrowIfCancellationRequested();
 194
 9195        GovernanceOutboxEntry updatedEntry = UpdateClaimedEntry(
 9196            claim,
 9197            entry => IsTerminal(entry) ? entry : entry.MarkFailed(governanceEmissionError, nextRetryUtc),
 9198            cancellationToken);
 199
 9200        return ValueTask.FromResult(updatedEntry);
 201    }
 202
 203    /// <inheritdoc />
 204    public ValueTask<GovernanceOutboxEntry> MarkDeadLetteredAsync(
 205        string outboxEntryId,
 206        GovernanceEmissionError governanceEmissionError,
 207        string? deadLetterReason = null,
 208        CancellationToken cancellationToken = default)
 209    {
 3210        ArgumentException.ThrowIfNullOrWhiteSpace(outboxEntryId);
 3211        ArgumentNullException.ThrowIfNull(governanceEmissionError);
 3212        cancellationToken.ThrowIfCancellationRequested();
 213
 3214        GovernanceOutboxEntry updatedEntry = UpdateExistingEntry(
 3215            outboxEntryId,
 3216            entry => IsTerminal(entry) ? entry : entry.MarkDeadLettered(governanceEmissionError, deadLetterReason),
 3217            cancellationToken);
 218
 3219        return ValueTask.FromResult(updatedEntry);
 220    }
 221
 222    /// <inheritdoc />
 223    public ValueTask<GovernanceOutboxEntry> MarkClaimDeadLetteredAsync(
 224        GovernanceOutboxClaim claim,
 225        GovernanceEmissionError governanceEmissionError,
 226        string? deadLetterReason = null,
 227        CancellationToken cancellationToken = default)
 228    {
 4229        ArgumentNullException.ThrowIfNull(claim);
 4230        ArgumentNullException.ThrowIfNull(governanceEmissionError);
 4231        cancellationToken.ThrowIfCancellationRequested();
 232
 4233        GovernanceOutboxEntry updatedEntry = UpdateClaimedEntry(
 4234            claim,
 4235            entry => IsTerminal(entry) ? entry : entry.MarkDeadLettered(governanceEmissionError, deadLetterReason),
 4236            cancellationToken);
 237
 4238        return ValueTask.FromResult(updatedEntry);
 239    }
 240
 241    /// <inheritdoc />
 242    public ValueTask<GovernanceOutboxEntry> SaveClaimAsync(
 243        GovernanceOutboxClaim claim,
 244        GovernanceOutboxEntry entry,
 245        CancellationToken cancellationToken = default)
 246    {
 8247        ArgumentNullException.ThrowIfNull(claim);
 8248        ArgumentNullException.ThrowIfNull(entry);
 8249        cancellationToken.ThrowIfCancellationRequested();
 250
 8251        if (!string.Equals(claim.OutboxEntryId, entry.OutboxEntryId, StringComparison.Ordinal))
 252        {
 0253            throw new ArgumentException("Claim and entry must reference the same outbox entry ID.", nameof(entry));
 254        }
 255
 8256        GovernanceOutboxEntry updatedEntry = UpdateClaimedEntry(
 8257            claim,
 8258            currentEntry => IsTerminal(currentEntry) ? currentEntry : entry,
 8259            cancellationToken);
 260
 8261        return ValueTask.FromResult(updatedEntry);
 262    }
 263
 264    /// <inheritdoc />
 265    public ValueTask<GovernanceOutboxEntry?> ReleaseClaimAsync(
 266        GovernanceOutboxClaim claim,
 267        string? reason = null,
 268        CancellationToken cancellationToken = default)
 269    {
 10270        ArgumentNullException.ThrowIfNull(claim);
 9271        cancellationToken.ThrowIfCancellationRequested();
 272
 8273        GovernanceOutboxEntry? releasedEntry = ReleaseClaim(claim, cancellationToken);
 274
 8275        return ValueTask.FromResult(releasedEntry);
 276    }
 277
 278    private List<GovernanceOutboxClaim> ClaimEntries(
 279        IReadOnlyList<GovernanceOutboxEntry> candidates,
 280        GovernanceOutboxClaimRequest request,
 281        CancellationToken cancellationToken)
 282    {
 72283        List<GovernanceOutboxClaim> claims = new(Math.Min(request.MaxCount, candidates.Count));
 284
 223285        foreach (GovernanceOutboxEntry candidate in candidates)
 286        {
 40287            cancellationToken.ThrowIfCancellationRequested();
 288
 40289            if (claims.Count >= request.MaxCount)
 290            {
 1291                break;
 292            }
 293
 39294            GovernanceOutboxClaim? claim = TryClaimEntry(candidate.OutboxEntryId, request, cancellationToken);
 39295            if (claim is not null)
 296            {
 39297                claims.Add(claim);
 298            }
 299        }
 300
 72301        return claims;
 302    }
 303
 304    private GovernanceOutboxClaim? TryClaimEntry(
 305        string outboxEntryId,
 306        GovernanceOutboxClaimRequest request,
 307        CancellationToken cancellationToken)
 308    {
 309        while (true)
 310        {
 39311            cancellationToken.ThrowIfCancellationRequested();
 312
 39313            if (!entries.TryGetValue(outboxEntryId, out GovernanceOutboxEntry? currentEntry))
 314            {
 0315                return null;
 316            }
 317
 39318            if (!currentEntry.CanBeClaimed(request.UtcNow))
 319            {
 0320                return null;
 321            }
 322
 39323            GovernanceOutboxEntry claimedEntry = currentEntry.MarkClaimed(
 39324                request.WorkerId,
 39325                claimedUtc: request.UtcNow,
 39326                leaseDuration: request.LeaseDuration);
 327
 39328            if (entries.TryUpdate(outboxEntryId, claimedEntry, currentEntry))
 329            {
 39330                return CreateClaim(claimedEntry);
 331            }
 332        }
 333    }
 334
 335    private GovernanceOutboxEntry SaveEntry(
 336        GovernanceOutboxEntry entry,
 337        CancellationToken cancellationToken)
 338    {
 339        while (true)
 340        {
 2341            cancellationToken.ThrowIfCancellationRequested();
 342
 2343            if (!entries.TryGetValue(entry.OutboxEntryId, out GovernanceOutboxEntry? currentEntry))
 344            {
 0345                if (entries.TryAdd(entry.OutboxEntryId, entry))
 346                {
 0347                    return entry;
 348                }
 349
 350                continue;
 351            }
 352
 2353            if (IsTerminal(currentEntry))
 354            {
 1355                return currentEntry;
 356            }
 357
 1358            if (entries.TryUpdate(entry.OutboxEntryId, entry, currentEntry))
 359            {
 1360                return entry;
 361            }
 362        }
 363    }
 364
 365    private GovernanceOutboxEntry UpdateExistingEntry(
 366        string outboxEntryId,
 367        Func<GovernanceOutboxEntry, GovernanceOutboxEntry> updateEntry,
 368        CancellationToken cancellationToken)
 369    {
 43370        string normalizedOutboxEntryId = outboxEntryId.Trim();
 371
 372        while (true)
 373        {
 43374            cancellationToken.ThrowIfCancellationRequested();
 375
 43376            if (!entries.TryGetValue(normalizedOutboxEntryId, out GovernanceOutboxEntry? currentEntry))
 377            {
 0378                throw new InvalidOperationException($"Outbox entry '{normalizedOutboxEntryId}' was not found.");
 379            }
 380
 43381            GovernanceOutboxEntry updatedEntry = updateEntry(currentEntry);
 382
 43383            if (ReferenceEquals(currentEntry, updatedEntry))
 384            {
 1385                return currentEntry;
 386            }
 387
 42388            if (entries.TryUpdate(normalizedOutboxEntryId, updatedEntry, currentEntry))
 389            {
 42390                return updatedEntry;
 391            }
 392        }
 393    }
 394
 395    private GovernanceOutboxEntry UpdateClaimedEntry(
 396        GovernanceOutboxClaim claim,
 397        Func<GovernanceOutboxEntry, GovernanceOutboxEntry> updateEntry,
 398        CancellationToken cancellationToken)
 399    {
 400        while (true)
 401        {
 33402            cancellationToken.ThrowIfCancellationRequested();
 403
 33404            if (!entries.TryGetValue(claim.OutboxEntryId, out GovernanceOutboxEntry? currentEntry))
 405            {
 0406                throw new InvalidOperationException($"Outbox entry '{claim.OutboxEntryId}' was not found.");
 407            }
 408
 33409            if (!currentEntry.IsClaimedBy(claim) || IsTerminal(currentEntry))
 410            {
 1411                return currentEntry;
 412            }
 413
 32414            GovernanceOutboxEntry updatedEntry = updateEntry(currentEntry);
 415
 32416            if (ReferenceEquals(currentEntry, updatedEntry))
 417            {
 0418                return currentEntry;
 419            }
 420
 32421            if (entries.TryUpdate(claim.OutboxEntryId, updatedEntry, currentEntry))
 422            {
 32423                return updatedEntry;
 424            }
 425        }
 426    }
 427
 428    private GovernanceOutboxEntry? ReleaseClaim(
 429        GovernanceOutboxClaim claim,
 430        CancellationToken cancellationToken)
 431    {
 432        while (true)
 433        {
 8434            cancellationToken.ThrowIfCancellationRequested();
 435
 8436            if (!entries.TryGetValue(claim.OutboxEntryId, out GovernanceOutboxEntry? currentEntry))
 437            {
 1438                return null;
 439            }
 440
 7441            if (!currentEntry.IsClaimedBy(claim) || IsTerminal(currentEntry))
 442            {
 5443                return currentEntry;
 444            }
 445
 2446            GovernanceOutboxEntry releasedEntry = currentEntry.ReleaseClaim();
 447
 2448            if (entries.TryUpdate(claim.OutboxEntryId, releasedEntry, currentEntry))
 449            {
 2450                return releasedEntry;
 451            }
 452        }
 453    }
 454
 455    private static GovernanceOutboxClaim CreateClaim(GovernanceOutboxEntry entry)
 456    {
 39457        return GovernanceOutboxClaim.Create(
 39458            entry,
 39459            entry.ClaimOwner ?? throw new InvalidOperationException("Claimed entry is missing claim owner."),
 39460            entry.ClaimToken ?? throw new InvalidOperationException("Claimed entry is missing claim token."),
 39461            entry.ClaimedUtc ?? throw new InvalidOperationException("Claimed entry is missing claimed timestamp."),
 39462            entry.ClaimExpiresUtc ?? throw new InvalidOperationException("Claimed entry is missing claim expiration time
 463    }
 464
 465    private static bool IsTerminal(GovernanceOutboxEntry entry)
 466    {
 111467        return entry.IsDelivered || entry.IsDeadLettered;
 468    }
 469
 470    private static int NormalizeMaxCount(int maxCount)
 471    {
 62472        return maxCount <= 0
 62473            ? throw new ArgumentOutOfRangeException(nameof(maxCount), maxCount, "Maximum count must be greater than zero
 62474            : maxCount;
 475    }
 476}

Methods/Properties

.ctor()
EnqueueAsync(AsiBackbone.Core.Emissions.GovernanceEmissionEnvelope,System.Threading.CancellationToken)
SaveAsync(AsiBackbone.Core.Outbox.GovernanceOutboxEntry,System.Threading.CancellationToken)
FindByOutboxEntryIdAsync(System.String,System.Threading.CancellationToken)
FindPendingAsync(System.Int32,System.Threading.CancellationToken)
FindRetryReadyAsync(System.DateTimeOffset,System.Int32,System.Threading.CancellationToken)
ClaimPendingAsync(AsiBackbone.Core.Outbox.GovernanceOutboxClaimRequest,System.Threading.CancellationToken)
ClaimRetryReadyAsync(AsiBackbone.Core.Outbox.GovernanceOutboxClaimRequest,System.Threading.CancellationToken)
MarkDeliveredAsync(System.String,AsiBackbone.Core.Emissions.GovernanceEmissionResult,System.Threading.CancellationToken)
MarkClaimDeliveredAsync(AsiBackbone.Core.Outbox.GovernanceOutboxClaim,AsiBackbone.Core.Emissions.GovernanceEmissionResult,System.Threading.CancellationToken)
MarkFailedAsync(System.String,AsiBackbone.Core.Emissions.GovernanceEmissionError,System.Nullable`1<System.DateTimeOffset>,System.Threading.CancellationToken)
MarkClaimFailedAsync(AsiBackbone.Core.Outbox.GovernanceOutboxClaim,AsiBackbone.Core.Emissions.GovernanceEmissionError,System.Nullable`1<System.DateTimeOffset>,System.Threading.CancellationToken)
MarkDeadLetteredAsync(System.String,AsiBackbone.Core.Emissions.GovernanceEmissionError,System.String,System.Threading.CancellationToken)
MarkClaimDeadLetteredAsync(AsiBackbone.Core.Outbox.GovernanceOutboxClaim,AsiBackbone.Core.Emissions.GovernanceEmissionError,System.String,System.Threading.CancellationToken)
SaveClaimAsync(AsiBackbone.Core.Outbox.GovernanceOutboxClaim,AsiBackbone.Core.Outbox.GovernanceOutboxEntry,System.Threading.CancellationToken)
ReleaseClaimAsync(AsiBackbone.Core.Outbox.GovernanceOutboxClaim,System.String,System.Threading.CancellationToken)
ClaimEntries(System.Collections.Generic.IReadOnlyList`1<AsiBackbone.Core.Outbox.GovernanceOutboxEntry>,AsiBackbone.Core.Outbox.GovernanceOutboxClaimRequest,System.Threading.CancellationToken)
TryClaimEntry(System.String,AsiBackbone.Core.Outbox.GovernanceOutboxClaimRequest,System.Threading.CancellationToken)
SaveEntry(AsiBackbone.Core.Outbox.GovernanceOutboxEntry,System.Threading.CancellationToken)
UpdateExistingEntry(System.String,System.Func`2<AsiBackbone.Core.Outbox.GovernanceOutboxEntry,AsiBackbone.Core.Outbox.GovernanceOutboxEntry>,System.Threading.CancellationToken)
UpdateClaimedEntry(AsiBackbone.Core.Outbox.GovernanceOutboxClaim,System.Func`2<AsiBackbone.Core.Outbox.GovernanceOutboxEntry,AsiBackbone.Core.Outbox.GovernanceOutboxEntry>,System.Threading.CancellationToken)
ReleaseClaim(AsiBackbone.Core.Outbox.GovernanceOutboxClaim,System.Threading.CancellationToken)
CreateClaim(AsiBackbone.Core.Outbox.GovernanceOutboxEntry)
IsTerminal(AsiBackbone.Core.Outbox.GovernanceOutboxEntry)
NormalizeMaxCount(System.Int32)