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]);
}