using Akka.Actor;
using Akka.Event;
using Microsoft.EntityFrameworkCore;
using ZB.MOM.WW.OtOpcUa.Configuration;
namespace ZB.MOM.WW.OtOpcUa.Runtime.Health;
///
/// Single-flight cached health probe against the ConfigDb. Reads cached state via
/// Ask<DbHealthStatus>; a single SELECT 1 runs at most every RefreshInterval.
/// Consumed by both the host's /health/ready endpoint and
/// RedundancyStateActor's stale calc.
///
public sealed class DbHealthProbeActor : ReceiveActor, IWithTimers
{
public static readonly TimeSpan RefreshInterval = TimeSpan.FromSeconds(5);
public sealed class GetStatus { public static readonly GetStatus Instance = new(); private GetStatus() { } }
public sealed record DbHealthStatus(bool Reachable, DateTime AsOfUtc, string? LastError);
public sealed class Tick { public static readonly Tick Instance = new(); private Tick() { } }
private readonly IDbContextFactory _dbFactory;
private readonly ILoggingAdapter _log = Context.GetLogger();
private DbHealthStatus _last = new(false, DateTime.MinValue, "not probed yet");
/// Gets or sets the timer scheduler for periodic health probes.
public ITimerScheduler Timers { get; set; } = null!;
/// Creates a Props instance for the DbHealthProbeActor.
/// The factory for creating ConfigDb contexts.
/// The Akka for creating this actor.
public static Props Props(IDbContextFactory dbFactory) =>
Akka.Actor.Props.Create(() => new DbHealthProbeActor(dbFactory));
/// Initializes a new instance of the class.
/// The factory for creating ConfigDb contexts.
public DbHealthProbeActor(IDbContextFactory dbFactory)
{
_dbFactory = dbFactory;
Receive(_ => Sender.Tell(_last));
Receive(_ => RunProbe());
}
///
protected override void PreStart()
{
RunProbe();
Timers.StartPeriodicTimer("probe", Tick.Instance, RefreshInterval);
}
private void RunProbe()
{
try
{
using var db = _dbFactory.CreateDbContext();
_ = db.Deployments.AsNoTracking().Take(1).ToList();
_last = new DbHealthStatus(true, DateTime.UtcNow, null);
}
catch (Exception ex)
{
_last = new DbHealthStatus(false, DateTime.UtcNow, ex.Message);
_log.Warning(ex, "DbHealthProbe: probe failed");
}
}
}