perf(worker): launcher-configurable event queue capacity

This commit is contained in:
Joseph Doherty
2026-08-15 17:10:54 -04:00
parent f3e1de5f37
commit 13583322b5
10 changed files with 253 additions and 5 deletions
@@ -1099,4 +1099,59 @@ public sealed class GatewayOptionsValidatorTests
Assert.True(result.Failed);
Assert.Contains(result.Failures!, f => f.Contains("MaxMessageBytes") && f.Contains("reserve"));
}
/// <summary>
/// Verifies the shipped worker event-queue capacity default passes validation and still matches
/// the worker-side <c>MxAccessEventQueue.DefaultCapacity</c> the environment variable falls back
/// to when the launcher value is unusable.
/// </summary>
[Fact]
public void Validate_Succeeds_WithDefaultEventQueueCapacity()
{
Assert.Equal(10000, new WorkerOptions().EventQueueCapacity);
ValidateOptionsResult result = new GatewayOptionsValidator().Validate(null, ValidOptions());
Assert.True(result.Succeeded);
}
/// <summary>
/// Verifies a worker event-queue capacity outside the supported range fails validation. Too
/// small leaves no burst headroom (an overflow faults the whole session); too large commits the
/// 32-bit worker to an outsized pre-allocation.
/// </summary>
/// <param name="eventQueueCapacity">Capacity under test.</param>
[Theory]
[InlineData(0)]
[InlineData(-1)]
[InlineData(999)]
[InlineData(1_000_001)]
public void Validate_Fails_WhenEventQueueCapacityOutOfRange(int eventQueueCapacity)
{
ValidateOptionsResult result = new GatewayOptionsValidator().Validate(
null,
WithWorkerAndProtocol(
new WorkerOptions { EventQueueCapacity = eventQueueCapacity },
new ProtocolOptions()));
Assert.True(result.Failed);
Assert.Contains(
result.Failures!,
f => f.Contains("MxGateway:Worker:EventQueueCapacity", StringComparison.Ordinal));
}
/// <summary>Verifies the worker event-queue capacity bounds themselves are accepted.</summary>
/// <param name="eventQueueCapacity">Capacity under test.</param>
[Theory]
[InlineData(1000)]
[InlineData(1_000_000)]
public void Validate_Succeeds_AtEventQueueCapacityBounds(int eventQueueCapacity)
{
ValidateOptionsResult result = new GatewayOptionsValidator().Validate(
null,
WithWorkerAndProtocol(
new WorkerOptions { EventQueueCapacity = eventQueueCapacity },
new ProtocolOptions()));
Assert.True(result.Succeeded);
}
}
@@ -47,6 +47,13 @@ public sealed class WorkerProcessLauncherTests
"1500",
processFactory.LastStartInfo.Environment[
WorkerProcessLauncher.WorkerWriteCompletionWaitEnvironmentVariableName]);
// The worker sizes its outbound event queue from the launch environment;
// the queue has no drop policy, so this capacity is the session's burst
// headroom rather than a throttle.
Assert.Equal(
"10000",
processFactory.LastStartInfo.Environment[
WorkerProcessLauncher.WorkerEventQueueCapacityEnvironmentVariableName]);
// MxGateway:Alarms defaults reach the worker's alarm poll loop and its
// GetXmlCurrentAlarms2 cap (which is also its truncation threshold)
// through the launch environment, not the command line.
@@ -64,6 +71,32 @@ public sealed class WorkerProcessLauncherTests
Assert.Equal(0, metrics.GetSnapshot().WorkersRunning);
}
/// <summary>
/// Verifies that a configured <see cref="WorkerOptions.EventQueueCapacity"/> — not the shipped
/// default — is what reaches the worker, so the option is deployable without a worker rebuild.
/// </summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task LaunchAsync_WithConfiguredEventQueueCapacity_ExportsItToTheWorkerEnvironment()
{
using TestDirectory directory = TestDirectory.Create();
string executablePath = directory.CreateWorkerExecutable(machine: 0x014c);
FakeWorkerProcessFactory processFactory = new(new FakeWorkerProcess(processId: 1234));
WorkerProcessLauncher launcher = CreateLauncher(
executablePath,
processFactory,
new SucceedingStartupProbe(),
eventQueueCapacity: 65536);
using WorkerProcessHandle handle = await launcher.LaunchAsync(CreateRequest());
Assert.NotNull(processFactory.LastStartInfo);
Assert.Equal(
"65536",
processFactory.LastStartInfo.Environment[
WorkerProcessLauncher.WorkerEventQueueCapacityEnvironmentVariableName]);
}
/// <summary>Verifies that a failed startup probe kills and disposes the worker process.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
@@ -216,7 +249,8 @@ public sealed class WorkerProcessLauncherTests
GatewayMetrics? metrics = null,
int startupTimeoutSeconds = 30,
int startupProbeRetryAttempts = 3,
int startupProbeRetryDelayMilliseconds = 250)
int startupProbeRetryDelayMilliseconds = 250,
int eventQueueCapacity = 10000)
{
GatewayOptions options = new()
{
@@ -227,6 +261,7 @@ public sealed class WorkerProcessLauncherTests
StartupTimeoutSeconds = startupTimeoutSeconds,
StartupProbeRetryAttempts = startupProbeRetryAttempts,
StartupProbeRetryDelayMilliseconds = startupProbeRetryDelayMilliseconds,
EventQueueCapacity = eventQueueCapacity,
},
};