Skip to content

feat: add Timeout Manager pattern#454

Merged
JerrettDavis merged 1 commit into
mainfrom
feat/timeout-manager-447
May 30, 2026
Merged

feat: add Timeout Manager pattern#454
JerrettDavis merged 1 commit into
mainfrom
feat/timeout-manager-447

Conversation

@JerrettDavis

Copy link
Copy Markdown
Owner

Summary

  • adds the Timeout Manager application architecture pattern with fluent runtime support and a source-generated factory path
  • adds order reservation timeout example, IServiceCollection integration, docs, catalog coverage, and benchmark matrix entries
  • adds TinyBDD coverage for runtime behavior, generator diagnostics/output, example execution, and production-readiness catalog expectations

Closes #447

Verification

  • dotnet test test\PatternKit.Tests\PatternKit.Tests.csproj --configuration Release --no-restore --filter "FullyQualifiedName~TimeoutManagerTests" -p:TestTfmsInParallel=false --logger "console;verbosity=minimal"
  • dotnet test test\PatternKit.Generators.Tests\PatternKit.Generators.Tests.csproj --configuration Release --no-restore --filter "FullyQualifiedName~TimeoutManagerGeneratorTests" -p:TestTfmsInParallel=false --logger "console;verbosity=minimal"
  • dotnet test test\PatternKit.Examples.Tests\PatternKit.Examples.Tests.csproj --configuration Release --no-restore --filter "FullyQualifiedNameTimeoutManagerDemo|FullyQualifiedNameProductionReadiness|FullyQualifiedName~DependencyInjection" -p:TestTfmsInParallel=false --logger "console;verbosity=minimal"
  • dotnet format PatternKit.slnx --verify-no-changes --verbosity minimal
  • dotnet build PatternKit.slnx --configuration Release --no-restore
  • dotnet test PatternKit.slnx --configuration Release --no-build -p:TestTfmsInParallel=false --logger "console;verbosity=minimal"

Copilot AI review requested due to automatic review settings May 30, 2026 05:07
@github-actions

github-actions Bot commented May 30, 2026

Copy link
Copy Markdown
Contributor

⚠️ Deprecation Warning: The deny-licenses option is deprecated for possible removal in the next major release. For more information, see issue 997.

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Scanned Files

None

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds the Timeout Manager application-architecture pattern: a fluent runtime (TimeoutManager<TKey>), a Roslyn incremental generator that emits a static factory from [GenerateTimeoutManager], an order-reservation example with DI integration, and the surrounding docs/catalog/benchmark/test plumbing.

Changes:

  • New PatternKit.Core.Application.Timeouts.TimeoutManager<TKey> (Builder + record types) with TinyBDD unit coverage.
  • New TimeoutManagerGenerator (PKTM001/PKTM002 diagnostics), attribute in Generators.Abstractions, and generator tests.
  • Order reservation example (OrderReservationTimeoutDemo), IServiceCollection extensions, benchmarks, and documentation/catalog updates (counts bumped from 106→107, application architecture 21→22, route results 424→428).

Reviewed changes

