Back to "Φωτιά και μη. Αρκετά. Ξεχάστε - Εφήμερη Εκτέλεση Χωρίς Κράτος"

This is a viewer only at the moment see the article on how this works.

To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk

This is a preview from the server running through my markdig pipeline

Architecture ASP.NET Async CQRS Privacy-Safe Design Systems Design

Φωτιά και μη. Αρκετά. Ξεχάστε - Εφήμερη Εκτέλεση Χωρίς Κράτος

Friday, 12 December 2025

Τα περισσότερα συστήματα async είτε θυμούνται πάρα πολλά (logs, ουρά, επιμένουν σκουπίδια που ποτέ δεν ήθελες)... ή δεν θυμούνται τίποτα σε όλα (φωτιά-και-ξέχασε τις μαύρες τρύπες που εξαφανίζονται τη στιγμή που κάτι πάει στραβά).

Αυτό το άρθρο εισάγει κάτι διαφορετικό:

Ένα μοτίβο όπου κάθε λειτουργία async γίνεται ένα μικροσκοπικό ανιχνεύσιμο, Επιθεωρήσιμη, re-retrievable, Μονάδα εργασίας αυτοκαθαρισμού -και όλο το σύστημα παραμένει ιδιωτικό, περιορισμένο, γρήγορο και καθοριστικό.

Είναι η συντροφιά του προηγούμενου άρθρου μου, **Μαθαίνοντας LRUs - Όταν η υπέρβαση της ικανότητας κάνει το σύστημά σας καλύτερο**Αυτό το ένα εξερευνούσε πώς η περιορισμένη μνήμη + η συρόμενη λήξη γίνεται ένας μηχανισμός επιβίωσης.

Αυτό εξερευνεί το άλλο μισό: Περιορίζεται, εφήμερη εκτέλεση -πώς ένα μικροσκοπικό τροχαίο ρυθμιστικό των ενεργών εργασιών γίνεται αποσφαλματωτής, ένα αρχείο καταγραφής γεγονότων, και ένας κινητήρας αναπαραγωγής, χωρίς να κρατήσει τα δεδομένα των χρηστών.

Το παράδειγμα προέρχεται από το μεταφραστικό widget του blog μου -ένα μικρό UI για τη μετάφραση του περιεχομένου markdown on-the-fly.

Αυτό είναι Μέρος 1 μιας σειράς δύο μερών:

NUGET!!!

Αυτό βρίσκεται τώρα στο πιο διαυγή.ephemerals πακέτο Nuget επίσης πάνω από 20 ως επί το πλείστον διαυγή.ephemerals μοτίβα και "άτομα".

NuGetCity name (optional, probably does not need a translation) Άδεια


Το πρόβλημα - Async Ροές Εργασίας Δημιουργούν Κρυφό Κράτος

Οι προγραμματιστές ASP.NET τείνουν να επιλέγουν από τρεις κακές επιλογές:

Fire-and-forget

Εσύ Task.Run() Κάτι, ελπίζω να τελειώσει, και όταν ανατιναχτεί δεν παίρνεις τίποτα.

Φωτιά και περίμενε.

Κόβεις τις κλωστές που δεν πρέπει να μπλοκάρεις, πεθαίνεις, εκρήγνυται η ευπρέπεια, οι χρήστες σου σε μισούν.

Ακτίνες/κανάλια παντού

Τώρα έχεις:

  • αδέσμευτες ουρές
  • επίμονη κατάσταση που δεν ήθελες να κρατήσεις
  • PII κατά λάθος αποθηκευμένο κάπου
  • ένα κατανεμημένο σύστημα όπου απλά ήθελες μια απλή λειτουργία

Κατανεμημένος εντοπισμός

Χρήσιμο... αλλά εξωτερικό. Επίσης: δεν μπορεί να ξαναπαίξει τίποτα και συχνά διαρρέει δεδομένα που ποτέ δεν σκόπευες να αποθηκεύσεις.

Αυτό που πραγματικά θέλουμε είναι:

  • βραχείας διάρκειας
  • ιδιωτικό
  • Αποσφαλματωτής
  • Επιθεωρήσιμη
  • Περιορίζεται
  • ανακτητέο, αν χρειαστεί.
  • Και μετά έφυγε.

Μια "αρκετά μεγάλη" ανάμνηση του τι κάνει το σύστημα - και τίποτα περισσότερο.


