fix(inbound): single-flight method compile so concurrent first callers share one assembly (review 2026-10-21 05/M1)
This commit is contained in:
@@ -514,6 +514,37 @@ public class InboundScriptExecutorTests
|
||||
Assert.Empty(errors);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that concurrent first callers of a new method share a single compile.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task ExecuteAsync_ConcurrentFirstCallers_ShareOneCompile()
|
||||
{
|
||||
// Review 2026-10-21 05/M1: N concurrent first-callers used to each run Roslyn (N
|
||||
// non-collectible loaded assemblies for one revision). Single-flight per name+revision.
|
||||
var logger = new CompileCountingLogger();
|
||||
var executor = new InboundScriptExecutor(logger, Substitute.For<IServiceProvider>());
|
||||
var method = new ApiMethod("burst", "var x = 0; for (int i = 0; i < 10; i++) x += i; return x;") { Id = 1, TimeoutSeconds = 30 };
|
||||
|
||||
var results = await Task.WhenAll(Enumerable.Range(0, 12).Select(_ => Task.Run(() =>
|
||||
executor.ExecuteAsync(method, new Dictionary<string, object?>(), _route, TimeSpan.FromSeconds(30)))));
|
||||
|
||||
Assert.All(results, r => Assert.True(r.Success, r.ErrorMessage));
|
||||
Assert.Equal(1, logger.CompileCount);
|
||||
}
|
||||
|
||||
private sealed class CompileCountingLogger : ILogger<InboundScriptExecutor>
|
||||
{
|
||||
private int _count;
|
||||
public int CompileCount => _count;
|
||||
public IDisposable? BeginScope<TState>(TState state) where TState : notnull => null;
|
||||
public bool IsEnabled(LogLevel logLevel) => true;
|
||||
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
|
||||
{
|
||||
if (formatter(state, exception).EndsWith("script compiled", StringComparison.Ordinal))
|
||||
Interlocked.Increment(ref _count);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Verifies that executing after a non-compiling update surfaces the compile failure.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
|
||||
Reference in New Issue
Block a user