Copilot reviewed 26 out of 26 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
src/PatternKit.Core/Application/Timeouts/TimeoutManager.cs Core runtime with Builder, record, state, lock-based dictionary store.
src/PatternKit.Generators/Timeouts/TimeoutManagerGenerator.cs Incremental generator emitting nested-host factory.
src/PatternKit.Generators/AnalyzerReleases.Unshipped.md Adds PKTM001/PKTM002 entries.
src/PatternKit.Generators.Abstractions/Timeouts/TimeoutManagerAttributes.cs New [GenerateTimeoutManager] attribute.
src/PatternKit.Examples/TimeoutManagerDemo/OrderReservationTimeoutDemo.cs Order-reservation example wiring fluent + generated paths.
src/PatternKit.Examples/DependencyInjection/PatternKitExampleServiceCollectionExtensions.cs Registers the new example aggregate.
src/PatternKit.Examples/ProductionReadiness/PatternKitPatternCatalog.cs Catalog entry for Timeout Manager.
src/PatternKit.Examples/ProductionReadiness/PatternKitExampleCatalog.cs Example descriptor entry.
benchmarks/PatternKit.Benchmarks/Application/TimeoutManagerBenchmarks.cs Fluent/generated construction + execution benchmarks.
test/PatternKit.Tests/Application/Timeouts/TimeoutManagerTests.cs Runtime behavior coverage.
test/PatternKit.Generators.Tests/TimeoutManagerGeneratorTests.cs Generator output and diagnostic coverage.
test/PatternKit.Examples.Tests/TimeoutManagerDemo/OrderReservationTimeoutDemoTests.cs Example/DI coverage.
test/PatternKit.Examples.Tests/ProductionReadiness/*.cs Catalog/benchmark count updates.
docs/** + README.md Pattern, generator, and example docs plus catalog counts.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

PKVO002 | PatternKit.Generators.ValueObjects | Error | Value Object host must be a class.
PKVO003 | PatternKit.Generators.ValueObjects | Error | Value Object must declare at least one component.
PKTM001 | PatternKit.Generators.Timeouts | Error | Timeout Manager host must be partial.
PKTM002 | PatternKit.Generators.Timeouts | Error | Timeout Manager key type is invalid.
Comment on lines +61 to +63
var keyType = attribute.ConstructorArguments.Length >= 1 ? attribute.ConstructorArguments[0].Value as INamedTypeSymbol : null;
if (keyType is null || keyType.TypeKind == TypeKind.Error)
return;
Comment on lines +126 to +136
private static void AppendTypeDeclaration(StringBuilder sb, INamedTypeSymbol type, string indent)
{
sb.Append(indent).Append(GetAccessibility(type.DeclaredAccessibility)).Append(' ');
if (type.IsStatic)
sb.Append("static ");
else if (type.IsAbstract && type.TypeKind == TypeKind.Class)
sb.Append("abstract ");
else if (type.IsSealed && type.TypeKind == TypeKind.Class)
sb.Append("sealed ");
sb.Append("partial ").Append(type.TypeKind == TypeKind.Struct ? "struct" : "class").Append(' ').Append(type.Name).AppendLine();
}
Comment on lines +53 to +59
private static void Generate(SourceProductionContext context, INamedTypeSymbol type, TypeDeclarationSyntax node, AttributeData attribute)
{
if (!node.Modifiers.Any(static modifier => modifier.Text == "partial"))
{
context.ReportDiagnostic(Diagnostic.Create(MustBePartial, node.Identifier.GetLocation(), type.Name));
return;
}
Comment on lines +49 to +59
private TimeoutRecord<TKey> ScheduleCore(TKey key, string? correlationId, DateTimeOffset now, DateTimeOffset deadline)
{
if (deadline < now)
throw new ArgumentOutOfRangeException(nameof(deadline), deadline, "Timeout deadline cannot be before the current clock value.");

var record = new TimeoutRecord<TKey>(key, correlationId, now, deadline);
lock (_gate)
_timeouts[key] = record;

return record;
}
Comment on lines +23 to +66
public string Name { get; }

public int PendingCount
{
get
{
lock (_gate)
return _timeouts.Count;
}
}

public TimeoutRecord<TKey> Schedule(TKey key, DateTimeOffset deadline, string? correlationId = null)
{
var now = _clock();
return ScheduleCore(key, correlationId, now, deadline);
}

public TimeoutRecord<TKey> ScheduleAfter(TKey key, TimeSpan dueAfter, string? correlationId = null)
{
if (dueAfter < TimeSpan.Zero)
throw new ArgumentOutOfRangeException(nameof(dueAfter), dueAfter, "Timeout duration cannot be negative.");

var now = _clock();
return ScheduleCore(key, correlationId, now, now.Add(dueAfter));
}

private TimeoutRecord<TKey> ScheduleCore(TKey key, string? correlationId, DateTimeOffset now, DateTimeOffset deadline)
{
if (deadline < now)
throw new ArgumentOutOfRangeException(nameof(deadline), deadline, "Timeout deadline cannot be before the current clock value.");

var record = new TimeoutRecord<TKey>(key, correlationId, now, deadline);
lock (_gate)
_timeouts[key] = record;

return record;
}

public bool Complete(TKey key) => Remove(key);

public bool Cancel(TKey key) => Remove(key);

public IReadOnlyList<TimeoutRecord<TKey>> ExpireDue()
=> ExpireDue(_clock());
Comment on lines +61 to +63
public bool Complete(TKey key) => Remove(key);

public bool Cancel(TKey key) => Remove(key);
Comment on lines +68 to +105
public IReadOnlyList<TimeoutRecord<TKey>> ExpireDue(DateTimeOffset now)
{
lock (_gate)
{
var due = _timeouts.Values
.Where(timeout => timeout.Deadline <= now)
.OrderBy(static timeout => timeout.Deadline)
.ThenBy(static timeout => timeout.CorrelationId, StringComparer.Ordinal)
.ToArray();

foreach (var timeout in due)
_timeouts.Remove(timeout.Key);

return due;
}
}

public IReadOnlyList<TimeoutRecord<TKey>> Snapshot()
{
lock (_gate)
return _timeouts.Values
.OrderBy(static timeout => timeout.Deadline)
.ThenBy(static timeout => timeout.CorrelationId, StringComparer.Ordinal)
.ToArray();
}

public TimeoutManagerState<TKey> GetState()
{
lock (_gate)
{
var pendingTimeouts = _timeouts.Values
.OrderBy(static timeout => timeout.Deadline)
.ThenBy(static timeout => timeout.CorrelationId, StringComparer.Ordinal)
.ToArray();

return new(Name, pendingTimeouts.Length, pendingTimeouts);
}
}
@github-actions

github-actions Bot commented May 30, 2026

Copy link
Copy Markdown
Contributor

Test Results

    12 files      12 suites   8m 30s ⏱️
 3 841 tests  3 841 ✅ 0 💤 0 ❌
11 954 runs  11 954 ✅ 0 💤 0 ❌

Results for commit 7e93be0.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented May 30, 2026

Copy link
Copy Markdown
Contributor

🔍 PR Validation Results

Version: ``

✅ Validation Steps

  • Build solution
  • Run tests
  • Build documentation
  • Dry-run NuGet packaging

📊 Artifacts

Dry-run artifacts have been uploaded and will be available for 7 days.


This comment was automatically generated by the PR validation workflow.

@codecov

codecov Bot commented May 30, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.89916% with 5 lines in your changes missing coverage. Please review.
✅ Project coverage is 96.78%. Comparing base (e8114c0) to head (7e93be0).

Files with missing lines Patch % Lines
...Kit.Generators/Timeouts/TimeoutManagerGenerator.cs 95.91% 4 Missing ⚠️
...ernKit.Core/Application/Timeouts/TimeoutManager.cs 98.78% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #454      +/-   ##
==========================================
+ Coverage   96.61%   96.78%   +0.16%     
==========================================
  Files         551      555       +4     
  Lines       44988    45224     +236     
  Branches     2966     6517    +3551     
==========================================
+ Hits        43467    43768     +301     
+ Misses       1521     1456      -65     
Flag Coverage Δ
unittests 96.78% <97.89%> (+0.16%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@JerrettDavis JerrettDavis changed the title Add Timeout Manager pattern feat: add Timeout Manager pattern May 30, 2026
@JerrettDavis JerrettDavis force-pushed the feat/timeout-manager-447 branch from 83c2712 to 7e93be0 Compare May 30, 2026 05:24
@github-actions

Copy link
Copy Markdown
Contributor

Code Coverage

Summary
  Generated on: 05/30/2026 - 05:32:46
  Coverage date: 05/30/2026 - 05:30:07 - 05/30/2026 - 05:32:34
  Parser: MultiReport (12x Cobertura)
  Assemblies: 5
  Classes: 1628
  Files: 555
  Line coverage: 96.6%
  Covered lines: 43693
  Uncovered lines: 1531
  Coverable lines: 45224
  Total lines: 97241
  Branch coverage: 82.2% (13663 of 16610)
  Covered branches: 13663
  Total branches: 16610
  Method coverage: 96.6% (8497 of 8789)
  Full method coverage: 90.2% (7934 of 8789)
  Covered methods: 8497
  Fully covered methods: 7934
  Total methods: 8789

PatternKit.Core                                                                                                     95.4%
  PatternKit.Application.ActivityTracking.ActivityGateState                                                          100%
  PatternKit.Application.ActivityTracking.ActivityLease                                                              100%
  PatternKit.Application.ActivityTracking.ActivityRecord                                                             100%
  PatternKit.Application.ActivityTracking.ActivityTracker                                                            100%
  PatternKit.Application.Aggregates.AggregateCommandHandler<T1, T2, T3>                                              100%
  PatternKit.Application.Aggregates.AggregateCommandResult<T>                                                        100%
  PatternKit.Application.Aggregates.AggregateRoot<T1, T2>                                                            100%
  PatternKit.Application.AntiCorruption.AntiCorruptionLayer<T1, T2>                                                 90.4%
  PatternKit.Application.AntiCorruption.AntiCorruptionResult<T>                                                      100%
  PatternKit.Application.AuditLog.AuditLogAppendResult<T>                                                           85.7%
  PatternKit.Application.AuditLog.InMemoryAuditLog<T1, T2>                                                          95.4%
  PatternKit.Application.BoundedContexts.BoundedContextAdapter                                                       100%
  PatternKit.Application.BoundedContexts.BoundedContextCapability                                                   83.3%
  PatternKit.Application.BoundedContexts.BoundedContextDescriptor                                                   95.4%
  PatternKit.Application.ContextMaps.ContextMapDescriptor                                                           96.8%
  PatternKit.Application.ContextMaps.ContextMapRelationship                                                          100%
  PatternKit.Application.DataMapping.DataMapper<T1, T2>                                                             94.6%
  PatternKit.Application.DataMapping.DataMapperError                                                                  90%
  PatternKit.Application.DataMapping.DataMapperResult<T>                                                            84.6%
  PatternKit.Application.DomainEvents.DomainEventDispatcher<T>                                                      95.4%
  PatternKit.Application.DomainEvents.DomainEventDispatchResult                                                      100%
  PatternKit.Application.DomainServices.DomainServiceOperation<T1, T2>                                               100%
  PatternKit.Application.DomainServices.DomainServiceRegistry<T1, T2>                                                100%
  PatternKit.Application.EventSourcing.EventStoreAppendResult                                                        100%
  PatternKit.Application.EventSourcing.InMemoryEventStore<T1, T2>                                                   97.9%
  PatternKit.Application.EventSourcing.StoredEvent<T1, T2>                                                            80%
  PatternKit.Application.FeatureToggles.FeatureToggleDecision                                                       87.5%
  PatternKit.Application.FeatureToggles.FeatureToggleRule<T>                                                         100%
  PatternKit.Application.FeatureToggles.FeatureToggleSet<T>                                                         96.9%
  PatternKit.Application.IdentityMap.IdentityMap<T1, T2>                                                             100%
  PatternKit.Application.IdentityMap.IdentityMapResult<T>                                                           92.8%
  PatternKit.Application.MaterializedViews.MaterializedView<T1, T2>                                                 98.4%
  PatternKit.Application.Repository.InMemoryRepository<T1, T2>                                                      92.8%
  PatternKit.Application.Repository.RepositoryResult<T>                                                             93.3%
  PatternKit.Application.ServiceLayer.ServiceLayerOperation<T1, T2>                                                 96.7%
  PatternKit.Application.ServiceLayer.ServiceLayerResult<T>                                                         94.7%
  PatternKit.Application.ServiceLayer.ServiceLayerRule<T>                                                            100%
  PatternKit.Application.Specification.Specification<T>                                                              100%

@JerrettDavis JerrettDavis merged commit aa7f909 into main May 30, 2026
12 checks passed
@JerrettDavis JerrettDavis deleted the feat/timeout-manager-447 branch May 30, 2026 05:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Timeout Manager pattern

2 participants