# mostlylucid.ephemeralM SK1complet; un système parallèle de schémas concurrents étrange dans une cache LRU

<!--category-- mostlylucid-ephemeral -->
<datetime class="hidden">2025-12-14T13:35</datetime>

C’est ce qui a été mon obsession la semaine dernière. Voir le [Parties précédentes](/blog/ephemeral-execution-library) et ce qui a mené à cette étude[Que se passerait-il si une LRU était un contexte d’exécution.'](/blog/learning-lrus-when-capacity-makes-systems-better). Il s’agit maintenant d’un ensemble de paquets Nuget 30 couvrant la plupart des principaux modèles d’exécution simultanée

Trouver la source ici: httpsMSC1github.comM SK3scottgalMST4mostlylucidMSL5atomsMSR6blobMSS7mainMSSL8Mostly Lucid MSS9ephemeralM SS10srcMISS11mosthlylucideMSMSS12ephémeralMSSS13complete

# Premières nations

Lire le [partie précédente sur les signaux ](/blog/ephemeral-signals)pour un aperçu de ce qui en est fait' ses utilisations . Nous présentons ici le ReadmeM SK2md provenant du mostlylucid.ephemeralMSC4pacakage complet qui contient à la fois le core mostlylucideMNK5 paquet éphéméral et tous les patronsMRK6 MMK7atoms MNK8 |MNK9coordinateurs etcMBK10 paquets dans une seule DLL pratiqueMGK11

## Le noyau

OU utiliser le noyau [mostlylucid.éphémère](https://github.com/scottgal/mostlylucid.atoms/tree/main/mostlylucid.ephemeral/src/mostlylucid.ephemeral) a TINY ( littéralement 10 classesM SK2 qui vous donne toutes les fonctionnalités brutes.

## Attributes & DI

OU si vous voulez l'attribut complet basé sur la routage async avec simple `[EphemeralJob]` et service.Add<x>Coordonnateur` enregistrement de style : [mostlylucid.ephemeral.package des attributs](https://github.com/scottgal/mostlylucid.atoms/tree/main/mostlylucid.ephemeral/src/mostlylucid.ephemeral.attributes).

C’est vraisemblablement le sujet de mon blog qui va se poursuivre...vous avez été avertis 🤓

# Mostlylucid.EphemeralM SK1Complet

[![NuGet](https://img.shields.io/nuget/v/mostlylucid.ephemeral.complete.svg)](https://www.nuget.org/packages/mostlylucid.ephemeral.complete)

**Meta-paquet qui renvoie à tous les paquets Mostlylucid.Ephemeral** - exécution asynchrone limitée avec le signal-coordination basée sur .

```bash
dotnet add package mostlylucid.ephemeral.complete
```

Il s'agit d'un paquet méta- qui intégre tous les atomes du noyau de , et des paquets de modèles en tant que dépendances. You can safely mix this with
- NuGet résoudra à une seule version sans collisions de noms de domaine.

## Quoi de neuf dans 2.0

### Résilier les changements

**Modèle d'abonnement SignalSink modifié:**

```csharp
// OLD (1.x) - Event-based
sink.SignalRaised += handler;

// NEW (2.0) - Lock-free Subscribe() returning IDisposable
using var sub = sink.Subscribe(handler);
```

**Le paquet complet est maintenant un méta-package:**

- Précédemment, toutes les sources ont été compilées en un seul DLL ( collisions de nommés à risque)
- Références aux paquets individuels en tant que dépendances
- Sécuritaire à mélanger avec des références individuelles d'emballage dans la même solution

### Nouvelles fonctionnalités

- **Système de détection des ledgers** (`Mostlylucid.Ephemeral.Atoms.Taxonomy.Ledger`)
  
  - `DetectionLedger` - Accumule les contributions du détecteur avec l'agrégation sigmoïde
  - `DetectionContribution` - Méthodes d'usineM SK1 `Bot()`, `Human()`, `Info()`, `VerifiedBot()`, `VerifiedGoodBot()`
  - `CategoryScore.TotalWeight` propriété (rénommée de `Weight`)

- **Surveillance de la propagation des signaux** - Détection du cycle et limitation de la profondeur des chaînes de signalisation

- **Lien de source** - Débug dans la source de bibliothèque directement à partir de votre IDE

Voir [docs/Taxonomy.md](https://github.com/scottgal/mostlylucid.atoms/blob/refs/heads/main/mostlylucid.ephemeral/docs/Taxonomy.md) pour le vocabulaire commun autour du substrat
et escalation.

---


## Table des matières

- [Démarrage rapide](#quick-start)
- [Coordonnateurs de base](#core-coordinators)
- [Configuration](#configuration-ephemeraloptions)
- [Signales](#signals)
- [Atomes](#atoms-building-blocks)
  - [Atome de travail fixe](#fixedworkatom)
  - [KeyedSequentialAtom](#keyedsequentialatom)
  - [SignalAwareAtom](#signalawareatom)
  - [BatchingAtom](#batchingatom)
  - [RetryAtom](#retryatom)
  - [RateLimitAtom](#ratelimitatom)
  - [Atomes de stockage des données](#data-storage-atoms)
  - [MoleculeRunner & AtomTrigger](#moleculerunner--atomtrigger)
  - [Atome de chevauchement Sliding](#slidingcacheatom)
  - [VolatileOperationAtom](#volatileoperationatom)
  - [EphemeralLruCache](#ephemerallrucache)
  - [Atomes de taxonomie](#taxonomy-atoms)
  - [Escalator Atom](#escalatoratom)
  - [Mécanicien d'écho](#echo-maker)
- [Paramètres](#patterns-ready-to-use)
  - [SignalBasedCircuitBreaker](#signalbasedcircuitbreaker)
  - [SignalDrivenBackpressure](#signaldrivenbackpressure)
  - [FanOut contrôlé](#controlledfanout)
  - [AdaptiveRateService](#adaptiverateservice)
  - [Démonstration de la Concurrence dynamique](#dynamicconcurrencydemo)
  - [KeyedPriorityFanOut](#keyedpriorityfanout)
  - [ReactiveFanOutPipeline](#reactivefanoutpipeline)
  - [Détecteur d'anomalie de signal](#signalanomalydetector)
  - [Les lectures coordonnées des signaux](#signalcoordinatedreads)
  - [Signalisation du client Http](#signalinghttpclient)
  - [SignalLogWatcher](#signallogwatcher)
  - [Gestionnaire de signatures de télémétrie](#telemetrysignalhandler)
  - [LongWindowDemo](#longwindowdemo)
  - [SignalReactionShowcase](#signalreactionshowcase)
  - [fenêtre de signature persistante](#persistentsignalwindow)
- [Injection de dépendance](#dependency-injection)

---


## Démarrage rapide

```csharp
using Mostlylucid.Ephemeral;

// Long-lived work coordinator
await using var coordinator = new EphemeralWorkCoordinator<WorkItem>(
    async (item, ct) => await ProcessAsync(item, ct),
    new EphemeralOptions { MaxConcurrency = 8 });

await coordinator.EnqueueAsync(new WorkItem("data"));

// One-shot parallel processing
await items.EphemeralForEachAsync(
    async (item, ct) => await ProcessAsync(item, ct),
    new EphemeralOptions { MaxConcurrency = 8 });
```

### Enregistrement des services

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

builder.Services.AddCoordinator<WorkItem>(
    async (item, ct) => await ProcessAsync(item, ct),
    new EphemeralOptions { MaxConcurrency = 8, MaxTrackedOperations = 128 });

builder.Services.AddEphemeralSignalJobRunner<LogWatcherJobs>();

var app = builder.Build();
app.MapPost("/", async ([FromServices] IEphemeralCoordinatorFactory<WorkItem> factory, WorkItem item) =>
{
    var coordinator = factory.CreateCoordinator();
    await coordinator.EnqueueAsync(item);
    return Results.Accepted();
});

await app.RunAsync();
```

La familiarité `services.AddCoordinator<T>()` aides et `AddEphemeralSignalJobRunner<T>()` conserver l'enregistrement du service
concise, laissez à DI la propriété de l’étangM SK1runner, et faites en sorte que les nouvelles responsabilités /cacheMST4logging stories a single click awayMSC5

### Attribute-emplois dirigés

`mostlylucid.ephemeral.complete` bourses `mostlylucid.ephemeral.attributes`, de sorte que les pipelines attributifs font partie du noyau
surface. Traiter le coureur comme un premier consommateur de signaux de classe -M SK2 les méthodes décorées se joignent à la même cachée
les histoires de pinning, et chaque attribut peut déclarer `Priority`, emploiM SK1niveau `MaxConcurrency`, `Lane`, `Key` sources, signal
émissions, pinM SK1expiration surrides, et retraitsMSC3

Pouvoirs d'attribut clé:

- **Ordonnance des voies &**: Usage `Priority`, `MaxConcurrency`, et `Lane` pour maintenir le travail dans l'ordre déterministe alors que les chemins chauds
  demeurer séparés.
- **Keying & tagging**: `OperationKey`, `KeyFromSignal`, `KeyFromPayload`, et `[KeySource]` vous aider à travailler en groupe avec
  clés significatives pour l’enregistrement, Calendrier équitable , et diagnosticsM SK2
- **Pinnage & pour les retraits**: `Pin`, `ExpireAfterMs`, `AwaitSignals`, `MaxRetries`, et `RetryDelayMs` laisser les gestionnaires s'étendre
  leur visibilité, exécution de la passerelle jusqu’à ce que les dépendances arrivent, et guérissent avec des retraits en émettant des signaux d’échecM SK2
- **Choréographie de signaux**: Emit `EmitOnStart`, `EmitOnComplete`, et `EmitOnFailure` signaler les étapes en aval, log
  appareils de surveillance, ou autres coordonnateurs sans câblage manuelM SK1

```csharp
var sink = new SignalSink();
await using var runner = new EphemeralSignalJobRunner(sink, new[] { new LogWatcherJobs(sink) });

var loggerFactory = LoggerFactory.Create(builder =>
{
    builder.AddConsole();
    builder.AddProvider(new SignalLoggerProvider(new TypedSignalSink<SignalLogPayload>(sink)));
});

var logger = loggerFactory.CreateLogger("orders");
logger.LogError(new EventId(1001, "DbFailure"), "Order store failed");

// Later tasks or other services can also raise watcher-friendly signals directly:
sink.Raise("log.error.orders.dbfailure", key: "orders");
```

```csharp
public sealed class LogWatcherJobs
{
    private readonly SignalSink _sink;

    public LogWatcherJobs(SignalSink sink) => _sink = sink;

    [EphemeralJob("log.error.*", Priority = 1, MaxConcurrency = 2, Lane = "hot:4", EmitOnComplete = new[] { "incident.created" })]
    public Task EscalateAsync(SignalEvent signal)
    {
        Console.WriteLine($"escalating {signal.Signal} for {signal.Key}");
        _sink.Raise("incident.created", key: signal.Key);
        return Task.CompletedTask;
    }

    [EphemeralJob("incident.created", EmitOnStart = new[] { "incident.monitor.start" })]
    public Task NotifyAsync(SignalEvent signal)
    {
        Console.WriteLine($"notified incident for {signal.Key}");
        return Task.CompletedTask;
    }
}
```

Ce coureur siège maintenant au démarrage et réagit chaque fois `log.error.*` ou tout signal émis touche le réservoir. Attribute
Les gestionnaires peuvent aussi lire les clés des signaux/payloadsM SK1 pinwork jusqu’à ce que le débit en aval ne soit pas satisfaisant
emplacement dans les voies de commande. Pour DI-les premières configurations sont utilisées `services.AddEphemeralSignalJobRunner<T>()` (ou la portée
variant) de sorte que le coureur et le puits sont gérés par le contenant.

[Emplois éphémères(Préfixe de signalisation M SK1 "stage", Voie par défaut ≥= ≤"pipelineMSC6
StageJobs classé public
{
[Emploi éphéméral("ingestM SK1 EmitOnComplete = nouveau[]
Public Task IngestAsync(SignalEvent evt) MSC2 ConsoleM SK3OutMST4WriteLineAsynCMSSK5evtMS.SignaleMSS7

    [EphemeralJob("finalize")]
    public Task FinalizeAsync(SignalEvent evt) => Console.Out.WriteLineAsync("final stage");

}

var stageSink = nouvelle SignalSinkM SK1
await using var stageRunner = nouveau EphemeralSignalJobRunnerM SK1stageSink, nouveau[] { nouveaux postes d'encadrement
stageSink.Raise ("stageM SK2ingest");

Pin-les emplois lourds peuvent compter sur `ResponsibilitySignalManager.PinUntilQueried` (patterne d'accouplement par défaut `responsibility.ack.*`) à
conserver leurs opérations visibles jusqu’à ce qu’un lecteur en aval reçoive la charge de paye, pendant `OperationEchoMaker`/
`OperationEchoAtom` persister le flux de signal final pour que les auditeurs ou les molécules puissent encore “tasteM SK1 l’état final même après
l'atome décède.

### Dépenses prévues

`mostlylucid.ephemeral.complete` contient également `mostlylucid.ephemeral.atoms.scheduledtasks`. Définir cron ou JSON
les calendriers par le biais de `ScheduledTaskDefinition` (cronM SK1 signal, optionnel `key`, `payload`, `description`, `timeZone`, `format`,
`runOnStartup`, etc .), et laisser `ScheduledTasksAtom` d'effectuer des travaux durables par `DurableTaskAtom`. Chaque tâche prévue
améliore le signal configuré à l'intérieur d'une fenêtre de coordonnateur, afin qu'il hérisse du pinningM SK1 de la saisie, et de la sémantique de responsabilité
alors que vos molécules ou pipelines attribuables réagissent à la vague de signaux émise.

Tous les `DurableTask` a l’ordre du jour de la réunion `Name`, `Signal`, optionnel `Key`, même un type `Payload`, et `Description`, donc
Les auditeurs en aval savent immédiatement quel travail a été exécuté et quelles métadonnées doivent être utilisées (filenames, URLs , etcM SK3 pour consommer
`DurableTaskAtom.WaitForIdleAsync()` lorsque vous voulez simplement attendre que le travail en cours se termine
sans remplir l’atome, en gardant le calendrier prêt pour le prochain tick de cron.

### Enregistrement des signaux &

`mostlylucid.ephemeral.logging` miroirs Microsoft.Extensions.Connexion aux signaux et vice-versaM SK2 Démarrer par attacher
`SignalLoggerProvider` à votre usine d'enregistrement pour que les événements soient signalés `log.*` signals, et crochet `SignalToLoggerAdapter` Si
vous voulez que les signaux retournent dans le pipeline standard de log.

```csharp
var sink = new SignalSink();
var typedSink = new TypedSignalSink<SignalLogPayload>(sink);

using var loggerFactory = LoggerFactory.Create(builder =>
{
    builder.AddConsole();
    builder.AddProvider(new SignalLoggerProvider(typedSink));
});

using var watcher = new EphemeralSignalJobRunner(sink, new[] { new LogWatcherJobs(sink) });

var logger = loggerFactory.CreateLogger("orders");
logger.LogError(new EventId(1001, "DbFailure"), "Order store failed");
```

```csharp
public sealed class LogWatcherJobs
{
    private readonly SignalSink _sink;

    public LogWatcherJobs(SignalSink sink) => _sink = sink;

    [EphemeralJob("log.error.*")]
    public Task EscalateAsync(SignalEvent signal)
    {
        _sink.Raise("incident.created", key: signal.Key);
        return Task.CompletedTask;
    }

    [EphemeralJob("incident.created")]
    public Task NotifyAsync(SignalEvent signal)
    {
        Console.WriteLine($"Incident for {signal.Key}");
        return Task.CompletedTask;
    }
}
```

Utilisation `SignalToLoggerAdapter` pour refléter les signaux résultant en retour dans des registres standard, de sorte que votre stade de surveillance voit les deux
les côtés du pont.

---


## Coordonnateurs de base

> **Package:** [mostlylucid.éphémère](https://www.nuget.org/packages/mostlylucid.ephemeral)

### Coordonnateur de travail temporaire&lt;T&gt;

Long-carte de travail vivante avec concurrence limitée et fenêtre observable

```csharp
await using var coordinator = new EphemeralWorkCoordinator<Request>(
    async (req, ct) => await HandleAsync(req, ct),
    new EphemeralOptions
    {
        MaxConcurrency = 8,
        MaxTrackedOperations = 200,
        MaxOperationLifetime = TimeSpan.FromMinutes(5)
    });

await coordinator.EnqueueAsync(request);

// Observe state
var running = coordinator.GetRunning();
var failed = coordinator.GetFailed();
var pending = coordinator.PendingCount;

// Graceful shutdown
coordinator.Complete();
await coordinator.DrainAsync();
```

### Coordonnateur de travail à clés éphémères&lt;TKey, T&gt;

Par -processage séquentiel de clés - articles dont la même clé est traitée selon l’ordre.

```csharp
await using var coordinator = new EphemeralKeyedWorkCoordinator<Order, string>(
    order => order.CustomerId,  // Key selector
    async (order, ct) => await ProcessOrder(order, ct),
    new EphemeralOptions
    {
        MaxConcurrency = 16,      // Total parallel
        MaxConcurrencyPerKey = 1  // Sequential per customer
    });

await coordinator.EnqueueAsync(order);
```

### Coordonnateur du résultat éphémère&lt;TInput, Résultat&gt;

Résultats des opérations asynciques de capture.

```csharp
await using var coordinator = new EphemeralResultCoordinator<Request, Response>(
    async (req, ct) => await FetchAsync(req, ct),
    new EphemeralOptions { MaxConcurrency = 4 });

var id = await coordinator.EnqueueAsync(request);
var snapshot = await coordinator.WaitForResult(id);
if (snapshot.HasResult)
    Console.WriteLine(snapshot.Result);
```

### Coordonnateur de travail prioritaire&lt;T&gt;

Plusieurs voies prioritaires avec une cohérence configurable par voie.

```csharp
var coordinator = new PriorityWorkCoordinator<WorkItem>(
    async (item, ct) => await ProcessAsync(item, ct),
    new PriorityWorkCoordinatorOptions<WorkItem>(
        Lanes: new[] { new PriorityLane("high"), new PriorityLane("normal"), new PriorityLane("low") }
    ));

await coordinator.EnqueueAsync(item, "high");
```

---


## Configuration (Options éphémères)

```csharp
new EphemeralOptions
{
    // Concurrency
    MaxConcurrency = 8,                    // Max parallel operations
    MaxConcurrencyPerKey = 1,              // For keyed coordinators
    EnableDynamicConcurrency = false,      // Allow runtime adjustment

    // Memory
    MaxTrackedOperations = 200,            // Window size (LRU eviction)
    MaxOperationLifetime = TimeSpan.FromMinutes(5),

    // Fair scheduling (keyed only)
    EnableFairScheduling = false,          // Prevent hot key starvation
    FairSchedulingThreshold = 10,

    // Signals
    Signals = sharedSink,                  // Shared signal sink
    OnSignal = evt => { },                 // Sync callback
    OnSignalAsync = async (evt, ct) => { }, // Async callback
    CancelOnSignals = new HashSet<string> { "circuit-open" },
    DeferOnSignals = new HashSet<string> { "backpressure" },
    DeferCheckInterval = TimeSpan.FromMilliseconds(100),
    MaxDeferAttempts = 50,

    // Signal handler limits
    MaxConcurrentSignalHandlers = 4,
    MaxQueuedSignals = 1000
}
```

---


## Signales

Opérations émettent des signaux pour le croisement-observabilité de coupeM SK1

```csharp
// Query signals
bool hasError = coordinator.HasSignal("error");
int count = coordinator.CountSignals("error");
var errors = coordinator.GetSignalsByPattern("error.*");

// Shared sink across coordinators
var sink = new SignalSink();
var c1 = new EphemeralWorkCoordinator<A>(body, new EphemeralOptions { Signals = sink });
var c2 = new EphemeralWorkCoordinator<B>(body, new EphemeralOptions { Signals = sink });
sink.Raise("system.busy");  // Both see it
```

---


## Signales de responsabilité & Finalisation

Nécessité de garder les résultats visibles assez longtemps pour les consommateurs en aval? `ResponsibilitySignalManager` vous permet d'établir un
opération jusqu’à ce qu’un signal d’ack arrive (patterne par défaut `responsibility.ack.*` avec la clé=`operationId`). Fournir un
optionnel `description` de façon à ce que l’opération puisse décrire sa responsabilité, et fixer `maxPinDuration` de façon gracieuse
auto-c'est clair si le consommateur n'apparaît jamais .

```csharp
var manager = new ResponsibilitySignalManager(coordinator, sink, maxPinDuration: TimeSpan.FromMinutes(5));
if (manager.PinUntilQueried(operationId, "file.ready", ackKey: fileId, description: "Awaiting fetch"))
{
    sink.Raise("file.ready", key: fileId);
}
// Consumer acknowledges the work
sink.Raise("file.ready.ack", key: fileId);
```

```csharp
using Mostlylucid.Ephemeral.Patterns;

var notes = new LastWordsNoteAtom(async note => await noteRepository.SaveAsync(note));
coordinator.OperationFinalized += snapshot =>
{
    var note = new LastWordsNote(
        OperationId: snapshot.OperationId,
        Key: snapshot.Key,
        Signal: snapshot.Signals?.FirstOrDefault(),
        Timestamp: DateTimeOffset.UtcNow);

    _ = notes.EnqueueAsync(note);
};
```

`LastWordsNote` Il demeure mince (ID d'exploitation,keyM SK2signe ,tampe de tempsMSC4 afin que vous puissiez enregistrer tout état minimal que vous désiriez
avant la collecte de l’opération.

Le coordonnateur garde également un bref - écho vivant des signaux définitifs ( activé par `EnableOperationEcho`) que vous pouvez
d'inspection `GetEchoes()` lorsque vous avez besoin de répéter la vague signalisée trimmée sans maintenir le fonctionnement complet autour.

```csharp
var recentErrors = coordinator.GetEchoes(pattern: "error.*")
    .Where(e => e.Timestamp > DateTimeOffset.UtcNow - TimeSpan.FromMinutes(1))
    .ToList();

if (recentErrors.Any())
    logger.LogWarning("Trimmed errors: {Count}", recentErrors.Count);
```

`OperationEchoRetention` et `OperationEchoCapacity` laissez-vous équilibrer le nombre d’échoes que vous gardez et la durée de leur durée.
pour que vous puissiez répéter les mots suivants :

Le gestionnaire débouche automatiquement lorsque l'ack prend feu, mais vous pouvez appeler `CompleteResponsibility(operationId)` à la fin de l’exercice
Responsabilité précoce (eM SK1g ., sur les retraits). Les opérations augmentent encore `OperationFinalized` lorsque la fenêtre les trime, ainsi
souscrire si vous voulez émettre un signal définitif, diagnostic log, ou lancer M SK2 dernières mots ” nettoyageMSC4

---


## Atomes (Bloques de construction)

### Atome de travail fixe

> **Package:
> ** [mostlylucid.epheméralM SK1atoms.fixedwork](https://www.nuget.org/packages/mostlylucid.ephemeral.atoms.fixedwork)

Base de travail fixe avec des statistiques. Enveloppeur API minimal autour du coordonnateur EphemeralWork.

```csharp
using Mostlylucid.Ephemeral.Atoms.FixedWork;

await using var atom = new FixedWorkAtom<WorkItem>(
    async (item, ct) => await ProcessAsync(item, ct),
    maxConcurrency: 4,
    maxTracked: 200);

await atom.EnqueueAsync(item);

// Get stats
var (pending, active, completed, failed) = atom.Stats();
Console.WriteLine($"Completed: {completed}, Failed: {failed}");

// Get recent operations
var snapshot = atom.Snapshot();

// Graceful shutdown
await atom.DrainAsync();
```

---


### KeyedSequentialAtom

> **Package:
> ** [mostlylucid.epheméralM SK1atoms.keyedsequential](https://www.nuget.org/packages/mostlylucid.ephemeral.atoms.keyedsequential)

Per-processage séquentiel clé avec calendrier équitable facultatif

```csharp
using Mostlylucid.Ephemeral.Atoms.KeyedSequential;

await using var atom = new KeyedSequentialAtom<Order, string>(
    keySelector: order => order.CustomerId,
    body: async (order, ct) => await ProcessOrder(order, ct),
    maxConcurrency: 16,
    perKeyConcurrency: 1,           // Sequential per key
    enableFairScheduling: true);    // Prevent hot key starvation

await atom.EnqueueAsync(order1);  // Customer A
await atom.EnqueueAsync(order2);  // Customer A - waits for order1
await atom.EnqueueAsync(order3);  // Customer B - parallel with A

var (pending, active, completed, failed) = atom.Stats();
await atom.DrainAsync();
```

---


### SignalAwareAtom

> **Package:
> ** [mostlylucid.epheméralM SK1atoms.signalaware](https://www.nuget.org/packages/mostlylucid.ephemeral.atoms.signalaware)

Pauser ou annuler l'apport en fonction des signaux ambiants.

```csharp
using Mostlylucid.Ephemeral.Atoms.SignalAware;

var sink = new SignalSink();

await using var atom = new SignalAwareAtom<WorkItem>(
    async (item, ct) => await ProcessAsync(item, ct),
    cancelOn: new HashSet<string> { "shutdown", "circuit-open" },
    deferOn: new HashSet<string> { "backpressure.*" },
    deferInterval: TimeSpan.FromMilliseconds(100),
    maxDeferAttempts: 50,
    signals: sink,
    maxConcurrency: 8);

// Enqueue work
await atom.EnqueueAsync(item);

// Raise ambient signals
atom.Raise("backpressure.downstream");  // New items defer
sink.Raise("shutdown");                  // New items rejected (returns -1)

await atom.DrainAsync();
```

---


### BatchingAtom

> **Package:
> ** [mostlylucid.epheméralM SK1atoms.batching](https://www.nuget.org/packages/mostlylucid.ephemeral.atoms.batching)

Collecter les articles en lots selon la taille ou l’intervalle de temps.

```csharp
using Mostlylucid.Ephemeral.Atoms.Batching;

await using var atom = new BatchingAtom<LogEntry>(
    onBatch: async (batch, ct) =>
    {
        Console.WriteLine($"Flushing {batch.Count} entries");
        await FlushToDatabase(batch, ct);
    },
    maxBatchSize: 100,
    flushInterval: TimeSpan.FromSeconds(5));

// Items are batched automatically
atom.Enqueue(new LogEntry("User logged in"));
atom.Enqueue(new LogEntry("Request received"));
// ... batch flushes when full OR after 5 seconds
```

---


### RetryAtom

> **Package:** [mostlylucid.epheméral.atomsM SK2retry](https://www.nuget.org/packages/mostlylucid.ephemeral.atoms.retry)

Enveloppe de réessai d'arrêt expositionnel.

```csharp
using Mostlylucid.Ephemeral.Atoms.Retry;

await using var atom = new RetryAtom<ApiRequest>(
    async (req, ct) => await CallExternalApi(req, ct),
    maxAttempts: 3,
    backoff: attempt => TimeSpan.FromMilliseconds(100 * Math.Pow(2, attempt)),
    maxConcurrency: 4);

// Automatically retries on failure with exponential backoff
// Attempt 1: immediate
// Attempt 2: 200ms delay
// Attempt 3: 400ms delay
await atom.EnqueueAsync(new ApiRequest("https://api.example.com"));

await atom.DrainAsync();
```

---


### Atomes de stockage des données

> **Package:** [Données principalementlucides.éphémèresM SK1atoms.données](https://www.nuget.org/packages/mostlylucid.ephemeral.atoms.data)

Configuration partagée pour le stockage des atomes (`DataStorageConfig`, `IDataStorageAtom<TKey, TValue>`) plus le signal
conventions qui conduisent le fichier, SQLite, et les adaptateurs PostgreSQLM SK2

```csharp
using Mostlylucid.Ephemeral.Atoms.Data;
using Mostlylucid.Ephemeral.Atoms.Data.File;

var sink = new SignalSink();
var config = new DataStorageConfig
{
    DatabaseName = "orders",
    SignalPrefix = "save.data",
    LoadSignalPrefix = "load.data",
    DeleteSignalPrefix = "delete.data",
    MaxConcurrency = 1
};

await using var storage = new FileDataStorageAtom<string, Order>(sink, config, "./orders");

storage.EnqueueSave("order-123", new Order { Id = "order-123", Total = 42.00m });
var loaded = await storage.LoadAsync("order-123");
```

Utiliser les mêmes `DataStorageConfig` avec [
`Mostlylucid.Ephemeral.Atoms.Data.Sqlite`](https://www.nuget.org/packages/mostlylucid.ephemeral.atoms.data.sqlite) ou [
`Mostlylucid.Ephemeral.Atoms.Data.Postgres`](https://www.nuget.org/packages/mostlylucid.ephemeral.atoms.data.postgres)
implémentations pour une persistance durable, signal-driven persistence powered by SQLite /PostgresM SK3 Les tâches attributives peuvent être abonnées à
`saved.data.{dbname}` signals pour amorcer le travail en aval pendant que `load.data.{dbname}` déclenche les caches d'hydrates.

---


### MoleculeRunner & AtomTrigger

> **Package:
> ** [Molécules principalementlucides.éphémèresM SK1atoms.molécules](https://www.nuget.org/packages/mostlylucid.ephemeral.atoms.molecules)

Plans combinés avec `MoleculeBlueprintBuilder` vous définissez les atomes (payementM SK1inventaire,expéditionMSC3
notification) qui devrait fonctionner lorsque un signal tel que `order.placed` Arrivée. `MoleculeRunner` écoute le déclencheur
pattern, crée un modèle partagé `MoleculeContext`, et exécute chaque étape pendant que vous vous abonnez à la mise en place de l’événement.
`AtomTrigger` lorsque le signal d’un atome' doit démarrer un autre coordonnateur ou une moléculeM SK1

```csharp
var sink = new SignalSink();
var blueprint = new MoleculeBlueprintBuilder("order", "order.placed")
    .AddAtom(async (ctx, ct) => await paymentCoordinator.EnqueueAsync(ctx.TriggerSignal.Key!, ct))
    .AddAtom(async (ctx, ct) =>
    {
        ctx.Raise("order.payment.complete", ctx.TriggerSignal.Key);
        await inventoryCoordinator.EnqueueAsync(ctx.TriggerSignal.Key!, ct);
    })
    .Build();

await using var runner = new MoleculeRunner(sink, new[] { blueprint }, serviceProvider);
using var trigger = new AtomTrigger(sink, "order.payment.complete", async (signal, ct) =>
{
    await notificationCoordinator.EnqueueAsync(signal.Key!, ct);
});

sink.Raise("order.placed", key: "order-42");
```

Les étapes moléculaires peuvent susciter des signaux additionnels (`ctx.Raise("order.shipping.start")`) de sorte que le reste du système reçoit
baton.

---


### Atome de chevauchement Sliding

> **Package:
> ** [mostlylucid.epheméralM SK1atoms.slidingcache](https://www.nuget.org/packages/mostlylucid.ephemeral.atoms.slidingcache)

Cache avec échéance sèche - l'accès à un résultat rétablit son TTL.

```csharp
using Mostlylucid.Ephemeral.Atoms.SlidingCache;

await using var cache = new SlidingCacheAtom<string, UserProfile>(
    async (userId, ct) => await LoadUserProfileAsync(userId, ct),
    slidingExpiration: TimeSpan.FromMinutes(5),
    absoluteExpiration: TimeSpan.FromHours(1),
    maxSize: 1000);

// First call: computes and caches
var profile = await cache.GetOrComputeAsync("user-123");

// Second call within 5 minutes: returns cached, resets TTL
var cached = await cache.GetOrComputeAsync("user-123");

// Try get without computation (still resets TTL on hit)
if (cache.TryGet("user-123", out var profile))
    Console.WriteLine(profile.Name);

// Get stats
var stats = cache.GetStats();
Console.WriteLine($"Entries: {stats.TotalEntries}, Hot: {stats.HotEntries}");
```

### VolatileOperationAtom

> **Package:
> ** [mostlylucid.épheméral.atomsM SK2volatile](https://www.nuget.org/packages/mostlylucid.ephemeral.atoms.volatile)

Les opérations de chute sont effectuées dès qu’elles émettent un signal d’arrêt, ce qui permet à la fenêtre de demeurer léger même sous une très haute puissance.
atom entend un modèle configurable (default `kill.*`) sur le partage `SignalSink`, appels `IOperationEvictor.TryKill`
avec l’opération capturée ID, et permet encore au coordonnateur d’allumer ses crochets de finalisation echo/ avant l’entrée
disparue.

```csharp
var sink = new SignalSink();
await using var coordinator = new EphemeralWorkCoordinator<JobItem>(
    async (job, ct) => await QuickProcessAsync(job, ct),
    new EphemeralOptions
    {
        Signals = sink,
        EnableOperationEcho = true,
        OperationEchoRetention = TimeSpan.FromSeconds(30)
    });

using var volatileAtom = new VolatileOperationAtom(sink, coordinator);

// inside the job: emitter.Emit("kill.job");
```

Paier l'atome avec `OperationEchoMaker`/`OperationEchoAtom` tel que typé `*.echo.*` les signaux survivent la mort comme un petit TTL’d
enregistrement.

### EphemeralLruCache

> **Package:** noyau (`mostlylucid.ephemeral`) — auto- optimisation du cache avec TTL sliding sur chaque coup et TTL étendu pour
> clés chaudes.

```csharp
using Mostlylucid.Ephemeral;

var cache = new EphemeralLruCache<string, Widget>(new EphemeralLruCacheOptions
{
    DefaultTtl = TimeSpan.FromMinutes(5),
    HotKeyExtension = TimeSpan.FromMinutes(30),
    HotAccessThreshold = 3,
    MaxSize = 10_000,
    SampleRate = 5 // emit 1 in 5 signals
});

var widget = await cache.GetOrAddAsync("widget:42", async key =>
{
    var data = await LoadWidgetAsync(key);
    return data!;
});

// Stats and signals to see how the cache self-focuses on hot keys
var stats = cache.GetStats();              // hot/expired counts, size
var signals = cache.GetSignals("cache.*"); // cache.hot/evict/miss/hit
```

> Remarque: `MemoryCache` peut être configuré pour l'expiration sliding, mais il n'émet jamais les signaux chauds/cold ou prolonge TTL
> pour clés chaudes. `EphemeralLruCache` est l’auto-optimisation par défaut dans le paquet de base ( et dans `SqliteSingleWriter`)
> whenever you want the cache to focus on the active working set.

### Taille de la fenêtre Atome

> **Package:
> ** [mostlylucid.epheméralM SK1atoms.windowsize](https://www.nuget.org/packages/mostlylucid.ephemeral.atoms.windowsize)

L'atome écoute `window.size.*` ou `window.time.*` commandes, colle la capacité requiseM SK1retention, et appelle
`SignalSink.UpdateWindowSize` vous pouvez ainsi élargir ou réduire la fenêtre partagée à partir d’un autre coordonnateur ou surveillant de signaux.

```csharp
var sink = new SignalSink(maxCapacity: 250);
await using var atom = new WindowSizeAtom(sink);

sink.Raise("window.size.decrease:50");
sink.Raise("window.time.set:00:02:00");
```

### Atomes de taxonomie

> **Packages:**
> 
> - Base
>   contrats: [mostlylucid.epheméralM SK1atoms.taxonomie](https://www.nuget.org/packages/mostlylucid.ephemeral.atoms.taxonomy) (
>   inclure `MultiTaxonomyAtom`)
> - Types d'atomes: `mostlylucid.ephemeral.atoms.taxonomy.sensor`, `.extractor`, `.embedder`, `.retriever`, `.proposer`,
>   > `.constrainer`, `.ranker`, `.renderer`, `.coordinator`, `.feedback`, `.guard`

Types d'atomes génériques qui s'alignent sur la taxonomie :
Rendreur, CoordonnateurM SK1 Rétroaction , Garde). Chaque atome émet un signal type à l'achèvement et transporte un AtomContract pour
Déterminisme et métadonnées de persistance. Installer le paquet d'atomes spécifique dont vous avez besoin M SK1l'exemple ci-dessous utilise le proposer).
Utilisation `MultiTaxonomyAtom` du paquet de base lorsque vous devez combiner plusieurs types dans un seul contrat.

```csharp
using Mostlylucid.Ephemeral;
using Mostlylucid.Ephemeral.Atoms.Taxonomy;

public sealed record Proposal(string Text, double Confidence);

var sink = new SignalSink();
var proposer = new ProposerAtom<string, Proposal>(
    sink,
    async (prompt, ct) => new Proposal($"Echo: {prompt}", 0.42),
    outputSignal: "proposal.created",
    keySelector: prompt => prompt);

await proposer.EnqueueAsync("hello");
```

### Escalator Atom

> **Package:
> ** [mostlylucid.epheméralM SK1atoms.escalator](https://www.nuget.org/packages/mostlylucid.ephemeral.atoms.escalator)

Promouvoir les signaux éphémères typés, dans des réservoirs durablesM SK1 EscalatorLes atomes sont la façon préférée de maintenir les sorties
Short-coordonnateurs vivants.

```csharp
using Mostlylucid.Ephemeral;
using Mostlylucid.Ephemeral.Atoms.Data.File;
using Mostlylucid.Ephemeral.Atoms.Escalator;

public sealed record EscalationPayload(string Kind, string? EvidenceId, double Confidence);

var sink = new SignalSink();
var typed = new TypedSignalSink<EscalationPayload>(sink);

await using var storage = new FileDataStorageAtom<string, EscalationPayload>(
    sink,
    new FileDataStorageConfig { DatabaseName = "signals" });

Func<SignalEvent<EscalationPayload>, CancellationToken, Task> audit = (evt, ct) =>
{
    Console.WriteLine($"audit: {evt.Signal} {evt.Key}");
    return Task.CompletedTask;
};

var targets = new[]
{
    new EscalationTarget<EscalationPayload>(
        "store",
        (evt, ct) => storage.SaveAsync(evt.Key ?? evt.OperationId.ToString(), evt.Payload, ct)),
    new EscalationTarget<EscalationPayload>("audit", audit)
};

await using var escalator = new EscalatorAtom<EscalationPayload>(
    sink,
    typed,
    targets,
    new EscalatorAtomOptions<EscalationPayload>
    {
        EscalateSignalPattern = "escalate.*",
        EmitOnSuccess = "escalation.persisted",
        EmitOnFailure = "escalation.failed"
    });

typed.Raise("escalate.signal", new EscalationPayload("risk", "file-42", 0.81), key: "order-123");
```

### Mécanicien d'écho

> **Package:** [mostlylucid.épheméralM SK1atoms.echo](https://www.nuget.org/packages/mostlylucid.ephemeral.atoms.echo)

Capture de la “ dernière phrase ” que l'opération émet avant qu'elle ne soit trimmée.
charges de travail (compatible `ActivationSignalPattern` / `CaptureSignalPattern`) et quand `OperationFinalized` les incendies qu’il produit
`OperationEchoEntry<TPayload>` les dossiers que vous pouvez conserver par `OperationEchoAtom<TPayload>`.

```csharp
var sink = new SignalSink();
var typedSink = new TypedSignalSink<EchoPayload>(sink);
var echoAtom = new OperationEchoAtom<EchoPayload>(async echo => await repository.AppendAsync(echo));

await using var coordinator = new EphemeralWorkCoordinator<JobItem>(ProcessAsync);
using var maker = coordinator.EnableOperationEchoing(
    typedSink,
    echoAtom,
    new OperationEchoMakerOptions<EchoPayload>
    {
        ActivationSignalPattern = "echo.capture",
        CaptureSignalPattern = "echo.*",
        MaxTrackedOperations = 128
    });

typedSink.Raise("echo.capture", new EchoPayload("order-1", "archived"), key: "order-1");
```

Les emplois d’attributes ne font qu’augmenter le signal type avec quel que soit l’état qu’ils jugent critique, et le fabricant garde la série de travail
bounded pendant que vous persistez à l’écho.

---


## Patterns (Ready-toM SK2UseMSC3

### SignalBasedCircuitBreaker

> **Package:
> ** [mostlylucid.epheméralM SK1patterns.circuitbreaker](https://www.nuget.org/packages/mostlylucid.ephemeral.patterns.circuitbreaker)

Écrasateur de circuit sans état utilisant la fenêtre historique des signaux.

```csharp
using Mostlylucid.Ephemeral.Patterns.CircuitBreaker;

var breaker = new SignalBasedCircuitBreaker(
    failureSignal: "api.failure",
    threshold: 5,
    windowSize: TimeSpan.FromSeconds(30));

// Check before making calls
if (breaker.IsOpen(coordinator))
{
    var retryAfter = breaker.GetTimeUntilClose(coordinator);
    throw new CircuitOpenException("Too many failures", retryAfter);
}

// Pattern matching variant
if (breaker.IsOpenMatching(coordinator, "error.*"))
    throw new CircuitOpenException("Error pattern detected");

// Get current failure count
int failures = breaker.GetFailureCount(coordinator);
```

---


### SignalDrivenBackpressure

> **Package:
> ** [Prédominantment clairs.éphémèresM SK1patternes.pressure arrière](https://www.nuget.org/packages/mostlylucid.ephemeral.patterns.backpressure)

Gestion de la profondeur des couloirs avec report automatique sur les signaux de pression arrière.

```csharp
using Mostlylucid.Ephemeral.Patterns.Backpressure;

var sink = new SignalSink();

await using var coordinator = SignalDrivenBackpressure.Create<WorkItem>(
    async (item, ct) => await ProcessAsync(item, ct),
    sink,
    maxConcurrency: 4);

// Enqueue work
await coordinator.EnqueueAsync(item);

// When downstream is slow
sink.Raise("backpressure.downstream");  // New work auto-defers

// When recovered
sink.Retract("backpressure.downstream"); // Work resumes
```

---


### FanOut contrôlé

> **Package:
> ** [plus lumineux.éphémèresM SK1patterns.fanout contrôlé](https://www.nuget.org/packages/mostlylucid.ephemeral.patterns.controlledfanout)

Global + par -gage de clés pour le parallèle contrôléM SK2

```csharp
using Mostlylucid.Ephemeral.Patterns.ControlledFanOut;

await using var fanout = new ControlledFanOut<string, Request>(
    keySelector: req => req.TenantId,
    body: async (req, ct) => await ProcessAsync(req, ct),
    maxGlobalConcurrency: 100,  // Total parallel across all tenants
    perKeyConcurrency: 5);      // Max 5 parallel per tenant

// Items for same tenant processed with limit
await fanout.EnqueueAsync(requestA);  // Tenant1
await fanout.EnqueueAsync(requestB);  // Tenant1 - waits if 5 already running
await fanout.EnqueueAsync(requestC);  // Tenant2 - parallel with Tenant1

await fanout.DrainAsync();
```

---


### AdaptiveRateService

> **Package:
> ** [taux d'adaptation](https://www.nuget.org/packages/mostlylucid.ephemeral.patterns.adaptiverate)

Signale-limitation du taux de conduite avec arrêt automatiqueM SK1

```csharp
using Mostlylucid.Ephemeral.Patterns.AdaptiveRate;

await using var service = new AdaptiveRateService<ApiRequest>(
    async (req, ct) => await CallApiAsync(req, ct),
    maxConcurrency: 8);

// Process with automatic rate limit handling
await service.ProcessAsync(request);

// When API returns 429, emit signal with retry-after
// Signal: "rate-limit:500ms"
// Service auto-parses and delays

Console.WriteLine($"Pending: {service.PendingCount}, Active: {service.ActiveCount}");
```

---


### Démonstration de la Concurrence dynamique

> **Package:
> ** [.éphémèresM SK1patternes.concurrence dynamique](https://www.nuget.org/packages/mostlylucid.ephemeral.patterns.dynamicconcurrency)

Étalonnage de la cohérence du temps d'exécution fondé sur les signaux de charge.

```csharp
using Mostlylucid.Ephemeral.Patterns.DynamicConcurrency;

var sink = new SignalSink();

await using var demo = new DynamicConcurrencyDemo<WorkItem>(
    async (item, ct) => await ProcessAsync(item, ct),
    sink,
    minConcurrency: 2,
    maxConcurrency: 32,
    scaleUpPattern: "load.high",
    scaleDownPattern: "load.low");

await demo.EnqueueAsync(item);

// Concurrency adjusts automatically based on signals
sink.Raise("load.high");  // Concurrency doubles (up to max)
sink.Raise("load.low");   // Concurrency halves (down to min)

Console.WriteLine($"Current concurrency: {demo.CurrentMaxConcurrency}");

await demo.DrainAsync();
```

---


### KeyedPriorityFanOut

> **Package:
> ** [plus clairs.éphémèresM SK1patternes.faut de priorité clé](https://www.nuget.org/packages/mostlylucid.ephemeral.patterns.keyedpriorityfanout)

Voies prioritaires avec per-conservation de l’ordonnance par cléM SK1

```csharp
using Mostlylucid.Ephemeral.Patterns.KeyedPriorityFanOut;

await using var fanout = new KeyedPriorityFanOut<string, UserCommand>(
    keySelector: cmd => cmd.UserId,
    body: async (cmd, ct) => await HandleCommand(cmd, ct),
    maxConcurrency: 32,
    perKeyConcurrency: 1,  // Sequential per user
    maxPriorityDepth: 100);

// Normal lane
await fanout.EnqueueAsync(normalCommand);

// Priority lane - jumps the queue for that user
bool accepted = await fanout.EnqueuePriorityAsync(urgentCommand);

// Check lane depths
var counts = fanout.PendingCounts;
Console.WriteLine($"Priority: {counts.Priority}, Normal: {counts.Normal}");

await fanout.DrainAsync();
```

---


### ReactiveFanOutPipeline

> **Package:
> ** [plus clairs.éphémèresM SK1patternes.réactivitéfanout](https://www.nuget.org/packages/mostlylucid.ephemeral.patterns.reactivefanout)

Deux-pipeline en phase avec répression automatique de l'arrière

```csharp
using Mostlylucid.Ephemeral.Patterns.ReactiveFanOut;

await using var pipeline = new ReactiveFanOutPipeline<WorkItem>(
    stage2Work: async (item, ct) => await SlowProcessing(item, ct),
    preStageWork: async (item, ct) => await FastPreprocessing(item, ct),
    stage1MaxConcurrency: 8,
    stage1MinConcurrency: 1,
    stage2MaxConcurrency: 4,
    backpressureThreshold: 32,  // Throttle when stage2 has 32+ pending
    reliefThreshold: 8);        // Resume when stage2 drops below 8

await pipeline.EnqueueAsync(item);

// Stage1 auto-throttles when stage2 backs up
Console.WriteLine($"Stage1 concurrency: {pipeline.Stage1CurrentMaxConcurrency}");
Console.WriteLine($"Stage2 pending: {pipeline.Stage2Pending}");

await pipeline.DrainAsync();
```

---


### Détecteur d'anomalie de signal

> **Package:
> ** [[Traduit par la Rédaction]](https://www.nuget.org/packages/mostlylucid.ephemeral.patterns.anomalydetector)

Moving-détection d'anomalies de fenêtresM SK1

```csharp
using Mostlylucid.Ephemeral.Patterns.AnomalyDetector;

var sink = new SignalSink();

var detector = new SignalAnomalyDetector(
    sink,
    pattern: "error.*",
    threshold: 5,
    window: TimeSpan.FromSeconds(10));

// Check for anomalies
if (detector.IsAnomalous())
{
    Console.WriteLine("Anomaly detected! Too many errors.");
    TriggerAlert();
}

// Get current match count
int errorCount = detector.GetMatchCount();
Console.WriteLine($"Errors in window: {errorCount}");
```

---


### Les lectures coordonnées des signaux

> **Package:
> ** [mostlylucid.ephemeralM SK1patterns.signalcoordinatedreads](https://www.nuget.org/packages/mostlylucid.ephemeral.patterns.signalcoordinatedreads)

Quiesce lit pendant les mises à jour sans bouclage dur.

```csharp
using Mostlylucid.Ephemeral.Patterns.SignalCoordinatedReads;

// Run demo: readers pause when update signal is present
var result = await SignalCoordinatedReads.RunAsync(
    readCount: 10,
    updateCount: 1);

Console.WriteLine($"Reads: {result.ReadsCompleted}, Updates: {result.UpdatesCompleted}");
Console.WriteLine($"Signals: {string.Join(", ", result.Signals)}");

// Manual implementation:
var sink = new SignalSink();

await using var readers = new EphemeralWorkCoordinator<Query>(
    body,
    new EphemeralOptions
    {
        DeferOnSignals = new HashSet<string> { "update.in-progress" },
        Signals = sink
    });

// Readers auto-defer when update is running
sink.Raise("update.in-progress");  // Readers wait
sink.Raise("update.done");         // Readers resume
```

---


### Signalisation du client Http

> **Package:
> ** [mostlylucid.ephemeralM SK1patterns.signalisationhttp](https://www.nuget.org/packages/mostlylucid.ephemeral.patterns.signalinghttp)

Client HTTP avec des signaux de progrès.

```csharp
using Mostlylucid.Ephemeral.Patterns.SignalingHttp;

var httpClient = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://example.com/large-file");

// Create an emitter from your coordinator
// (emitter is any ISignalEmitter - operations implement this)

byte[] data = await SignalingHttpClient.DownloadWithSignalsAsync(
    httpClient,
    request,
    emitter);

// Signals emitted during download:
// - stage.starting
// - progress:0
// - stage.request
// - stage.headers
// - stage.reading
// - progress:25, progress:50, progress:75, progress:100
// - stage.completed
```

---


### SignalLogWatcher

> **Package:
> ** [Vérificateur de signalisation](https://www.nuget.org/packages/mostlylucid.ephemeral.patterns.signallogwatcher)

Voir la fenêtre de signalisation pour les modèles et déclencher des appels de rappel.

```csharp
using Mostlylucid.Ephemeral.Patterns.SignalLogWatcher;

var sink = new SignalSink();

await using var watcher = new SignalLogWatcher(
    sink,
    onMatch: evt =>
    {
        Console.WriteLine($"Error detected: {evt.Signal} at {evt.Timestamp}");
        AlertOps(evt);
    },
    pattern: "error.*",
    pollInterval: TimeSpan.FromMilliseconds(200));

// Watcher runs in background, calling onMatch for each new error signal
sink.Raise("error.database");    // -> onMatch called
sink.Raise("error.timeout");     // -> onMatch called
sink.Raise("info.started");      // -> ignored (doesn't match pattern)
```

---


### Gestionnaire de signatures de télémétrie

> **Package:
> ** [mostlylucid.epheméralM SK1patterns.telemétrie](https://www.nuget.org/packages/mostlylucid.ephemeral.patterns.telemetry)

OpenTelemetry/Intégration de l'inspection des applications.

```csharp
using Mostlylucid.Ephemeral.Patterns.Telemetry;

// Use in-memory for testing, or implement ITelemetryClient for real telemetry
var telemetry = new InMemoryTelemetryClient();

await using var handler = new TelemetrySignalHandler(telemetry);

// Wire up to coordinator
var options = new EphemeralOptions
{
    OnSignal = signal => handler.OnSignal(signal)
};

// Signals are processed asynchronously
// - "error.*" signals -> TrackExceptionAsync
// - "perf.*" signals -> TrackMetricAsync
// - all signals -> TrackEventAsync

Console.WriteLine($"Queued: {handler.QueuedCount}");
Console.WriteLine($"Processed: {handler.ProcessedCount}");
Console.WriteLine($"Dropped: {handler.DroppedCount}");

// Check recorded events
var events = telemetry.GetEvents();
```

---


### LongWindowDemo

> **Package:
> ** [mostlylucid.ephemeralM SK1patterns.longwindowdemo](https://www.nuget.org/packages/mostlylucid.ephemeral.patterns.longwindowdemo)

Démonstration de la configuration d'une large fenêtre pour les pistes de vérification.

```csharp
using Mostlylucid.Ephemeral.Patterns.LongWindowDemo;

// Configure coordinator with large tracking window
var options = new EphemeralOptions
{
    MaxTrackedOperations = 10000,
    MaxOperationLifetime = TimeSpan.FromHours(24)
};
```

---


### SignalReactionShowcase

> **Package:
> ** [mostlylucid.epheméralM SK1patterns.signalreactionshowcase](https://www.nuget.org/packages/mostlylucid.ephemeral.patterns.signalreactionshowcase)

Démontre les patrons de diffusion des signaux et les appels de retour.

```csharp
using Mostlylucid.Ephemeral.Patterns.SignalReactionShowcase;

// See source for signal dispatch examples
// Demonstrates OnSignal, OnSignalAsync, CancelOnSignals, DeferOnSignals
```

---


### fenêtre de signature persistante

> **Package:
> ** [mostlylucid.epheméralM SK1patterns.peristentwindow](https://www.nuget.org/packages/mostlylucid.ephemeral.patterns.persistentwindow)

La fenêtre de signalisation avec la persistance SQLite - survivant le remarrage du processus.

```csharp
using Mostlylucid.Ephemeral.Patterns.PersistentWindow;

await using var window = new PersistentSignalWindow(
    "Data Source=signals.db",
    flushInterval: TimeSpan.FromSeconds(30));

// On startup: restore previous signals
await window.LoadFromDiskAsync(maxAge: TimeSpan.FromHours(24));

// Raise signals as normal
window.Raise("order.completed", key: "order-service");
window.Raise("payment.processed", key: "payment-service");

// Query signals
var recentOrders = window.Sense("order.*");

// Signals automatically flush every 30 seconds
// Also flushes on dispose

// Get stats
var stats = window.GetStats();
Console.WriteLine($"In memory: {stats.InMemoryCount}, Flushed: {stats.LastFlushedId}");
```

---


## Injection de dépendance

```csharp
// Register in Startup/Program.cs
services.AddEphemeralWorkCoordinator<WorkItem>(
    async (item, ct) => await ProcessAsync(item, ct),
    new EphemeralOptions { MaxConcurrency = 8 });

// Named coordinators
services.AddEphemeralWorkCoordinator<WorkItem>("priority",
    async (item, ct) => await ProcessPriorityAsync(item, ct));

// Inject and use
public class MyService(IEphemeralCoordinatorFactory<WorkItem> factory)
{
    public async Task DoWork()
    {
        var coordinator = factory.CreateCoordinator();
        await coordinator.EnqueueAsync(new WorkItem());
    }
}
```

Les racines DI modernes peuvent préférer les aides plus courtes comme : `services.AddCoordinator<T>(...)`,
`services.AddScopedCoordinator<T>(...)`, ou `services.AddKeyedCoordinator<T, TKey>(...)` car ils lisent comme normalement
`AddX` les inscriptions; elles sont simplement déléguées aux aidants spéciaux sous le capuchon de l’Ephemeral-

## Voie + Configuration de clés (Configuration simpleM SK2 puissance cachée)

`mostlylucid.ephemeral.complete` bourses `PriorityWorkCoordinator` et les variantes clés, donc la même M SK1AddCoordinator”
surface que vous utilisez au démarrage peut envoyer le travail à travers des voies désignées. Configurer quelques noms de voies, leur donner facultatif `MaxDepth`,
et s’appuyer sur le coordonnateur pour drainer les voies “hotM SK1 avant de les détruire, tout en observant toutes les clés de requête

```csharp
var sink = new SignalSink();
var lanes = new[]
{
    new PriorityLane("hot:4", CancelOnSignals: new HashSet<string> { "maintenance" }),
    new PriorityLane("normal"),
    new PriorityLane("slow:2")
};

await using var coordinator = new PriorityWorkCoordinator<WorkItem>(
    new PriorityWorkCoordinatorOptions<WorkItem>(
        async (item, ct) => await processor.ProcessAsync(item, ct),
        lanes,
        new EphemeralOptions { Signals = sink }));

await coordinator.EnqueueAsync(new WorkItem("order-42"), laneName: "hot");
await coordinator.EnqueueAsync(new WorkItem("order-43"), laneName: "slow");
```

Utilisation `PriorityKeyedWorkCoordinator` Si vous avez également besoin de commandes par clé, le choix des voies se fait toujours dans la pompe.
construite-le sélecteur de clés interne garde chaque partition séquentielle.

```csharp
var keyed = new PriorityKeyedWorkCoordinator<WorkItem, string>(
    new PriorityKeyedWorkCoordinatorOptions<WorkItem, string>(
        order => order.CustomerId,
        async (order, ct) => await processor.ProcessPerCustomerAsync(order, ct),
        lanes,
        new EphemeralOptions { Signals = sink }));

await keyed.EnqueueAsync(new WorkItem("order-42") { CustomerId = "A" }, laneName: "hot");
await keyed.EnqueueAsync(new WorkItem("order-44") { CustomerId = "B" }, laneName: "normal");
```

Pensez au coordonnateur comme à la cuisinier, aux voies de conduite comme aux vaisseaux et aux clés comme aux gouttes qui servent à maintenir l’ordre de chaque client.
séquentiel—la puissance cachée qui maintient le travail ordonné,rachetéeM SK2 et signalée -aware without extra ceremonyMSC4

---


## Cadre cible

- .NET 6.0, 7.0, | | 3 | 4 | 5

## Licence

Non-licence (domaine publique)