# Costruire una libreria di esecuzione effimera riutilizzabile

<!--category-- ASP.NET, Architecture, Systems Design, Async, DI -->
<datetime class="hidden">2025-12-12T14:00</datetime>

Dentro **[Parte 1: Fuoco e non *Abbastanza* Dimentica](/blog/fire-and-dont-quite-forget-ephemeral-execution)**Abbiamo esplorato la teoria dietro l'esecuzione effimera - flussi di lavoro asincroni limitati, privati, debuggabili che ricordano quanto basta per essere utili e poi evaporare.

Questo articolo trasforma quel modello in una libreria riutilizzabile che si può cadere in qualsiasi progetto .NET.

## NUGET!!!

[Questo è ora nel pacchetto per lo piùlucid.efemerals Nuget anche più di 20 modelli per lo piùlucid.efemerals e 'atomi'](https://www.nuget.org/packages?q=mostlylucid&includeComputedFrameworks=true&prerel=true&sortby=created-desc).

[![NuGetCity name (optional, probably does not need a translation)](https://img.shields.io/nuget/v/mostlylucid.ephemeral.svg)](https://www.nuget.org/packages/mostlylucid.ephemeral)
[![Licenza](https://img.shields.io/badge/license-Unlicense-blue.svg)](../../UNLICENSE)

## File sorgente

La libreria è suddivisa in file ben fattorizzati:

| File | Scopo |
|------|---------|
| [Opzioni effimere.cs](https://github.com/scottgal/mostlylucidweb/blob/main/Mostlylucid/Helpers/Ephemeral/EphemeralOptions.cs) | Configurazione (valuta, dimensione della finestra, durata, segnali) |
| [Operazione effimera.cs](https://github.com/scottgal/mostlylucidweb/blob/main/Mostlylucid/Helpers/Ephemeral/EphemeralOperation.cs) | Tracciamento interno del funzionamento con supporto del segnale |
| [Istantanee.cs](https://github.com/scottgal/mostlylucidweb/blob/main/Mostlylucid/Helpers/Ephemeral/Snapshots.cs) | Record snapshot immutabili esposti ai consumatori |
| [Segnali.cs](https://github.com/scottgal/mostlylucidweb/blob/main/Mostlylucid/Helpers/Ephemeral/Signals.cs) |Eventi di segnale, propagazione, vincoli e il lavello di segnale globale |
| [Generatore Effimero.](https://github.com/scottgal/mostlylucidweb/blob/main/Mostlylucid/Helpers/Ephemeral/EphemeralIdGenerator.cs) |Fast XxHash64-based ID generation |
| [ConcurrencyGates.cs](https://github.com/scottgal/mostlylucidweb/blob/main/Mostlylucid/Helpers/Ephemeral/ConcurrencyGates.cs) | Limitazione della concorrenza fissa e regolabile |
| [StringPatternMatcher.cs](https://github.com/scottgal/mostlylucidweb/blob/main/Mostlylucid/Helpers/Ephemeral/StringPatternMatcher.cs) |Modello in stile Glob per il filtraggio del segnale |
| [ParallelEfemeral.cs](https://github.com/scottgal/mostlylucidweb/blob/main/Mostlylucid/Helpers/Ephemeral/ParallelEphemeral.cs) | Metodi statici di estensione (`EphemeralForEachAsync`) |
| [Coordinatore EphemeralWork.cs](https://github.com/scottgal/mostlylucidweb/blob/main/Mostlylucid/Helpers/Ephemeral/EphemeralWorkCoordinator.cs) | Coordinatore della coda di lavoro a lunga durata |
| [EfemeralKeyedWorkCoordinator.cs](https://github.com/scottgal/mostlylucidweb/blob/main/Mostlylucid/Helpers/Ephemeral/EphemeralKeyedWorkCoordinator.cs) | Esecuzione sequenziale per chiave con programmazione equa |
| [EfemeralResultCoordinator.cs](https://github.com/scottgal/mostlylucidweb/blob/main/Mostlylucid/Helpers/Ephemeral/EphemeralResultCoordinator.cs) | Variante del coordinatore della cattura dei risultati |
| [SignalDispatcher.cs](https://github.com/scottgal/mostlylucidweb/blob/main/Mostlylucid/Helpers/Ephemeral/SignalDispatcher.cs) | Instradamento del segnale asincrono con motivo corrispondente |
| [DipendenzaIniezione.cs](https://github.com/scottgal/mostlylucidweb/blob/main/Mostlylucid/Helpers/Ephemeral/DependencyInjection.cs) | Metodi di estensione DI e implementazioni di fabbrica |
| [Esempi/SignalingHttpClient.cs](https://github.com/scottgal/mostlylucidweb/blob/main/Mostlylucid/Helpers/Ephemeral/Examples/SignalingHttpClient.cs) |Emissione del segnale a grana fine del campione per le chiamate HTTP |

E [prove complete](https://github.com/scottgal/mostlylucidweb/blob/main/Mostlylucid.Test/ParallelEphemeralTests.cs) coprendo tutti i casi di orlo.

[TOC]

---


## Prima e dopo

Ecco cosa stiamo sostituendo:

```csharp
// ❌ Before: Fire-and-forget black hole
_ = Task.Run(() => ProcessAsync(item));
// No visibility. No debugging. No idea if it worked.

// ❌ Or: Blocking everything
await ProcessAsync(item);  // Hope you like waiting...
```

E quello che stiamo costruendo:

```csharp
// ✅ After: Trackable, bounded, debuggable
await coordinator.EnqueueAsync(item);

// Instant visibility
Console.WriteLine($"Pending: {coordinator.PendingCount}");
Console.WriteLine($"Active: {coordinator.ActiveCount}");
Console.WriteLine($"Failed: {coordinator.TotalFailed}");

// Full operation history
var snapshot = coordinator.GetSnapshot();
var failures = coordinator.GetFailed();
```

Stessa esecuzione asincrona. Completa osservabilità. Nessun dato utente conservato.

---


## Avvio rapido

Il modello più comune: registrare un coordinatore in DI e iniettarlo:

```csharp
// Program.cs
services.AddEphemeralWorkCoordinator<TranslationRequest>(
    async (request, ct) => await TranslateAsync(request, ct),
    new EphemeralOptions { MaxConcurrency = 8 });

// Your service
public class TranslationService(EphemeralWorkCoordinator<TranslationRequest> coordinator)
{
    public async Task TranslateAsync(TranslationRequest request)
    {
        await coordinator.EnqueueAsync(request);
        // Returns immediately - work happens in background
    }

    public object GetStatus() => new
    {
        pending = coordinator.PendingCount,
        active = coordinator.ActiveCount,
        completed = coordinator.TotalCompleted,
        failed = coordinator.TotalFailed
    };
}
```

---


## Di quale variante ho bisogno?

```text
┌─────────────────────────────────────────────────────────────────┐
│                    DECISION TREE                                │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  Processing a collection once?                                  │
│  └─► EphemeralForEachAsync<T> (ParallelEphemeral.cs)            │
│                                                                 │
│  Need a long-lived queue that accepts items over time?          │
│  └─► EphemeralWorkCoordinator<T>                                │
│                                                                 │
│  Need per-entity ordering (user commands, tenant jobs)?         │
│  └─► EphemeralKeyedWorkCoordinator<TKey, T>                     │
│                                                                 │
│  Need to capture results (fingerprints, summaries)?             │
│  └─► EphemeralResultCoordinator<TInput, TResult>                │
│                                                                 │
│  Need multiple coordinators with different configs?             │
│  └─► IEphemeralCoordinatorFactory<T> (like IHttpClientFactory)  │
│                                                                 │
│  Need dynamic concurrency adjustment at runtime?                │
│  └─► Set EnableDynamicConcurrency = true, call SetMaxConcurrency│
│                                                                 │
└─────────────────────────────────────────────────────────────────┘
```

---


## L'oggetto di configurazione

Da [Opzioni effimere.cs](https://github.com/scottgal/mostlylucidweb/blob/main/Mostlylucid/Helpers/Ephemeral/EphemeralOptions.cs):

```csharp
public sealed class EphemeralOptions
{
    // Concurrency control
    public int MaxConcurrency { get; init; } = Environment.ProcessorCount;
    public int MaxConcurrencyPerKey { get; init; } = 1;
    public bool EnableDynamicConcurrency { get; init; } = false;

    // Window management
    public int MaxTrackedOperations { get; init; } = 200;
    public TimeSpan? MaxOperationLifetime { get; init; } = TimeSpan.FromMinutes(5);

    // Fair scheduling (keyed coordinator)
    public bool EnableFairScheduling { get; init; } = false;
    public int FairSchedulingThreshold { get; init; } = 10;

    // Signal-reactive processing
    public IReadOnlySet<string>? CancelOnSignals { get; init; }
    public IReadOnlySet<string>? DeferOnSignals { get; init; }
    public int MaxDeferAttempts { get; init; } = 10;
    public TimeSpan DeferCheckInterval { get; init; } = TimeSpan.FromMilliseconds(100);

    // Signal infrastructure
    public SignalSink? Signals { get; init; }
    public SignalConstraints? SignalConstraints { get; init; }
    public Action<SignalEvent>? OnSignal { get; init; }

    // Async signal handling
    public Func<SignalEvent, CancellationToken, Task>? OnSignalAsync { get; init; }
    public int MaxConcurrentSignalHandlers { get; init; } = 4;
    public int MaxQueuedSignals { get; init; } = 1000;

    // Observability
    public Action<IReadOnlyCollection<EphemeralOperationSnapshot>>? OnSample { get; init; }
}
```

### Decisioni chiave in materia di progettazione

- **MaxConcurrency** Predefiniti per il conteggio della CPU - ragionevole per il lavoro legato alla CPU. Per il lavoro legato all'I/O, aumentarlo.
- **Abilita valuta dinamica** consente la regolazione del runtime tramite `SetMaxConcurrency()` - usa un cancello personalizzato invece di `SemaphoreSlim`.
- **AnnullaOnSignals/DeferOnSignals** rendono i coordinatori reattivi al segnale - rispondono allo stato del sistema ambientale (modelli di supporto) `*`/`?`/comma lists).
- **OnSignal** è sincrono; per uso fan-out asincrono `SignalDispatcher` oppure `AsyncSignalProcessor` All'interno del responsabile.
- **SignalConstraints** previene gli infiniti cicli di segnalazione con rilevamento del ciclo e limiti di profondità.

---


## Le registrazioni di snapshot

Da [Istantanee.cs](https://github.com/scottgal/mostlylucidweb/blob/main/Mostlylucid/Helpers/Ephemeral/Snapshots.cs):

```csharp
public sealed record EphemeralOperationSnapshot(
    long Id,
    DateTimeOffset Started,
    DateTimeOffset? Completed,
    string? Key,
    bool IsFaulted,
    Exception? Error,
    TimeSpan? Duration,
    IReadOnlyList<string>? Signals = null,
    bool IsPinned = false)
{
    public bool HasSignal(string signal) => Signals?.Contains(signal) == true;
}

// For result-capturing coordinators
public sealed record EphemeralOperationSnapshot<TResult>(
    long Id,
    DateTimeOffset Started,
    DateTimeOffset? Completed,
    string? Key,
    bool IsFaulted,
    Exception? Error,
    TimeSpan? Duration,
    TResult? Result,
    bool HasResult,
    IReadOnlyList<string>? Signals = null,
    bool IsPinned = false);
```

Questo e' **Solo metadati**. Notate cosa è *non* qui:

- Nessun carico utile
- Nessun dato di input
- Nessun contenuto utente

Abbastanza da rispondere "che cosa è successo, quando, e ha funzionato?" - niente di più.

---


## Come questo si confronta con altri approcci

.NET ti dà diversi modi per fare lavori paralleli. Ecco come la libreria Effimera confronta:

### Parallelo.ForEachAsync (.NET 6+)

```csharp
await Parallel.ForEachAsync(items,
    new ParallelOptions { MaxDegreeOfParallelism = 4 },
    async (item, ct) => await ProcessAsync(item, ct));
```

**Meglio per**: Semplice elaborazione parallela di collezioni dove non c'è bisogno di visibilità.

**Cio' che gli manca**:

- Nessuna operazione di monitoraggio
- Nessuna esecuzione sequenziale per chiave
- Nessuna visibilità su ciò che sta funzionando

**Usare Effimero quando**: È necessario debug / osservabilità, per-key ordering, o l'elaborazione del segnale-reattivo.

### Flusso di dati TPL

```csharp
var block = new ActionBlock<T>(
    async item => await ProcessAsync(item),
    new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 4 });

foreach (var item in items)
    block.Post(item);

block.Complete();
await block.Completion;
```

**Meglio per**: Tubi di flusso dati complessi con ramificazione, fusione, batching.

**Quello che fa bene**:

- Ricca composizione del gasdotto (link blocks together)
- Batching integrato, trasformazione, trasmissione
- Capacità limitata con contropressione

**Usa TPL Dataflow quando**: Hai bisogno di complesse topologie di pipeline (fan-out, fan-in, routing condizionale).

**Usare Effimero quando**: È necessario il monitoraggio delle operazioni, più semplice API, o la coordinazione segnale-reattiva.

### System.Threading.Channels

```csharp
var channel = Channel.CreateBounded<T>(100);

// Producer
foreach (var item in items)
    await channel.Writer.WriteAsync(item);
channel.Writer.Complete();

// Consumer (multiple workers)
var workers = Enumerable.Range(0, 4).Select(async _ =>
{
    await foreach (var item in channel.Reader.ReadAllAsync())
        await ProcessAsync(item);
});
await Task.WhenAll(workers);
```

**Meglio per**: Modelli produttore-consumatore dove si controllano entrambi i lati.

**Quello che fa bene**:

- Ottima prestazione
- Back-pressure tramite canali delimitati
- Separazione dei produttori e dei consumatori

**Usa i canali quando**: Stai costruendo un'infrastruttura personalizzata e hai bisogno del massimo controllo.

**Usare Effimero quando**: Si desidera il monitoraggio dell'operazione e osservabilità senza la piastra caldaia.

### PollyCity name (optional, probably does not need a translation)

```csharp
var policy = Policy
    .Handle<HttpRequestException>()
    .WaitAndRetryAsync(3, attempt => TimeSpan.FromSeconds(Math.Pow(2, attempt)));

await policy.ExecuteAsync(() => ProcessAsync(item));
```

**Meglio per**: Politiche di resilienza (reti, interruttori di circuito, timeout) per le singole operazioni.

**Usa Polly quando**: Hai bisogno di resilienza intorno alle chiamate individuali.

**Usare Effimero quando**: Hai bisogno di coordinamento in molte operazioni con consapevolezza ambientale.

**Combinarli**: Utilizzare Polly all'interno del vostro corpo di lavoro effimero per la resilienza per-operazione.

### MassTransit / NServiceBus

**Meglio per**: Messaggi distribuiti tra i servizi con code durevoli.

**Usa bus di messaggi quando**: Il lavoro deve sopravvivere riavvia processo, abbracciare servizi multipli, o richiedere la consegna garantita.

**Usare Effimero quando**: Il lavoro è in-processo, non ha bisogno di durata, e si desidera osservare leggero.

### Tabella di confronto

| Avvicinamento | Bounded | Tracking | Per-Key | Signals | Self-cleaning | Complexity |
|----------|:-------:|:--------:|:-------:|:-------:|:-------------:|:----------:|
| `Parallel.ForEachAsync` • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • •
| Flusso di dati TPL | ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' Elevato ' '
| Canali | ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' '
| Polly | N/A | ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' basso ' '
| Servizi di fondo | ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' '
|MassTransit/NServiceBus | ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' '
| **Biblioteca effimera** • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • •

---


## Effimera per ogni sincrono: la versione con un colpo solo

Da [ParallelEfemeral.cs](https://github.com/scottgal/mostlylucidweb/blob/main/Mostlylucid/Helpers/Ephemeral/ParallelEphemeral.cs):

```csharp
// Simple parallel processing with tracking
await items.EphemeralForEachAsync(
    async (item, ct) => await ProcessAsync(item, ct),
    new EphemeralOptions { MaxConcurrency = 8 });

// With keyed execution (per-user sequential)
await commands.EphemeralForEachAsync(
    cmd => cmd.UserId,  // Key selector
    async (cmd, ct) => await ExecuteCommandAsync(cmd, ct),
    new EphemeralOptions
    {
        MaxConcurrency = 32,
        MaxConcurrencyPerKey = 1  // Sequential per user
    });
```

### Perché la materia dei tubi a chiave

Immaginate di elaborare i comandi utente:

- L'utente A invia comandi 1, 2, 3
- L'utente B invia i comandi 4, 5, 6

Senza chiave, questi potrebbero eseguire come: 1, 4, 2, 5, 3, 6 - interleaved.

Con `MaxConcurrencyPerKey = 1`:

- I comandi dell'utente A eseguono in ordine: 1 → 2 → 3
- I comandi dell'utente B eseguono in ordine: 4 → 5 → 6
- Ma A e B possono funzionare in parallelo

Questo e' **per entità sequenziale, globalmente parallelo** - critico per i sistemi in cui l'ordine ha importanza all'interno di un'entità.

---


## Il coordinatore del lavoro: una coda a lungo termine

Da [Coordinatore EphemeralWork.cs](https://github.com/scottgal/mostlylucidweb/blob/main/Mostlylucid/Helpers/Ephemeral/EphemeralWorkCoordinator.cs):

```csharp
await using var coordinator = new EphemeralWorkCoordinator<TranslationRequest>(
    async (request, ct) => await TranslateAsync(request, ct),
    new EphemeralOptions
    {
        MaxConcurrency = 8,
        MaxTrackedOperations = 500,
        EnableDynamicConcurrency = true  // Allow runtime adjustment
    });

// Enqueue items over time
await coordinator.EnqueueAsync(new TranslationRequest("Hello", "es"));

// Check status anytime
Console.WriteLine($"Pending: {coordinator.PendingCount}");
Console.WriteLine($"Active: {coordinator.ActiveCount}");

// Get snapshots
var snapshot = coordinator.GetSnapshot();
var running = coordinator.GetRunning();
var failed = coordinator.GetFailed();
var completed = coordinator.GetCompleted();

// Control flow
coordinator.Pause();   // Stop pulling new work
coordinator.Resume();  // Continue

// Adjust concurrency at runtime (requires EnableDynamicConcurrency)
coordinator.SetMaxConcurrency(16);

// Pin important operations to survive eviction
coordinator.Pin(operationId);
coordinator.Unpin(operationId);
coordinator.Evict(operationId);

// When done
coordinator.Complete();
await coordinator.DrainAsync();
```

### Flussi continui con IAsyncNumerable

```csharp
await using var coordinator = EphemeralWorkCoordinator<Message>.FromAsyncEnumerable(
    messageStream,  // IAsyncEnumerable<Message>
    async (msg, ct) => await ProcessMessageAsync(msg, ct),
    new EphemeralOptions { MaxConcurrency = 16 });

await coordinator.DrainAsync();
```

---


## Il coordinatore chiave: per-entity Pipelines

Da [EfemeralKeyedWorkCoordinator.cs](https://github.com/scottgal/mostlylucidweb/blob/main/Mostlylucid/Helpers/Ephemeral/EphemeralKeyedWorkCoordinator.cs):

```csharp
await using var coordinator = new EphemeralKeyedWorkCoordinator<string, Command>(
    cmd => cmd.UserId,  // Key selector
    async (cmd, ct) => await ExecuteCommandAsync(cmd, ct),
    new EphemeralOptions
    {
        MaxConcurrency = 32,
        MaxConcurrencyPerKey = 1,      // Per-user sequential
        EnableFairScheduling = true,   // Prevent hot user starvation
        FairSchedulingThreshold = 10   // Reject if user has 10+ pending
    });

// TryEnqueue returns false if fair scheduling rejects
if (!coordinator.TryEnqueue(hotUserCommand))
{
    await DeferCommandAsync(hotUserCommand);
}

// Per-key visibility
var pendingForUser = coordinator.GetPendingCountForKey("user-123");
var opsForUser = coordinator.GetSnapshotForKey("user-123");
```

---


## Coordinatori per la valutazione dei risultati

Da [EfemeralResultCoordinator.cs](https://github.com/scottgal/mostlylucidweb/blob/main/Mostlylucid/Helpers/Ephemeral/EphemeralResultCoordinator.cs):

```csharp
await using var coordinator = new EphemeralResultCoordinator<SessionInput, SessionResult>(
    async (input, ct) =>
    {
        var fingerprint = await ComputeFingerprintAsync(input.Events, ct);
        return new SessionResult(fingerprint, input.Events.Length);
    },
    new EphemeralOptions { MaxConcurrency = 16 });

await coordinator.EnqueueAsync(session);
coordinator.Complete();
await coordinator.DrainAsync();

// Get just the results (no metadata)
var results = coordinator.GetResults();

// Get snapshots with results + metadata
var snapshots = coordinator.GetSnapshot();

// Get base snapshots without results (privacy-safe)
var baseSnapshots = coordinator.GetBaseSnapshot();

// Filter by success/failure
var successful = coordinator.GetSuccessful();
var failed = coordinator.GetFailed();
```

---


## Concurrency Control (Concurrency Control)

Da [ConcurrencyGates.cs](https://github.com/scottgal/mostlylucidweb/blob/main/Mostlylucid/Helpers/Ephemeral/ConcurrencyGates.cs):

La libreria fornisce due meccanismi di controllo delle concorrenze:

### FixedConcurrencyGate (Predefinito)

- Sostenuto da `SemaphoreSlim`
- Ottima prestazione del percorso a caldo
- Impossibile regolare al runtime

### RegolabileConcurrencyGateCity name (optional, probably does not need a translation)

- Implementazione personalizzata con `Queue<WaiterEntry>`
- Supporti `UpdateLimit()` al runtime
- Abilitato tramite `EnableDynamicConcurrency = true`

```csharp
// Dynamic concurrency adjustment
var coordinator = new EphemeralWorkCoordinator<T>(body,
    new EphemeralOptions
    {
        MaxConcurrency = 4,
        EnableDynamicConcurrency = true
    });

// Later, based on system load:
coordinator.SetMaxConcurrency(16);  // Scale up
coordinator.SetMaxConcurrency(2);   // Scale down
```

---


## Il modello di fabbrica: Coordinatori nominati

Da [DipendenzaIniezione.cs](https://github.com/scottgal/mostlylucidweb/blob/main/Mostlylucid/Helpers/Ephemeral/DependencyInjection.cs):

Come `IHttpClientFactory`, puoi registrare configurazioni con nome:

```csharp
// Registration
services.AddEphemeralWorkCoordinator<TranslationRequest>("fast",
    async (request, ct) => await FastTranslateAsync(request, ct),
    new EphemeralOptions { MaxConcurrency = 32 });

services.AddEphemeralWorkCoordinator<TranslationRequest>("accurate",
    async (request, ct) => await AccurateTranslateAsync(request, ct),
    new EphemeralOptions { MaxConcurrency = 4 });

// Usage
public class TranslationService(IEphemeralCoordinatorFactory<TranslationRequest> factory)
{
    private readonly EphemeralWorkCoordinator<TranslationRequest> _fast =
        factory.CreateCoordinator("fast");
    private readonly EphemeralWorkCoordinator<TranslationRequest> _accurate =
        factory.CreateCoordinator("accurate");
}
```

### Garanzie di fabbrica

1. **Stesso nome = stessa istanza** - Sto chiamando. `CreateCoordinator("fast")` due volte ritorna lo stesso coordinatore
2. **Nomi diversi = istanze diverse** - `"fast"` e `"accurate"` ottenere coordinatori separati
3. **Creazione pigra** - I coordinatori vengono creati solo quando prima richiesto
4. **Convalida di configurazione** - Richiedere un nome non registrato crea un utile errore

---


## API di interrogazione del segnale

Tutti i coordinatori forniscono metodi ottimizzati di interrogazione dei segnali:

```csharp
// Get all signals
var signals = coordinator.GetSignals();

// Filter by key (zero-allocation)
var userSignals = coordinator.GetSignalsByKey("user-123");

// Filter by time range
var recentSignals = coordinator.GetSignalsSince(DateTimeOffset.UtcNow.AddMinutes(-5));
var rangeSignals = coordinator.GetSignalsByTimeRange(from, to);

// Filter by signal name or pattern
var rateSignals = coordinator.GetSignalsByName("rate-limit");
var httpSignals = coordinator.GetSignalsByPattern("http.*");

// Check existence (short-circuits on first match)
if (coordinator.HasSignal("rate-limit"))
    await ThrottleAsync();

if (coordinator.HasSignalMatching("error.*"))
    await AlertAsync();

// Count signals efficiently (no allocation)
var totalSignals = coordinator.CountSignals();
var errorCount = coordinator.CountSignals("error");
var httpCount = coordinator.CountSignalsMatching("http.*");
```

---


## Ottimizzazioni di produzione

### Generazione ID veloce

Da [Generatore Effimero.](https://github.com/scottgal/mostlylucidweb/blob/main/Mostlylucid/Helpers/Ephemeral/EphemeralIdGenerator.cs):

```csharp
internal static class EphemeralIdGenerator
{
    private static long _counter;
    private static readonly long _processStart = Environment.TickCount64;
    private static readonly int _processId = Environment.ProcessId;

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static long NextId()
    {
        var counter = Interlocked.Increment(ref _counter);

        // Combine counter with process-unique seed
        Span<byte> buffer = stackalloc byte[24];
        BitConverter.TryWriteBytes(buffer, _processStart);
        BitConverter.TryWriteBytes(buffer.Slice(8), _processId);
        BitConverter.TryWriteBytes(buffer.Slice(16), counter);

        return unchecked((long)XxHash64.HashToUInt64(buffer));
    }
}
```

- **Senza assegnazione** (impieghi `stackalloc`)
- **Filettatura sicura** (impieghi `Interlocked.Increment`)
- **Unica per tutti i processi** (include l'ID del processo)
- **Non consequenziale** (Hash diffonde il contatore)

### Memory-Safe Long Lived Operation

I coordinatori non memorizzano `Task` riferimenti - solo contatori:

```csharp
private int _activeTaskCount;
private readonly TaskCompletionSource _drainTcs;

// In ExecuteItemAsync:
finally
{
    // Signal drain when last task completes AND channel iteration is done
    if (Interlocked.Decrement(ref _activeTaskCount) == 0 &&
        Volatile.Read(ref _channelIterationComplete))
    {
        _drainTcs.TrySetResult();
    }
}
```

### Pulizia della serratura per chiave

Il coordinatore chiave pulisce automaticamente i semafori inattivi per chiave:

```csharp
private sealed class KeyLock(SemaphoreSlim gate, int maxCount)
{
    public SemaphoreSlim Gate { get; } = gate;
    public int MaxCount { get; } = maxCount;
    public long LastUsedTicks = Environment.TickCount64;
}

// Cleanup runs periodically, removes locks idle > 60 seconds
```

---


## Esempio completo

```csharp
// Program.cs
var builder = WebApplication.CreateBuilder(args);

// Named coordinators
builder.Services.AddEphemeralWorkCoordinator<TranslationRequest>("fast",
    async (req, ct) => await FastTranslateAsync(req, ct),
    new EphemeralOptions { MaxConcurrency = 16 });

// Keyed coordinator for per-user commands
builder.Services.AddEphemeralKeyedWorkCoordinator<string, UserCommand>("commands",
    cmd => cmd.UserId,
    sp =>
    {
        var handler = sp.GetRequiredService<ICommandHandler>();
        return async (cmd, ct) => await handler.HandleAsync(cmd, ct);
    },
    new EphemeralOptions
    {
        MaxConcurrency = 32,
        MaxConcurrencyPerKey = 1,
        EnableFairScheduling = true,
        CancelOnSignals = new HashSet<string> { "system-overload" }
    });

var app = builder.Build();
```

```csharp
// Controller
[ApiController]
[Route("api")]
public class WorkController : ControllerBase
{
    private readonly EphemeralWorkCoordinator<TranslationRequest> _translator;
    private readonly EphemeralKeyedWorkCoordinator<string, UserCommand> _commands;

    public WorkController(
        IEphemeralCoordinatorFactory<TranslationRequest> translationFactory,
        IEphemeralKeyedCoordinatorFactory<string, UserCommand> commandFactory)
    {
        _translator = translationFactory.CreateCoordinator("fast");
        _commands = commandFactory.CreateCoordinator("commands");
    }

    [HttpPost("translate")]
    public async Task<IActionResult> Translate([FromBody] TranslationRequest request)
    {
        await _translator.EnqueueAsync(request);
        return Ok(new { pending = _translator.PendingCount });
    }

    [HttpPost("command")]
    public IActionResult SubmitCommand([FromBody] UserCommand command)
    {
        if (!_commands.TryEnqueue(command))
            return StatusCode(429, "Too many pending commands for this user");
        return Ok();
    }

    [HttpGet("status")]
    public IActionResult GetStatus() => Ok(new
    {
        translator = new
        {
            pending = _translator.PendingCount,
            active = _translator.ActiveCount,
            completed = _translator.TotalCompleted,
            failed = _translator.TotalFailed,
            hasRateLimit = _translator.HasSignal("rate-limit")
        },
        commands = new
        {
            pending = _commands.PendingCount,
            active = _commands.ActiveCount,
            errorCount = _commands.CountSignalsMatching("error.*")
        }
    });
}
```

---


## Conclusione

Abbiamo costruito una libreria di esecuzione effimera completa con:

1. **`EphemeralForEachAsync`** - Lavorazione parallela monofoto con tracciamento
2. **`EphemeralWorkCoordinator`** - Code osservabili a lunga durata
3. **`EphemeralKeyedWorkCoordinator`** - Esecuzione sequenziale per entità con programmazione equa
4. **`EphemeralResultCoordinator`** - Variante di cattura dei risultati
5. **Modello di fabbrica** - Configurazioni con nome come `IHttpClientFactory`
6. **Concorrenze dinamiche** - Regolazione del runtime del parallelismo
7. **Infrastrutture di segnalazione** - Emissione del segnale integrato e interrogatorio

Il modello si trova in un punto dolce:

- Più osservabile di `Parallel.ForEachAsync`
- Più semplice del flusso di dati TPL
- Più integrato dei canali grezzi
- Privacy-safe by design

**Fuoco... e non dimenticarti.**

---


## Collegamenti

- [Parte 1: Fuoco e non *Abbastanza* Dimentica](/blog/fire-and-dont-quite-forget-ephemeral-execution) - la teoria e il modello
- [Parte 3: Segnali effimeri](/blog/ephemeral-signals) - trasformare gli atomi in una rete di rilevamento
- [Documentazione SemaphoreSlim](https://learn.microsoft.com/en-us/dotnet/api/system.threading.semaphoreslim)
- [System.Threading.Channels](https://learn.microsoft.com/en-us/dotnet/core/extensions/channels)
- [Flusso di dati TPL](https://learn.microsoft.com/en-us/dotnet/standard/parallel-programming/dataflow-task-parallel-library)
- [Schema di IHttpClientFactory](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/http-requests)