Το μοτίβο - Φωτιά και Μην το λες αυτό. Ξέχνα το.

Ιδού η ιδέα σε μια πρόταση:

Kick off μια λειτουργία async αμέσως, παρακολουθείτε το ρητά μέσω μιας TaskClearingΠηγή, καθαρίστε το καθοριστικά, και κρατήστε ένα μικροσκοπικό rolling buffer των τελευταίων εργασιών, ώστε να μπορείτε να επιθεωρήσετε ή να τα ανακτήσει -χωρίς να διατηρήσετε οποιοδήποτε περιεχόμενο χρήστη.

Κάθεται στα μισά του δρόμου μεταξύ:

  • Προμήθειες γεγονότων
  • Εντοπισμός
  • ουρά εργασίας
  • Μέλλοντα/υποσχέσεις
  • εφήμερες συνεδρίες

...χωρίς να γίνει κάποιος από αυτούς.

flowchart TB
    subgraph Client["Client Request"]
        R[Start Translation]
    end

    subgraph API["API Layer"]
        A[Create TaskId] --> B[Create TaskCompletionSource]
        B --> C[Queue to Channel]
        C --> D[Return TaskId Immediately]
    end

    subgraph Cache["Ephemeral Cache (Bounded)"]
        E[Store TranslateTask]
        F[Max 5 per user]
        G[6hr absolute / 1hr sliding expiry]
    end

    subgraph Worker["Background Worker"]
        H[Read from Channel]
        H --> I[Execute Translation]
        I --> J[Complete TCS]
    end

    R --> A
    B --> E
    E --> F --> G
    C --> H

    style Cache fill:none,stroke:#10b981,stroke-width:2px
    style Worker fill:none,stroke:#6366f1,stroke-width:2px

Ανατομία μιας εργασίας της Εφημερίδος

Η δομή των βασικών δεδομένων είναι TranslateTask -μια μικροσκοπική ατομική μονάδα εκτέλεσης που υποστηρίζεται από ένα Task<TaskCompletion>:

// From TranslateTask.cs
public class TranslateTask(
    string taskId,
    DateTime startTime,
    string language,
    Task<TaskCompletion>? task)
    : TranslateResultTask(taskId, startTime, language)
{
    public Task<TaskCompletion>? Task { get; init; } = task;
}

public record TaskCompletion(
    string? TranslatedMarkdown,
    string OriginalMarkdown,
    string Language,
    bool Complete,
    DateTime? EndTime);

Περιέχει:

  • Μια μοναδική ταυτότητα (διαδίδεται σε όλη τη ροή)
  • Χρονοσφραγίδα (όταν ξεκίνησε)
  • Μεταδεδομένα (γλώσσα -όχι περιεχόμενο χρήστη!)
  • Μια αναφορά στην πραγματική Task<TaskCompletion>
  • Διάρκεια (υπολογιζόμενη κατά την πρόσβαση)
  • Κατάσταση λάθους (που προέρχεται από την κατάσταση εργασίας)
  • Τελικό αποτέλεσμα (μόνο εάν ολοκληρωθεί επιτυχώς)

Τίποτα δεν γράφτηκε στον δίσκο, τίποτα αποθηκευμένο πέρα από το παράθυρο.

AWS έχει Βήμα Λειτουργίες. Azure έχει Ανθεκτικές Λειτουργίες. Έχετε... 30 γραμμές κώδικα που δεν απαιτούν ένα νομοσχέδιο σύννεφο.


Η ολοκλήρωση εργασιώνΠηγή μοτίβο - Γεφυρώνοντας φωτιά-και-ξεχάστε στη φωτιά-και-τρακ

Η μαγεία συμβαίνει στο BackgroundTranslateServiceΑντί για φωτιά-και-ξέχασε, χρησιμοποιούμε ένα Ολοκλήρωση έργουΠηγή -ένα είδος υπόσχεσης που μας επιτρέπει να επιστρέψουμε αμέσως ενώ η δουλειά γίνεται στο παρασκήνιο.

// From BackgroundTranslateService.cs
private readonly Channel<(PageTranslationModel, TaskCompletionSource<TaskCompletion>)>
    _translations = Channel.CreateUnbounded<(PageTranslationModel, TaskCompletionSource<TaskCompletion>)>();

