Skip to content

Conversation

@ElanHasson
Copy link
Contributor

@ElanHasson ElanHasson commented Jan 25, 2026

Summary

This PR modernizes the InfinityFlow.Aspire.Temporal package with:

  1. Simplified fluent API - No more lambda callbacks
  2. Dynamic port allocation - Following standard Aspire patterns
  3. Critical bug fixes - Memory leaks, silent failures, and error handling
  4. Semantic endpoint methods - Clearer, more discoverable API
  5. Platform-aware executable support - Automatic command detection
  6. Dependency updates - Latest Temporal packages (1.4.0)

🎯 New Simplified API

Before (Lambda Callback Pattern):

var temporal = builder.AddTemporalServerContainer("temporal", x => x
    .WithPort(7233)
    .WithUiPort(8233)
    .WithLogFormat(LogFormat.Json)
    .WithNamespace("test1"));

After (Direct Fluent API):

var temporal = builder.AddTemporalServer("temporal")
    .WithServiceEndpoint(7233)    // Semantic method names
    .WithUiEndpoint(8233)
    .WithLogFormat(LogFormat.Json)
    .WithNamespace("test1");

builder.AddProject<Projects.Worker>("worker")
    .WithReference(temporal);      // Works seamlessly

Executable Mode (New):

// Uses platform-specific defaults: temporal.exe on Windows, temporal on Linux/macOS
var temporal = builder.AddTemporalServer("temporal-local")
    .WithExecutable()
    .WithLogFormat(LogFormat.Json)
    .WithNamespace("test1");

// Or specify custom command and working directory
var temporal = builder.AddTemporalServer("temporal-custom")
    .WithExecutable("/usr/local/bin/temporal", "/opt/temporal")
    .WithNamespace("test1");

🔧 Breaking Changes

1. Default Port Allocation (v0.x → v1.0)

Old behavior (fixed ports):

var temporal = builder.AddTemporalServerContainer("temporal");
// Always: localhost:7233, localhost:8233

New behavior (dynamic ports):

var temporal = builder.AddTemporalServer("temporal");
// Dynamic: localhost:54321, localhost:54322 (avoids conflicts)

Migration to fixed ports:

var temporal = builder.AddTemporalServer("temporal")
    .WithServiceEndpoint(7233)
    .WithUiEndpoint(8233);

2. API Simplification

  • AddTemporalServerContainer()AddTemporalServer() (container by default)
  • AddTemporalServerExecutable()AddTemporalServer().WithExecutable()
  • WithPort()WithServiceEndpoint()
  • WithUiPort()WithUiEndpoint()
  • New: WithMetricsEndpoint(), WithHttpEndpoint()
  • Lambda callbacks removed in favor of direct method chaining

3. Client Library Returns Builder

Old:

IServiceCollection services = builder.AddTemporalClient();

New:

var clientBuilder = builder.AddTemporalClient();
clientBuilder.Services  // Access IServiceCollection via .Services property

🐛 Critical Fixes

1. Memory Leak (Fixed)

  • Issue: Meter instances created but never disposed
  • Fix: Register Meter as singleton with DI for proper lifecycle management
  • File: AspireTemporalExtensions.cs:220-221

2. Silent Failure in Dynamic Config (Fixed)

  • Issue: Unsupported types silently produced malformed CLI arguments
  • Fix: Throw ArgumentException with clear error message
  • File: TemporalServerResourceArguments.cs:148-151

3. Worker Sample Silent Failure (Fixed)

  • Issue: Null-conditional operator ?. caused silent failures
  • Fix: Explicit null check with meaningful error message
  • File: sample/Worker/Program.cs:18-19

4. Unprotected User Callbacks (Fixed)

  • Issue: Callback exceptions produced cryptic DI errors
  • Fix: Wrapped all callbacks in try-catch with contextual messages
  • Files:
    • WithInterceptors() - Line 127-137
    • WithTracingSources() - Line 152-162
    • ConfigureOptions() - Line 272-281

5. Poor Error Messages (Fixed)

  • Issue: Missing connection string error didn't help debugging
  • Fix: Enhanced error includes available connection names
  • File: AspireTemporalExtensions.cs:205-216

6. Missing Validation (Fixed)

  • Issue: Null/whitespace tracing sources silently failed
  • Fix: Added validation with clear error message
  • File: AspireTemporalExtensions.cs:238-243

