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
162 .Where(x => x.CompileJob!.Job.Instance!.Id ==
metadata.Id)
163 .Include(x => x.CompileJob)
164 .Include(x => x.InitialCompileJob)
165 .ToListAsync(cancellationToken);
166 result = dbReattachInfos.FirstOrDefault();
170 var timeoutMilliseconds = await db
174 .Select(x => x.DreamDaemonSettings!.TopicRequestTimeout)
175 .FirstOrDefaultAsync(cancellationToken);
177 if (timeoutMilliseconds ==
default)
179 logger.LogCritical(
"Missing TopicRequestTimeout!");
183 topicTimeout = TimeSpan.FromMilliseconds(timeoutMilliseconds.Value);
186 foreach (var reattachInfo
in dbReattachInfos)
194 await KillProcess(reattachInfo);
196 db.ReattachInformations.Remove(reattachInfo);
197 logger.LogTrace(
"Deleting ReattachInformation {id}...", reattachInfo.Id);
200 await db.Save(cancellationToken);
203 if (!topicTimeout.HasValue)
205 logger.LogDebug(
"Reattach information not found!");
212 logger.LogError(
"Unable to reattach! Could not load .dmb!");
213 await KillProcess(result);
217 logger.LogTrace(
"Deleting ReattachInformation {id}...", result.Id);
219 .ReattachInformations
221 .Where(x => x.Id == result.Id)
222 .ExecuteDeleteAsync(cancellationToken);
228 if (result.InitialCompileJob !=
null)
230 logger.LogTrace(
"Loading initial compile job...");
231 initialDmb = await
dmbFactory.
FromCompileJob(result.InitialCompileJob,
"Session Loading Initial Deployment", cancellationToken);
234 logger.LogTrace(
"Retrieved ReattachInformation");
242 logger.LogDebug(
"Reattach information loaded: {info}", info);
252 logger.LogDebug(
"Clearing reattach information");
253 return ClearImpl(db,
true, cancellationToken);
265 var baseQuery = databaseContext
268 .Where(x => x.CompileJob!.Job.Instance!.Id ==
metadata.Id);
272 .ExecuteDeleteAsync(cancellationToken);
275 var results = await baseQuery.ToListAsync(cancellationToken);
276 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.