private async Task<Task<TaskCompletion>> Translate(PageTranslationModel message)
{
    // Create a TaskCompletionSource that will eventually hold the result
    var tcs = new TaskCompletionSource<TaskCompletion>();

    // Send the translation request along with the TCS to be processed
    await _translations.Writer.WriteAsync((message, tcs));

    // Return the Task immediately -caller can await it or check status later
    return tcs.Task;
}

Τι είναι το TaskCleinclusionSource;

Αν δεν έχεις χρησιμοποιήσει TaskCompletionSource<T> Πριν, σκεφτείτε το ως ένα Υποσχέσου ότι θα ελέγχεις με το χέρι.. Σε αντίθεση με ένα κανονικό Task που ολοκληρώνει όταν τελειώνει η εργασία του, ένα TCS ολοκληρώνει όταν εσύ κλήση SetResult(), SetException(), ή SetCanceled().

Αυτό σας επιτρέπει:

  1. Επιστρέφω αμέσως. Task στον καλούντα αμέσως.
  2. Κάντε την πραγματική εργασία κάπου αλλού (διαφορετικό νήμα, background service, κ.λπ.)
  3. Ολοκληρώστε την εργασία όταν εσύ Αποφάσισε ότι έγινε.

Είναι η γέφυρα μεταξύ "φωτιά-και-ξέχασε" και "φωτιά-και-track."


Το στρώμα API - Ξεκινώντας μια Μετάφραση

Όταν ένας χρήστης κάνει κλικ στο "Μετάφρασε," το frontend καλεί το API:

// From translations.js
fetch('/api/translate/start-translation', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
        Language: shortCode,
        OriginalMarkdown: markdown
    })
})
.then(response => response.json())
.then(taskId => {
    // Got a taskId immediately -translation is running in background
    console.log("Task ID:", taskId);

    // Poll for updates via HTMX
    htmx.ajax('get', "/editor/get-translations", {
        target: '#translations',
        swap: 'innerHTML'
    });
});

Το API επιστρέφει αμέσως με μια απλή ταυτότητα εργασίας:

// From TranslateAPI.cs
[HttpPost("start-translation")]
public async Task<Results<Ok<string>, BadRequest<string>>> StartTranslation(
    [FromBody] MarkdownTranslationModel model)
{
    if (!backgroundTranslateService.TranslationServiceUp)
        return TypedResults.BadRequest("Translation service is down");

    // Create a unique identifier for this translation task
    var taskId = Guid.NewGuid().ToString("N");
    var userId = Request.GetUserId(Response);

    // Trigger translation -returns Task<TaskCompletion> immediately
    var translationTask = await backgroundTranslateService.Translate(model);

    // Wrap it in our trackable TranslateTask
    var translateTask = new TranslateTask(taskId, DateTime.Now, model.Language, translationTask);

    // Store in the ephemeral cache (bounded, self-cleaning)
    translateCacheService.AddTask(userId, translateTask);

    // Return the task ID to the client -they can poll for status
    return TypedResults.Ok(taskId);
}

Η απάντηση είναι άμεση. Η πραγματική μετάφραση τρέχει στο παρασκήνιο. Ο χρήστης μπορεί να δημοσκοπήσει ή απλά να παρακολουθήσουν την ενημέρωση UI.


Το παράθυρο Rolling Operation -Ένας αυτοκαθαριζόμενος μπουφές

Η βασική εικόνα από την Άρθρο LRU ισχύει και εδώ:

Κράτα αρκετή ιστορία για να αποσφαλματώσεις και να λογικευτείς για το σύστημα... και άσε τη φυσική φθορά να σβήσει οτιδήποτε άλλο.

// From TranslateCacheService.cs
public class TranslateCacheService(IMemoryCache memoryCache)
{
    public void AddTask(string userId, TranslateTask task)
    {
        if (memoryCache.TryGetValue(userId, out CachedTasks? tasks))
        {
            var currentTasks = tasks?.Tasks ?? new List<TranslateTask>();
            currentTasks = currentTasks.OrderByDescending(x => x.StartTime).ToList();

            // Keep only the 5 most recent tasks -bounded window
            if (currentTasks.Count >= 5)
            {
                var lastTask = currentTasks.Last();
                currentTasks.Remove(lastTask);
            }

            currentTasks.Add(task);
            currentTasks = currentTasks.OrderByDescending(x => x.StartTime).ToList();
            tasks!.Tasks = currentTasks;

            memoryCache.Set(userId, tasks, new MemoryCacheEntryOptions
            {
                AbsoluteExpiration = tasks.AbsoluteExpiration,
                SlidingExpiration = TimeSpan.FromHours(1)
            });
        }
        else
        {
            // First task for this user
            var cachedTasks = new CachedTasks
            {
                Tasks = new List<TranslateTask> { task },
                AbsoluteExpiration = DateTime.Now.AddHours(6)
            };
            memoryCache.Set(userId, cachedTasks, new MemoryCacheEntryOptions
            {
                AbsoluteExpiration = cachedTasks.AbsoluteExpiration,
                SlidingExpiration = TimeSpan.FromHours(1)
            });
        }
    }

    public List<TranslateTask> GetTasks(string userId)
    {
        if (memoryCache.TryGetValue(userId, out CachedTasks? tasks))
            return tasks?.Tasks ?? new List<TranslateTask>();
        return new List<TranslateTask>();
    }

    private class CachedTasks
    {
        public List<TranslateTask> Tasks { get; set; } = new();
        public DateTime AbsoluteExpiration { get; set; }
    }
}

Κάθε νέα εργασία:

  • Προστίθεται στο παράθυρο κύλισης του χρήστη
  • Σπρώχνει το παλαιότερο αν είμαστε σε χωρητικότητα (5 μέγ.)
  • Εξατμίζεται εξ ολοκλήρου μετά από 6 ώρες (απόλυτης) ή 1 ώρα αδράνειας (σύρσιμο)
flowchart LR
    subgraph Window["Per-User Task Window (Max 5)"]
        T1[Oldest Task] --- T2[Older] --- T3[Recent] --- T4[Newer] --- T5[Newest]
    end

    New[New Task] -->|Enqueue| Window
    T1 -->|Evicted| Gone[(Expired)]

    style Window fill:none,stroke:#10b981,stroke-width:2px
    style Gone fill:none,stroke:#ef4444,stroke-width:2px

Δεν υπάρχει κίνδυνος κατακράτησης. Δεν PII στην κρύπτη (μόνο ταυτότητες εργασιών, χρονοσφραγίσεις, και γλωσσικούς κώδικες).


Τραβηγμένο νόμισμα - Ο Κυβερνήτης Λουπ

Η υπηρεσία υποβάθρου εφαρμόζει a σταθερό νόμισμα βρόχος χρησιμοποιώντας έναν αναγνώστη καναλιών και Task.WhenAny:

// From BackgroundTranslateService.cs
private async Task TranslateFilesAsync(CancellationToken cancellationToken)
{
    var processingTasks = new List<Task>();

    while (!cancellationToken.IsCancellationRequested)
    {
        // Fill up to IPCount concurrent tasks (e.g., 4 parallel translations)
        while (processingTasks.Count < markdownTranslatorService.IPCount &&
               !cancellationToken.IsCancellationRequested)
        {
            var item = await _translations.Reader.ReadAsync(cancellationToken);
            var translateModel = item.Item1;
            var tcs = item.Item2;

            // Start the task and add it to the list
            var task = TranslateTask(cancellationToken, translateModel, item, tcs);
            processingTasks.Add(task);
        }

        // Wait for ANY of the tasks to complete
        var completedTask = await Task.WhenAny(processingTasks);
        processingTasks.Remove(completedTask);

        // Handle exceptions if needed
        try
        {
            await completedTask;
        }
        catch (Exception ex)
        {
            logger.LogError(ex, "Error translating markdown");
        }
    }
}

Αυτό είναι σαν Κυβερνήτης σε κινητήρα ατμού: περισσότερο φορτίο → πίσω πίεση → φυσικό throttling → σταθερότητα.

Το μοτίβο σας δίνει:

  • Δεν σπείρα υπερφόρτωσης -Δεν μπορώ να γεννήσω απεριόριστες δουλειές
  • Απαλή συμπεριφορά σε πραγματικό χρόνο -πλήρης καθυστέρηση
  • Φυσική λείανση των εκρήξεων -κανάλι απομονώνει τα καρφιά
  • Σταθερότητα υπό φορτίο -παραμορφώνεται χαριτωμένα