📦 Changes

Core API Improvements

  • Fluent builder pattern with lazy registration
  • Semantic method names for better discoverability
  • Platform-aware defaults for executable mode
  • Comprehensive error handling with actionable messages
  • Lazy resource building - prevents double-registration issues

Client Library (InfinityFlow.Aspire.Temporal.Client)

  • Automatic OpenTelemetry integration - tracing and metrics work out of the box
  • Fluent configuration via WithInterceptors(), WithTracingSources()
  • Automatic connection string resolution from Aspire configuration
  • Service defaults extensions for centralized OpenTelemetry configuration

Testing

  • 22 comprehensive tests (7 original + 15 new)
  • Tests cover:
    • Container vs Executable modes
    • Dynamic vs Fixed ports
    • Semantic endpoint methods
    • Fluent API chaining
    • Builder state management
    • Configuration validation

Dependencies

  • Updated Temporalio from 1.3.4 to 1.4.0
  • Updated Temporalio.Extensions.Hosting from 1.3.4 to 1.4.0
  • Addresses NU1603 warnings

Documentation

  • Updated AppHost sample with new API
  • Added executable mode examples
  • Improved inline documentation
  • Added comprehensive XML docs

✅ Benefits

  1. Simpler API: No lambda callbacks, direct method chaining
  2. Better IntelliSense: Semantic method names are discoverable
  3. No port conflicts: Dynamic allocation prevents CI/CD issues
  4. Production-ready: All critical bugs fixed
  5. Platform-aware: Automatic exe/no-exe detection
  6. Type-safe: Prevents common configuration errors
  7. Testable: Comprehensive test coverage

🧪 Testing

All tests passing (22/22):

dotnet build  # ✅ No errors, no warnings
dotnet test   # ✅ Passed: 22, Failed: 0, Skipped: 0

Sample applications verified:

  • ✅ AppHost runs with new API
  • ✅ Worker connects successfully
  • ✅ Client integration works
  • ✅ OpenTelemetry data flows to Aspire dashboard

📚 References

🚀 Version

Recommend v1.0.0 release due to:

  1. API redesign (breaking)
  2. Default port behavior change (breaking)
  3. Client API signature change (breaking)
  4. Critical bug fixes
  5. Production-ready quality

BREAKING CHANGE: Default port allocation changed from fixed to dynamic

## Changes

### Core API (Breaking)
- Changed `Port` property from `int` to `int?` in TemporalServerResourceArguments
- Removed default values (7233, 8233) to enable dynamic allocation
- Updated container extension to use fixed `targetPort` with dynamic `port`
- Removed fallback port calculations in executable extension
- Updated builder methods to accept nullable port parameters

### Client Integration Library (New)
- Added InfinityFlow.Aspire.Temporal.Client package
- Provides `AddTemporalClient()` and `AddTemporalWorker()` extension methods
- Automatic connection string resolution from Aspire configuration
- Follows standard Aspire client integration patterns

### Documentation
- Updated README with breaking changes section
- Added migration guide from v0.x to v1.0
- Updated sample to demonstrate both dynamic and fixed port usage
- Improved connection string documentation

### Tests
- Added comprehensive unit tests for port allocation
- Tests verify both dynamic and fixed port scenarios
- All 7 tests passing

## Benefits
- Eliminates port conflicts in multi-instance scenarios
- Better parallel test execution
- Consistent with official Aspire networking patterns
- Easier client integration with new library

## Migration
To maintain v0.x behavior with fixed ports:
```csharp
var temporal = builder.AddTemporalServerContainer("temporal", x => x
    .WithPort(7233)
    .WithUiPort(8233));
```

Follows patterns from:
- https://learn.microsoft.com/en-us/dotnet/aspire/fundamentals/networking-overview
- Aspire.Hosting.PostgreSQL
- Aspire.Hosting.Valkey

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@ElanHasson ElanHasson force-pushed the feature/dynamic-port-allocation branch from 3bc0bc3 to 7d93d3f Compare January 25, 2026 22:48
@ElanHasson ElanHasson changed the title feat: Add dynamic port allocation following Aspire patterns feat: Simplified Temporal API with dynamic ports, fluent builder, and critical fixes Jan 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant