36#pragma warning disable CA1506
264 "tgs_watchdog_status",
265 $"TGS Watchdog status: {(int)WatchdogStatus.Offline} = Offline, {(int)WatchdogStatus.Online} = Online, {(int)WatchdogStatus.Restoring} = Restoring, {(int)WatchdogStatus.DelayedRestart} = Delayed Restart");
266 cpuUsageMetric = metricFactory.CreateGauge(
"tgs_game_cpu_usage",
"Estimated total CPU usage time for the game process from 0-1");
267 ramUsageMetric = metricFactory.CreateGauge(
"tgs_game_ram_usage",
"Total used bytes of private memory for the game process");
288 Logger.LogTrace(
"Created watchdog");
294 Logger.LogTrace(
"Disposing...");
338 Text =
"TGS: Server offline!",
346 Text =
"TGS: Bad topic exchange!",
352 Text =
"TGS: Bad topic response!",
400 Logger.LogTrace(
"Begin Restart. Graceful: {gracefulFlag}",
graceful);
414 Logger.LogWarning(
"Unable to send reboot state change event!");
426 var job = Models.Job.Create(
428 ?
JobCode.StartupWatchdogReattach
429 :
JobCode.StartupWatchdogLaunch,
437 if (
core?.Watchdog !=
this)
468 Logger.LogDebug(
"Waiting for server to gracefully shut down.");
472 Logger.LogTrace(
"Graceful shutdown requested but server is already offline.");
481 Logger.LogTrace(
"Not sending detach chat message as status is: {status}",
Status);
502 Logger.LogInformation(
"Attempted broadcast failed, no active server!");
508 Logger.LogInformation(
"Attempted broadcast failed, no DMAPI!");
516 "Attempted broadcast failed, insufficient interop version: {interopVersion}. Requires {minimumRequiredVersion}!",
589 Logger.LogTrace(
"Begin LaunchImplNoLock");
619 Logger.LogTrace(
ex,
"Controller initialization cancelled!");
624 Logger.LogWarning(
e,
"Failed to start watchdog!");
645 Logger.LogTrace(
ex,
"Announcement task canceled!");
649 Logger.LogInformation(
"Controller(s) initialized successfully");
664 Logger.LogTrace(
"StopMonitor");
670 Logger.LogTrace(
"Stopped Monitor");
692 new JobException(
$"{serverName} failed to start: {launchResult}"));
696 new JobException(
$"{serverName} timed out on startup: {ActiveLaunchParameters.StartupTimeout!.Value}s"));
710 const string FailReattachMessage =
"Unable to properly reattach to server! Restarting watchdog...";
730 Logger.LogTrace(
"DisposeAndNullControllers");
765 Logger.LogTrace(
"Same compile job, not sending deployment event");
777 GameIOManager.ResolvePath(newCompileJob.DirectoryName!.Value.ToString()),
788 Logger.LogWarning(
ex,
"Failed to apply remote deployment!");
814 Logger.LogError(
ex,
"Suppressing exception triggered by event!");
825 Logger.LogTrace(
"Monitor restart!");
839 Logger.LogDebug(
"Relaunch successful, resuming monitor...");
853 TimeSpan.FromHours(1).TotalSeconds);
856 $"Failed to restart (Attempt: {retryAttempts}), retrying in {retryDelay}s...");
876 Logger.LogDebug(
"Found new CompileJob without waiting");
888#pragma warning disable CA1502
891 Logger.LogTrace(
"Entered MonitorLifetimes");
980 Logger.LogError(
"Controller was null on monitor wakeup! Attempting restart...");
985 Logger.LogTrace(
"Monitor activated");
1055 "Monitor crashed! Iteration: {iteration}",
1062 $"Monitor crashed, this should NEVER happen! Please report this, full details in logs! {nextActionMessage}. Error: {e.Message}");
1071 Logger.LogDebug(
"Server seems to be okay, not restarting");
1084 Logger.LogDebug(
"Monitor cancelled");
1088 Logger.LogTrace(
"Detaching server...");
1093 Logger.LogError(
"Controller was null on monitor shutdown!");
1101 Logger.LogTrace(
"Monitor exiting...");
1103#pragma warning restore CA1502
1142 Logger.LogTrace(
"Graceful termination requested");
1145 Logger.LogTrace(
"Could not gracefully terminate as there is no active controller!");
1155 Logger.LogTrace(
"Sending health check to active server...");
1168 Logger.LogDebug(
"DEFCON 4: Game server missed first health check!");
1171 const string message2 =
"DEFCON 3: Game server has missed 2 health checks!";
1179 const string logTemplate1 =
"DEFCON 2: Game server has missed 3 health checks! If it does not respond to the next one, the watchdog will {actionToTake}!";
1189 ?
"Shutting down due to graceful termination request"
1191 const string logTemplate2 =
"DEFCON 1: Four health checks have been missed! {actionTaken}...";
1201 Logger.LogDebug(
"DumpOnHealthCheckRestart enabled.");
1208 Logger.LogWarning(
ex,
"Creating dump failed!");
1212 Logger.LogWarning(
ex,
"Creating dump failed!");
1216 Logger.LogTrace(
"DumpOnHealthCheckRestart disabled.");
1240 if (
result?.ChatResponses !=
null)
1246 if (response.ChannelIds == null)
1248 if (!warnedMissingChannelIds)
1250 Logger.LogWarning(
"DMAPI response contains null channelIds!");
1251 warnedMissingChannelIds = true;
1286 if (
session?.Lifetime.IsCompleted !=
false)
1292 diagnosticsIOManager.ConcatPath(
1294 $"DreamDaemon-{DateTimeOffset.UtcNow.ToFileStamp()}"));
1299 dumpFileName =
$"{dumpFileNameTemplate} ({++iteration}){dumpFileExtension}";
1304 if (
session.Lifetime.IsCompleted)
1307 Logger.LogInformation(
"Dumping session to {dumpFileName}...",
dumpFileName);
EngineType? Engine
The EngineType.
virtual ? long Id
The ID of the entity.
Metadata about a server instance.
Represents a deployment run.
Launch settings for DreamDaemon.
uint? HealthCheckSeconds
The number of seconds between each watchdog health check. 0 disables.
bool? DumpOnHealthCheckRestart
If a process core dump should be created prior to restarting the watchdog due to health check failure...
Extension methods for the ValueTask and ValueTask<TResult> classes.
static async ValueTask WhenAll(IEnumerable< ValueTask > tasks)
Fully await a given list of tasks .
Represents a tgs_chat_user datum.
bool DmbAvailable
If LockNextDmb will succeed.
Task OnNewerDmb
Get a Task that completes when the result of a call to LockNextDmb will be different than the previou...
async ValueTask< CompileJob?> LatestCompileJob()
Gets the latest CompileJob.A ValueTask<TResult> resulting in the latest CompileJob or null if none ar...
const string DifferentCoreExceptionMessage
Message for the InvalidOperationException if ever a job starts on a different IInstanceCore than the ...
Represents a message to send to a chat provider.
string? Text
The message string.
Represents a chat command to be handled by DD.
Data structure for TopicCommandType.EventNotification requests.
Parameters for a topic request.
static TopicParameters CreateBroadcastParameters(string broadcastMessage)
Initializes a new instance of the TopicParameters class.
A response to a topic request.
async ValueTask< ReattachInformation?> Load(CancellationToken cancellationToken)
Load a saved ReattachInformation.A ValueTask<TResult> resulting in the stored ReattachInformation if ...
ValueTask Clear(CancellationToken cancellationToken)
Clear any stored ReattachInformation.A ValueTask representing the running operation.
Base class for IWatchdogs.
async ValueTask HandleRestart(Version? updateVersion, bool handlerMayDelayShutdownWithExtremelyLongRunningTasks, CancellationToken cancellationToken)
Handle a restart of the server.A ValueTask representing the running operation.
async ValueTask< bool > Broadcast(string message, CancellationToken cancellationToken)
Send a broadcast message to the DMAPI.A ValueTask<TResult> resulting in true if the broadcast succee...
async ValueTask< MonitorAction > HandleHealthCheck(CancellationToken cancellationToken)
Handles a watchdog health check.
ISessionController? GetActiveController()
Get the active ISessionController.
readonly Gauge cpuUsageMetric
Active session CPU usage as a metric.
readonly IJobManager jobManager
The IJobManager for the WatchdogBase.
ILogger< WatchdogBase > Logger
The ILogger for the WatchdogBase.
bool releaseServers
If the servers should be released instead of shutdown.
async ValueTask ReattachFailure(CancellationToken cancellationToken)
Call from InitController(ValueTask, ReattachInformation, CancellationToken) when a reattach operation...
async ValueTask CreateDumpNoLock(CancellationToken cancellationToken)
Attempt to create a process dump for the game server. Requires a lock on synchronizationSemaphore.
Models.? CompileJob ActiveCompileJob
Retrieves the Models.CompileJob currently running on the server.
DreamDaemonLaunchParameters? LastLaunchParameters
The DreamDaemonLaunchParameters the active server is using.This may not be the exact same as ActiveLa...
DateTimeOffset? LaunchTime
When the current server executions was started.
WatchdogBase(IChatManager chat, ISessionControllerFactory sessionControllerFactory, IDmbFactory dmbFactory, ISessionPersistor sessionPersistor, IJobManager jobManager, IServerControl serverControl, IAsyncDelayer asyncDelayer, IIOManager diagnosticsIOManager, IEventConsumer eventConsumer, IRemoteDeploymentManagerFactory remoteDeploymentManagerFactory, IMetricFactory metricFactory, IIOManager gameIOManager, ILogger< WatchdogBase > logger, DreamDaemonLaunchParameters initialLaunchParameters, Api.Models.Instance metadata, bool autoStart)
Initializes a new instance of the WatchdogBase class.
readonly bool autoStart
If the WatchdogBase should LaunchNoLock(bool, bool, bool, ReattachInformation, CancellationToken) in ...
async ValueTask Terminate(bool graceful, CancellationToken cancellationToken)
Stops the watchdog.A ValueTask representing the running operation.
async ValueTask CheckLaunchResult(ISessionController controller, string serverName, CancellationToken cancellationToken)
Check the LaunchResult of a given controller for errors and throw a JobException if any are detected...
readonly Api.Models.Instance metadata
The Api.Models.Instance for the WatchdogBase.
bool AlphaIsActive
If the alpha server is the active server.
readonly IEventConsumer eventConsumer
The IEventConsumer that is not the WatchdogBase.
bool disposed
If the WatchdogBase has been DisposeAsync'd.
readonly SemaphoreSlim controllerDisposeSemaphore
SemaphoreSlim used for DisposeAndNullControllers.
WatchdogStatus Status
The current WatchdogStatus.
ValueTask< MonitorAction > HandleMonitorWakeup(MonitorActivationReason activationReason, CancellationToken cancellationToken)
Handles the actions to take when the monitor has to "wake up".
CancellationTokenSource? monitorCts
The CancellationTokenSource for the monitor loop.
Task? monitorTask
The Task running the monitor loop.
void RunMetricsScrape()
Callback to update transient metrics.
async ValueTask DisposeAsync()
readonly IRemoteDeploymentManagerFactory remoteDeploymentManagerFactory
The IRemoteDeploymentManagerFactory for the WatchdogBase.
readonly SemaphoreSlim synchronizationSemaphore
The SemaphoreSlim for the WatchdogBase.
async ValueTask Restart(bool graceful, CancellationToken cancellationToken)
Restarts the watchdog.A ValueTask representing the running operation.
long? MemoryUsage
Gets the memory usage of the game server in bytes.
readonly IIOManager diagnosticsIOManager
The IIOManager pointing to the Diagnostics directory.
async ValueTask< MessageContent > HandleChatCommand(string commandName, string arguments, ChatUser sender, CancellationToken cancellationToken)
Handle a chat command.A ValueTask<TResult> resulting in the MessageContent text to send back.
ValueTask InitController(ValueTask eventTask, ReattachInformation? reattachInfo, CancellationToken cancellationToken)
Starts all ISessionControllers.
async ValueTask CreateDump(CancellationToken cancellationToken)
Attempt to create a process dump for DreamDaemon.A ValueTask representing the running operation.
IIOManager GameIOManager
The IIOManager for the WatchdogBase pointing to the Game directory.
DreamDaemonLaunchParameters ActiveLaunchParameters
The DreamDaemonLaunchParameters to be applied.
uint? ClientCount
Last known client count queried from the DMAPI. Requires health checks to be enabled to populate.
async ValueTask BeforeApplyDmb(Models.CompileJob newCompileJob, CancellationToken cancellationToken)
To be called before a given newCompileJob goes live.
async ValueTask< bool > StopMonitor()
Stops MonitorLifetimes(CancellationToken). Doesn't kill the servers.
async ValueTask MonitorRestart(CancellationToken cancellationToken)
Attempt to restart the monitor from scratch.
async ValueTask Launch(CancellationToken cancellationToken)
Start the IWatchdog.A ValueTask representing the running operation.
async Task InitialCheckDmbUpdated(CompileJob currentCompileJob)
Check for a new IDmbProvider.
async ValueTask LaunchNoLock(bool startMonitor, bool announce, bool announceFailure, ReattachInformation? reattachInfo, CancellationToken cancellationToken)
Launches the watchdog.
async ValueTask DisposeAndNullControllers(CancellationToken cancellationToken)
Wrapper for DisposeAndNullControllersImpl under a locked context.
ValueTask DisposeAndNullControllersImpl()
Call IDisposable.Dispose and null the fields for all ISessionControllers.
async ValueTask TerminateNoLock(bool graceful, bool announce, CancellationToken cancellationToken)
Implementation of Terminate(bool, CancellationToken). Does not lock synchronizationSemaphore.
async ValueTask< bool > ChangeSettings(DreamDaemonLaunchParameters launchParameters, CancellationToken cancellationToken)
Changes the ActiveLaunchParameters. If currently running, may trigger a graceful restart....
void HandleChatResponses(TopicResponse? result)
Handle any TopicResponse.ChatResponses in a given topic result .
virtual async ValueTask ResetRebootState(CancellationToken cancellationToken)
Cancels pending graceful actions.A ValueTask representing the running operation.
async Task MonitorLifetimes(CancellationToken cancellationToken)
The main loop of the watchdog. Ayschronously waits for events to occur and then responds to them.
long? SessionId
An incrementing ID for representing current server execution.
async Task StartAsync(CancellationToken cancellationToken)
ValueTask InstanceRenamed(string newInstanceName, CancellationToken cancellationToken)
Called when the owning Instance is renamed.A ValueTask representing the running operation.
readonly Gauge ramUsageMetric
MemoryUsage as a metric.
int healthChecksMissed
The number of hearbeats missed.
readonly Gauge watchdogStatusMetric
The Status as a metric.
IChatManager Chat
The IChatManager for the WatchdogBase.
async Task StopAsync(CancellationToken cancellationToken)
readonly IRestartRegistration restartRegistration
The IRestartRegistration for the WatchdogBase.
async ValueTask HandleEventImpl(EventType eventType, IEnumerable< string > parameters, bool relayToSession, CancellationToken cancellationToken)
Handle a given eventType without re-throwing errors.
WatchdogStatus status
Backing field for Status.
volatile TaskCompletionSource activeParametersUpdated
TaskCompletionSource that completes when ActiveLaunchParameters are changed and we are running.
Operation exceptions thrown from the context of a Models.Job.
async ValueTask Delay(TimeSpan timeSpan, CancellationToken cancellationToken)
Create a Task that completes after a given timeSpan .A ValueTask representing the running operation.
Async lock context helper.
static async ValueTask< SemaphoreSlimContext > Lock(SemaphoreSlim semaphore, CancellationToken cancellationToken, ILogger? logger=null)
Asyncronously locks a semaphore .
Helpers for manipulating the Serilog.Context.LogContext.
const string WatchdogMonitorIterationContextProperty
The Serilog.Context.LogContext property name for the ID of the watchdog monitor iteration currently b...
For managing connected chat services.
void QueueWatchdogMessage(string message)
Queue a chat message to configured watchdog channels.
void RegisterCommandHandler(ICustomCommandHandler customCommandHandler)
Registers a customCommandHandler to use.
ValueTask UpdateTrackingContexts(CancellationToken cancellationToken)
Force an update with the active channels on all active IChatTrackingContexts.
void QueueMessage(MessageContent message, IEnumerable< ulong > channelIds)
Queue a chat message to a given set of channelIds .
Handles Commands.ICommands that map to those defined in a IChatTrackingContext.
Factory for IDmbProviders.
Factory for creating IRemoteDeploymentManagers.
IRemoteDeploymentManager CreateRemoteDeploymentManager(Api.Models.Instance metadata, RemoteGitProvider remoteGitProvider)
Creates a IRemoteDeploymentManager for a given remoteGitProvider .
Consumes EventTypes and takes the appropriate actions.
ValueTask? HandleCustomEvent(string eventName, IEnumerable< string?> parameters, CancellationToken cancellationToken)
Handles a given custom event.
ValueTask HandleEvent(EventType eventType, IEnumerable< string?> parameters, bool deploymentPipeline, CancellationToken cancellationToken)
Handle a given eventType .
Factory for ISessionControllers.
Handles communication with a DreamDaemon IProcess.
Models.CompileJob CompileJob
Gets the CompileJob associated with the ISessionController.
ReattachInformation ReattachInformation
Gets the Session.ReattachInformation associated with the ISessionController.
EngineVersion EngineVersion
Gets the Api.Models.EngineVersion associated with the ISessionController.
Handles saving and loading ReattachInformation.
Runs and monitors the twin server controllers.
Handler for server restarts.
Represents the lifetime of a IRestartHandler registration.
Represents a service that may take an updated Host assembly and run it, stopping the current assembly...
IRestartRegistration RegisterForRestart(IRestartHandler handler)
Register a given handler to run before stopping the server for a restart.
Interface for using filesystems.
Manages the runtime of Jobs.
ValueTask RegisterOperation(Job job, JobEntrypoint operation, CancellationToken cancellationToken)
Registers a given Job and begins running it.
long? MemoryUsage
Gets the process' memory usage in bytes.
DateTimeOffset? LaunchTime
When the process was started.
For waiting asynchronously.
ErrorCode
Types of Response.ErrorMessageResponses that the API may return.
JobCode
The different types of Response.JobResponse.
WatchdogStatus
The current status of the watchdog.
@ List
User may list files if the Models.Instance allows it.
DreamDaemonRights
Rights for managing DreamDaemon.
EventType
Types of events. Mirror in tgs.dm. Prefer last listed name for script.
RebootState
Represents the action to take when /world/Reboot() is called.
MonitorAction
The action for the monitor loop to take when control is returned to it.
MonitorActivationReason
Reasons for the monitor to wake up.