Ολοκληρώνοντας την εργασίαΟλοκληρώστε την piηγή

Όταν τελειώσει το έργο μετάφρασης, ολοκληρώνουμε το TCS:

// From BackgroundTranslateService.cs
private async Task TranslateTask(
    CancellationToken cancellationToken,
    PageTranslationModel translateModel,
    (PageTranslationModel, TaskCompletionSource<TaskCompletion>) item,
    TaskCompletionSource<TaskCompletion> tcs)
{
    try
    {
        await retryPolicy.ExecuteAsync(async () =>
        {
            // Do the actual translation work
            var translatedMarkdown = await markdownTranslatorService.TranslateMarkdown(
                translateModel.OriginalMarkdown,
                translateModel.Language,
                cancellationToken);

            // SUCCESS: Complete the TCS with the result
            tcs.SetResult(new TaskCompletion(
                translatedMarkdown,
                translateModel.OriginalMarkdown,
                translateModel.Language,
                true,
                DateTime.Now));
        });
    }
    catch (TranslateException e)
    {
        // FAILURE: Complete the TCS with an exception
        tcs.SetException(new Exception($"Translation failed after 3 retries: {e.Message}"));
    }
    catch (Exception e)
    {
        // UNEXPECTED: Complete the TCS with the exception
        tcs.SetException(e);
    }
}

Του καλούντος. Task<TaskCompletion> - που πήραν αμέσως όταν τηλεφώνησαν. Translate() - Τώρα ολοκληρώνει.

  • await Αν θέλουν να μπλοκάρουν
  • Έλεγχος IsCompleted προς δημοσκόπηση
  • Έλεγχος IsFaulted Για να δούμε αν απέτυχε.
  • Έλεγχος Result για να πάρει το μεταφρασμένο περιεχόμενο

Η Προβολή Κατάστασης - Deriveing State from Task State

Όταν επιδεικνύουμε εργασίες στον χρήστη, προβάλλουμε την κατάσταση ζωντανής εργασίας σε ένα μοντέλο προβολής:

// From TranslateTask.cs
public TranslateResultTask(TranslateTask task, bool includeMarkdown = false)
{
    TaskId = task.TaskId;
    StartTime = task.StartTime;
    Language = task.Language;

    // Check for faulted state first -a faulted task is also "completed" in .NET terms
    if (task.Task?.IsFaulted == true)
    {
        Failed = true;
        Completed = false;
        TotalMilliseconds = (int)(DateTime.Now - task.StartTime).TotalMilliseconds;
    }
    else if (task.Task?.IsCompletedSuccessfully == true)
    {
        Completed = true;
        Failed = false;
        var endTime = task.Task.Result.EndTime;
        TotalMilliseconds = (int)((endTime - task.StartTime)!).Value.TotalMilliseconds;
        EndTime = endTime;
    }
    else
    {
        // Still in progress
        Completed = false;
        Failed = false;
        TotalMilliseconds = (int)(DateTime.Now - task.StartTime).TotalMilliseconds;
    }

    if (Completed && includeMarkdown)
    {
        var result = task.Task?.Result;
        if (result == null) return;
        OriginalMarkdown = result.OriginalMarkdown;
        TranslatedMarkdown = result.TranslatedMarkdown;
    }
}

Σημείωση για το κράτος εργασίας

NET's Task έχει κάποιους ιδιόμορφους συνδυασμούς κατάστασης:

  • IsCompleted είναι αλήθεια για και τα δύο επιτυχής ολοκλήρωση ΚΑΙ ελαττωματικές εργασίες
  • IsCompletedSuccessfully είναι μόνο αλήθεια για την επιτυχία
  • IsFaulted Σημαίνει ότι έριξε μια εξαίρεση
  • IsCanceled Σημαίνει ότι ακυρώθηκε.

Οπότε πρέπει να το ελέγξεις. IsFaulted πριν από τον έλεγχο IsCompletedΑλλιώς θα αντιμετωπίσεις τις αποτυχίες ως επιτυχίες.


The UI - HTMX Polling

Οι δημοσκοπήσεις προβολής για ενημερώσεις που χρησιμοποιούν HTMX hx-trigger:

@* From _GetTranslations.cshtml *@
@{
    var allCompleted = Model.All(x => x.Completed);
    var trigger = allCompleted ? "none" : "every 5s";
}

