4using System.Threading.Tasks;
6using Microsoft.EntityFrameworkCore;
7using Microsoft.Extensions.Logging;
37 readonly ILogger<SessionPersistor>
logger;
56 ILogger<SessionPersistor>
logger,
62 this.logger =
logger ??
throw new ArgumentNullException(nameof(
logger));
63 this.metadata =
metadata ??
throw new ArgumentNullException(nameof(
metadata));
69 ArgumentNullException.ThrowIfNull(reattachInformation);
71 logger.LogTrace(
"Saving reattach information: {info}...", reattachInformation);
73 await
ClearImpl(db,
false, cancellationToken);
75 var dbReattachInfo =
new Models.ReattachInformation(reattachInformation.AccessIdentifier)
77 CompileJobId = reattachInformation.Dmb.CompileJob.Require(x => x.Id),
78 InitialCompileJobId = reattachInformation.InitialDmb?.CompileJob.Require(x => x.Id),
79 Port = reattachInformation.Port,
80 ProcessId = reattachInformation.ProcessId,
82 LaunchSecurityLevel = reattachInformation.LaunchSecurityLevel,
83 LaunchVisibility = reattachInformation.LaunchVisibility,
84 TopicPort = reattachInformation.TopicPort,
87 db.ReattachInformations.Add(dbReattachInfo);
88 await db.Save(cancellationToken);
90 reattachInformation.Id = dbReattachInfo.Id!.Value;
91 logger.LogDebug(
"Saved reattach information: {info}", reattachInformation);
97 ArgumentNullException.ThrowIfNull(reattachInformation);
98 if (!reattachInformation.Id.HasValue)
99 throw new InvalidOperationException(
"Provided reattachInformation has no Id!");
101 logger.LogTrace(
"Updating reattach information: {info}...", reattachInformation);
103 var dbReattachInfo =
new Models.ReattachInformation(String.Empty)
105 Id = reattachInformation.Id.Value,
108 db.ReattachInformations.Attach(dbReattachInfo);
110 dbReattachInfo.AccessIdentifier = reattachInformation.AccessIdentifier;
111 dbReattachInfo.CompileJobId = reattachInformation.Dmb.CompileJob.Require(x => x.Id);
112 dbReattachInfo.InitialCompileJobId = reattachInformation.InitialDmb?.CompileJob.Require(x => x.Id);
113 dbReattachInfo.Port = reattachInformation.Port;
114 dbReattachInfo.ProcessId = reattachInformation.ProcessId;
115 dbReattachInfo.RebootState = reattachInformation.RebootState;
116 dbReattachInfo.LaunchSecurityLevel = reattachInformation.LaunchSecurityLevel;
117 dbReattachInfo.LaunchVisibility = reattachInformation.LaunchVisibility;
118 dbReattachInfo.TopicPort = reattachInformation.TopicPort;
120 await db.Save(cancellationToken);
122 logger.LogDebug(
"Updated reattach information: {info}", reattachInformation);
126 public async ValueTask<ReattachInformation?>
Load(CancellationToken cancellationToken)
128 Models.ReattachInformation? result =
null;
129 TimeSpan? topicTimeout =
null;
131 async ValueTask KillProcess(Models.ReattachInformation reattachInfo)
138 if (reattachInfo == result)
140 logger.LogWarning(
"Killing PID {pid} associated with CompileJob-less reattach information...", reattachInfo.ProcessId);
144 logger.LogWarning(
"Killing PID {pid} associated with extra reattach information...", reattachInfo.ProcessId);
148 await process.Lifetime;
153 logger.LogWarning(ex,
"Failed to kill process!");
159 var dbReattachInfos = await db
160 .ReattachInformations
161 .Where(x => x.CompileJob!.Job.Instance!.Id ==
metadata.Id)
162 .Include(x => x.CompileJob)
163 .Include(x => x.InitialCompileJob)
164 .ToListAsync(cancellationToken);
165 result = dbReattachInfos.FirstOrDefault();
169 var timeoutMilliseconds = await db
172 .Select(x => x.DreamDaemonSettings!.TopicRequestTimeout)
173 .FirstOrDefaultAsync(cancellationToken);
175 if (timeoutMilliseconds ==
default)
177 logger.LogCritical(
"Missing TopicRequestTimeout!");
181 topicTimeout = TimeSpan.FromMilliseconds(timeoutMilliseconds.Value);
184 foreach (var reattachInfo
in dbReattachInfos)
192 await KillProcess(reattachInfo);
194 db.ReattachInformations.Remove(reattachInfo);
195 logger.LogTrace(
"Deleting ReattachInformation {id}...", reattachInfo.Id);
198 await db.Save(cancellationToken);
201 if (!topicTimeout.HasValue)
203 logger.LogDebug(
"Reattach information not found!");
210 logger.LogError(
"Unable to reattach! Could not load .dmb!");
211 await KillProcess(result);
215 logger.LogTrace(
"Deleting ReattachInformation {id}...", result.Id);
217 .ReattachInformations
218 .Where(x => x.Id == result.Id)
219 .ExecuteDeleteAsync(cancellationToken);
225 if (result.InitialCompileJob !=
null)
227 logger.LogTrace(
"Loading initial compile job...");
228 initialDmb = await
dmbFactory.
FromCompileJob(result.InitialCompileJob,
"Session Loading Initial Deployment", cancellationToken);
231 logger.LogTrace(
"Retrieved ReattachInformation");
239 logger.LogDebug(
"Reattach information loaded: {info}", info);
249 logger.LogDebug(
"Clearing reattach information");
250 return ClearImpl(db,
true, cancellationToken);
262 var baseQuery = databaseContext
264 .Where(x => x.CompileJob!.Job.Instance!.Id ==
metadata.Id);
268 .ExecuteDeleteAsync(cancellationToken);
271 var results = await baseQuery.ToListAsync(cancellationToken);
272 foreach (var result
in results)
Metadata about a server instance.
readonly ILogger< SessionPersistor > logger
The ILogger for the SessionPersistor.
readonly IProcessExecutor processExecutor
The IProcessExecutor for the SessionPersistor.
readonly IDatabaseContextFactory databaseContextFactory
The IDatabaseContextFactory for the SessionPersistor.
readonly Api.Models.Instance metadata
The Api.Models.Instance for the SessionPersistor.
ValueTask Update(ReattachInformation reattachInformation, CancellationToken cancellationToken)
Update some reattachInformation .A ValueTask representing the running operation.
readonly IDmbFactory dmbFactory
The IDmbFactory for the SessionPersistor.
async ValueTask< ReattachInformation?> Load(CancellationToken cancellationToken)
Load a saved ReattachInformation.A ValueTask<TResult> resulting in the stored ReattachInformation if ...
SessionPersistor(IDatabaseContextFactory databaseContextFactory, IDmbFactory dmbFactory, IProcessExecutor processExecutor, ILogger< SessionPersistor > logger, Api.Models.Instance metadata)
Initializes a new instance of the SessionPersistor class.
ValueTask Clear(CancellationToken cancellationToken)
Clear any stored ReattachInformation.A ValueTask representing the running operation.
ValueTask Save(ReattachInformation reattachInformation, CancellationToken cancellationToken)
Save some reattachInformation .A ValueTask representing the running operation.
async ValueTask ClearImpl(IDatabaseContext databaseContext, bool instant, CancellationToken cancellationToken)
Clear any stored ReattachInformation.
Factory for IDmbProviders.
ValueTask< IDmbProvider?> FromCompileJob(CompileJob compileJob, string reason, CancellationToken cancellationToken, [CallerFilePath] string? callerFile=null, [CallerLineNumber] int callerLine=default)
Gets a IDmbProvider for a given CompileJob.
Provides absolute paths to the latest compiled .dmbs.
Handles saving and loading ReattachInformation.
void Remove(TModel model)
Remove a given model from the the working set.
Factory for scoping usage of IDatabaseContexts. Meant for use by Components.
ValueTask UseContextTaskReturn(Func< IDatabaseContext, Task > operation)
Run an operation in the scope of an IDatabaseContext.
ValueTask UseContext(Func< IDatabaseContext, ValueTask > operation)
Run an operation in the scope of an IDatabaseContext.
IDatabaseCollection< ReattachInformation > ReattachInformations
The DbSet<TEntity> for ReattachInformations.
IProcess? GetProcess(int id)
Get a IProcess by id .
void Terminate()
Asycnhronously terminates the process.
RebootState
Represents the action to take when /world/Reboot() is called.