Skip to content

Commit dee2cfe

Browse files
committed
Improved comments and cancellation behavior
1 parent e07f9ed commit dee2cfe

File tree

1 file changed

+13
-5
lines changed

1 file changed

+13
-5
lines changed

src/ServiceControl.RavenDB/EmbeddedDatabase.cs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -192,37 +192,45 @@ public async Task Stop(CancellationToken cancellationToken)
192192

193193
await shutdownTokenSource.CancelAsync();
194194

195-
// EmbeddedServer does not have have an async Stop method, the Dispose operation blocks and waits
196-
// until its GracefulShutdownTimeout is reached and then does a Process.Kill
195+
// This is a workaround until the EmbeddedServer properly supports cancellation!
197196
//
198-
// This logic gets the child process ID uses a Task.Delay with infinite.
197+
// EmbeddedServer does not have an async Stop method, the Dispose operation blocks and waits
198+
// until its GracefulShutdownTimeout is reached and then does a Process.Kill. Due to this behavior this can
199+
// be shorter or longer than the allowed stop duration.
200+
//
201+
// With the Task.WhenAny 2 things can happen:
199202
//
200203
// a. The Task.Delay gets cancelled first
201204
// b. The EmbeddedServer.Dispose completes first
202205
//
203206
// If the Task.Delay gets cancelled first this means Dispose is still running and
204-
// then we kill the process
207+
// then we try and kill the process
205208

206209
serverOptions!.GracefulShutdownTimeout = TimeSpan.FromHours(1); // During Stop/Dispose we manually control this
207210

208-
var processId = await EmbeddedServer.Instance.GetServerProcessIdAsync(cancellationToken);
209211

210212
// Dispose always need to be invoked, even when already cancelled
211213
var disposeTask = Task.Run(() => EmbeddedServer.Instance.Dispose(), CancellationToken.None);
212214

215+
// Runs "infinite" but will eventually get cancelled
213216
var waitForCancellationTask = Task.Delay(Timeout.Infinite, cancellationToken);
214217

215218
var delayIsCancelled = waitForCancellationTask == await Task.WhenAny(disposeTask, waitForCancellationTask);
216219

217220
if (delayIsCancelled)
218221
{
222+
int processId = 0;
219223
try
220224
{
225+
// We always want to try and kill the process, even when already cancelled
226+
processId = await EmbeddedServer.Instance.GetServerProcessIdAsync(CancellationToken.None);
221227
Logger.WarnFormat("Killing RavenDB server PID {0} because host cancelled", processId);
222228
using var ravenChildProcess = Process.GetProcessById(processId);
223229
ravenChildProcess.Kill(entireProcessTree: true);
224230
// Kill only signals
225231
Logger.WarnFormat("Waiting for RavenDB server PID {0} to exit... ", processId);
232+
// When WaitForExitAsync returns, the process could still exist but in a frozen state to flush
233+
// memory mapped pages to storage.
226234
await ravenChildProcess.WaitForExitAsync(CancellationToken.None);
227235
}
228236
catch (Exception e)

0 commit comments

Comments
 (0)