<div class="translationpoller"
     hx-get="/editor/get-translations"
     hx-swap="outerHTML"
     hx-trigger="@trigger">
    <table class="table">
        @foreach (var item in Model)
        {
            <tr>
                <td>
                    @if (item.Completed)
                    {
                        <a href="#" x-on:click.prevent="viewTranslation('@item.TaskId')">View</a>
                    }
                    else if (item.Failed)
                    {
                        <text>Failed</text>
                    }
                    else
                    {
                        <text>Processing</text>
                    }
                </td>
                <td>
                    @if (item.Completed)
                    {
                        <i class='bx bx-check text-green'></i>
                    }
                    else if (item.Failed)
                    {
                        <i class='bx bx-x text-red'></i>
                    }
                    else
                    {
                        <img src="~/img/3-dots-bounce.svg" />
                    }
                </td>
                <td>@item.Language</td>
                <td>@TimeSpan.FromMilliseconds(item.TotalMilliseconds).Humanize()</td>
            </tr>
        }
    </table>
</div>

Το έξυπνο κομμάτι: hx-trigger="@trigger" αλλαγές με βάση την κατάσταση:

  • Εάν οι εργασίες εξακολουθούν να εκτελούνται: δημοσκόπηση κάθε 5 δευτερόλεπτα
  • Εάν γίνονται όλες οι εργασίες: σταματήστε τις δημοσκοπήσεις ("none")

Αυτό είναι αυτορυθμιζόμενες δημοσκοπήσεις - Σταματάει αυτόματα όταν δεν υπάρχει τίποτα να δεις.


Ανάκτηση του αποτελέσματος

Όταν ο χρήστης κάνει κλικ στο "Προβολή," φέρνουμε την ολοκληρωμένη μετάφραση:

// From TranslateAPI.cs
[HttpGet("get-translation/{taskId}")]
public async Task<Results<JsonHttpResult<TranslateResultTask>, BadRequest<string>>> GetTranslation(
    string taskId)
{
    var userId = Request.GetUserId(Response);
    var tasks = translateCacheService.GetTasks(userId);

    var translationTask = tasks.FirstOrDefault(t => t.TaskId == taskId);
    if (translationTask == null)
        return TypedResults.BadRequest("Task not found");

    // Include the markdown content in the response
    var result = new TranslateResultTask(translationTask, includeMarkdown: true);
    return TypedResults.Json(result);
}

Η JavaScript στη συνέχεια κοσμεί τον επεξεργαστή:

// From translations.js
export function viewTranslation(taskId) {
    fetch(`/api/translate/get-translation/${taskId}`)
        .then(response => response.json())
        .then(data => {
            // Show the translated content area
            document.getElementById("translatedcontent").classList.remove("hidden");

            // Populate the editors
            var originalMde = window.mostlylucid.simplemde.getinstance('translatedcontentarea');
            originalMde.value(data.originalMarkdown);

            var mde = window.mostlylucid.simplemde.getinstance('markdowneditor');
            mde.value(data.translatedMarkdown);
        });
}

Γιατί Αυτό είναι Απορρήτου-Ασφαλής

Επειδή:

  • Κανένα περιεχόμενο χρήστη δεν αποθηκεύεται στην κρύπτη -μόνο μεταδεδομένα εργασιών (ID, χρονοσφραγίδα, γλώσσα)
  • Το περιεχόμενο υπάρχει μόνο στο αποτέλεσμα της εργασίας -που είναι στη μνήμη, προσκολλημένο στο έργο
  • Το παράθυρο είναι περιορισμένο. -max 5 εργασίες ανά χρήστη, μέγιστη κατακράτηση 6 ωρών
  • Όλα αποσυντίθενται φυσικά. -slivering λήξη καθαρίζει τους ανενεργούς χρήστες
  • Τίποτα δεν αγγίζει το δίσκο -Χωρίς κούτσουρα, ουρά, βάση δεδομένων
  • Δεν μπορείτε κατά λάθος να διατηρήσετε το PII -Δεν υπάρχει πουθενά να πάει.

Αυτή είναι η αρχιτεκτονική. Εύχομαι τα προγράμματα περιήγησης και τα πλαίσια frontend να είχαν χρησιμοποιηθεί για εργασίες συνεδρίας.


Η Αναδυόμενη Συμπεριφορά

