Table of Contents

src/MultiSiteManufacturing.Host/Baseline/BaselineFactBackend.cs

This page is part of the documentation for Orleans.Lattice 9.9.0 (release line 9.9), built 2026-10-04. It is also published as markdown, with every table and list, at src-multisitemanufacturing-host-baseline-baselinefactbackend.md, and llms.txt lists every page.

Part of MultiSiteManufacturing source.

using System.Collections.Concurrent;
using MultiSiteManufacturing.Host.Domain;
using MultiSiteManufacturing.Host.Federation;

namespace MultiSiteManufacturing.Host.Baseline;

/// <summary>
/// Baseline backend - delegates per-part state to <see cref="IBaselinePartGrain"/>
/// and keeps an in-process directory of known parts so <see cref="ListPartsAsync"/>
/// doesn't require a query-by-convention over grain storage.
/// </summary>
/// <remarks>
/// <para>
/// The in-process directory is rebuilt on every host start (facts replayed
/// through <see cref="EmitAsync"/> repopulate it). Grain state itself is
/// durable - the directory is a cache, not the source of truth.
/// </para>
/// <para>
/// On peer clusters, <see cref="EmitAsync"/> is also called from
/// <see cref="MultiSiteManufacturing.Host.Replication.BaselineReplicationReplay"/>
/// for every replicated <c>mfg-facts</c> Set entry observed on the
/// package's <c>IChangeFeed</c>. This models naive event-log
/// replication for the baseline backend so a peer's baseline view
/// matches the seeding cluster on cold start. Deletes are not
/// replayed - the baseline is an append-only fold with no
/// retraction concept.
/// </para>
/// </remarks>
public sealed class BaselineFactBackend(IGrainFactory grainFactory) : IFactBackend
{
    private readonly ConcurrentDictionary<PartSerialNumber, byte> _knownParts = new();

    /// <inheritdoc />
    public string Name => "baseline";

    /// <inheritdoc />
    public async Task EmitAsync(Fact fact, CancellationToken cancellationToken = default)
    {
        ArgumentNullException.ThrowIfNull(fact);
        _knownParts.TryAdd(fact.Serial, 0);
        var grain = grainFactory.GetGrain<IBaselinePartGrain>(fact.Serial.Value);
        await grain.AppendAsync(fact);
    }

    /// <inheritdoc />
    public Task<ComplianceState> GetStateAsync(PartSerialNumber serial, CancellationToken cancellationToken = default)
    {
        var grain = grainFactory.GetGrain<IBaselinePartGrain>(serial.Value);
        return grain.GetStateAsync();
    }

    /// <inheritdoc />
    public Task<IReadOnlyList<Fact>> GetFactsAsync(PartSerialNumber serial, CancellationToken cancellationToken = default)
    {
        var grain = grainFactory.GetGrain<IBaselinePartGrain>(serial.Value);
        return grain.GetFactsAsync();
    }

    /// <inheritdoc />
    public Task<IReadOnlyList<PartSerialNumber>> ListPartsAsync(CancellationToken cancellationToken = default) =>
        Task.FromResult<IReadOnlyList<PartSerialNumber>>([.. _knownParts.Keys]);
}