Τι παίρνεις χωρίς να σκέφτεσαι πολύ σκληρά:

Πως να το κάνετε αυτό; |---------|-----| | Σταθερότητα Βραχυκύκλωμα μέσω καναλιού + μοτίβο semaphore | Αρνητικός βρόχος ανατροφοδότησης Περισσότερο φορτίο → πίσω πίεση → οριοθετημένη μνήμη → συνεπής συμπεριφορά | Προβολή αποσφαλμάτωσης Οι ακριβείς λειτουργίες είναι παρούσες μόλις αρκετό καιρό για να επιθεωρήσουν | Αναθεώρησις Τα ολοκληρωμένα αποτελέσματα είναι προσβάσιμα μέχρι τη λήξη τους. | Μηδενική κατακράτηση δεδομένων □ Απόρρητο και απλότητα ευθυγραμμισμένη για μία φορά | Αυτοπεποίθηση εκτέλεσης Οι παλιές δουλειές παραλείπουν, οι σχετικές παραμένουν

Ακριβώς όπως η κρύπτη LRU ακονίζει τη μνήμη συμπεριφοράς, το παράθυρο λειτουργίας κυλώντας ακονίζει την προβολή εκτέλεσης.


Πότε να χρησιμοποιήσετε αυτό το πρότυπο

Χρησιμοποιήστε το όταν:

  • Τα δεδομένα των χρηστών δεν πρέπει να επιμένουν
  • Οι πράξεις είναι βραχύβια (δευτερόλεπτα έως λεπτά)
  • Αποσφαλμάτωση θεμάτων
  • Θέματα προστασίας της ιδιωτικής ζωής
  • Θέματα σταθερότητας φόρτωσης
  • Θέλετε άμεση απάντηση + επεξεργασία φόντου

Μην το χρησιμοποιείτε όταν:

  • Χρειάζεστε πραγματική αντοχή (χρησιμοποιήστε μια πραγματική ουρά)
  • Οι λειτουργίες διαρκούν ώρες (χρησιμοποιήστε Hangfire ή παρόμοια)
  • Χρειάζεστε ακριβώς μια φορά σημασιολογία (χρησιμοποιήστε μια κατανεμημένη συναλλαγή)
  • Πολλαπλοί διακομιστές πρέπει να συντονίσουν (χρήση Redis ή μεσίτη μηνυμάτων)

Συμπέρασμα -Εφημερίς Εκτέλεσης ως Σχεδιασμού Φιλοσοφία

Εάν η Άρθρο LRU ήταν περίπου μάθηση ξεχνώντας, αυτό είναι περίπου εκτέλεση με εξάτμιση.

Το σύστημα θυμάται ακριβώς αρκετά για να βοηθήσει - και τίποτα περισσότερο.

Είναι:

  • Περιορίζεται
  • Καθοριστική
  • ιδιωτικό
  • Αποσφαλματωτής
  • retrievable
  • αυτοκαθαρισμός
  • στιβαρή
  • και αρχιτεκτονική-πρώτα

Το καλύτερο πράγμα; Ξέρετε ήδη πώς να το εφαρμόσετε: TaskCompletionSource, α Channel, a IMemoryCache με συρόμενη λήξη, και έναν εργάτη υποβάθρου.

Φωτιά... και μην ξεχνάς.


Ποιο είναι το επόμενο;

Το Μέρος 2ο: Κατασκευή μιας Αναξιοποίητης Βιβλιοθήκης Εκτέλεσης της Εφήμερης, θα μετατρέψουμε αυτό το μοτίβο σε ένα drop-in βοηθό:

  • EphemeralForEachAsync<T> - όπως Parallel.ForEachAsync αλλά με την παρακολούθηση λειτουργίας
  • Πλήκτροι αγωγοί για διαδοχική εκτέλεση ανά φορέα
  • EphemeralWorkCoordinator<T> - μια μακροχρόνια ορατή ουρά εργασίας
  • Ονοματοποιημένοι/τυποποιημένοι συντονιστές με AddEphemeralWorkCoordinator<TCoordinator> (όπως AddHttpClient)
  • Πλήρης ενσωμάτωση DI με δυνατότητα και διάρκεια ζωής singleton
  • Σύγκριση με άλλες προσεγγίσεις (PTL Dataflow, Channels, Background Services)

Σύνδεσμοι

logo

© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.