Skip to content

Commit d75203e

Browse files
authored
Do not unregister extension endpoints on shutdown (#10522)
* Do not unregister extension endpoints on shutdown * Add comment about known RPC issue * Fix unit test build * Update unit test condition * Remove NotEmpty check
1 parent 65e1bcf commit d75203e

File tree

3 files changed

+31
-27
lines changed

3 files changed

+31
-27
lines changed

src/WebJobs.Script.Grpc/Server/ExtensionsCompositeEndpointDataSource.cs

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,21 +26,25 @@ namespace Microsoft.Azure.WebJobs.Script.Grpc
2626
internal sealed class ExtensionsCompositeEndpointDataSource : EndpointDataSource, IDisposable
2727
{
2828
private readonly object _lock = new();
29-
private readonly List<EndpointDataSource> _dataSources = new();
3029
private readonly IScriptHostManager _scriptHostManager;
3130
private readonly TaskCompletionSource _initialized = new();
31+
private readonly ILogger _logger;
3232

33+
private IList<EndpointDataSource> _dataSources = Array.Empty<EndpointDataSource>();
3334
private IServiceProvider _extensionServices;
3435
private List<Endpoint> _endpoints;
3536
private IChangeToken _consumerChangeToken;
3637
private CancellationTokenSource _cts;
3738
private List<IDisposable> _changeTokenRegistrations;
3839
private bool _disposed;
3940

40-
public ExtensionsCompositeEndpointDataSource(IScriptHostManager scriptHostManager)
41+
public ExtensionsCompositeEndpointDataSource(
42+
IScriptHostManager scriptHostManager,
43+
ILogger<ExtensionsCompositeEndpointDataSource> logger)
4144
{
4245
_scriptHostManager = scriptHostManager;
4346
_scriptHostManager.ActiveHostChanged += OnHostChanged;
47+
_logger = logger;
4448
}
4549

4650
/// <inheritdoc />
@@ -186,23 +190,21 @@ private void OnHostChanged(object sender, ActiveHostChangedEventArgs args)
186190
{
187191
lock (_lock)
188192
{
189-
_dataSources.Clear();
193+
// Do not clear out endpoints when host changes to null, as functions may still be running.
194+
// TODO: there are still edge cases where we will switch active hosts, leading to potential
195+
// issues with draining invocations.
190196
if (args?.NewHost?.Services is { } services)
191197
{
192198
_extensionServices = services;
193-
IEnumerable<WebJobsRpcEndpointDataSource> sources = services
194-
.GetService<IEnumerable<WebJobsRpcEndpointDataSource>>()
195-
?? Enumerable.Empty<WebJobsRpcEndpointDataSource>();
196-
_dataSources.AddRange(sources);
199+
IEnumerable<EndpointDataSource> sources = services.GetServices<WebJobsRpcEndpointDataSource>();
200+
_dataSources = sources.ToList();
201+
202+
int totalEndpoints = _dataSources.Sum(x => x.Endpoints.Count);
203+
_logger.LogDebug("Host changed. Registering {ExtensionCount} extension data sources and {EndpointCount} total endpoints.", _dataSources.Count, totalEndpoints);
197204
_initialized.TrySetResult(); // signal we have first initialized.
198-
}
199-
else
200-
{
201-
_extensionServices = null;
205+
OnEndpointsChange(collectionChanged: true);
202206
}
203207
}
204-
205-
OnEndpointsChange(collectionChanged: true);
206208
}
207209

208210
private void OnEndpointsChange(bool collectionChanged)

src/WebJobs.Script/Host/IScriptHostManager.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public interface IScriptHostManager
1515
event EventHandler HostInitializing;
1616

1717
/// <summary>
18-
/// Evewnt raised when the active host managed by this instance changes.
18+
/// Event raised when the active host managed by this instance changes.
1919
/// </summary>
2020
event EventHandler<ActiveHostChangedEventArgs> ActiveHostChanged;
2121

test/WebJobs.Script.Tests/Workers/Rpc/ExtensionsCompositeEndpointDataSourceTests.cs

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,35 +21,37 @@ namespace Microsoft.Azure.WebJobs.Script.Tests.Workers.Rpc
2121
{
2222
public class ExtensionsCompositeEndpointDataSourceTests
2323
{
24-
private static readonly ILogger<ExtensionsCompositeEndpointDataSource.EnsureInitializedMiddleware> _logger
24+
private static readonly ILogger<ExtensionsCompositeEndpointDataSource> _dataSourceLogger
25+
= NullLogger<ExtensionsCompositeEndpointDataSource>.Instance;
26+
27+
private static readonly ILogger<ExtensionsCompositeEndpointDataSource.EnsureInitializedMiddleware> _middlewareLogger
2528
= NullLogger<ExtensionsCompositeEndpointDataSource.EnsureInitializedMiddleware>.Instance;
2629

2730
[Fact]
2831
public void NoActiveHost_NoEndpoints()
2932
{
30-
ExtensionsCompositeEndpointDataSource dataSource = new(Mock.Of<IScriptHostManager>());
33+
ExtensionsCompositeEndpointDataSource dataSource = new(Mock.Of<IScriptHostManager>(), _dataSourceLogger);
3134
Assert.Empty(dataSource.Endpoints);
3235
}
3336

3437
[Fact]
35-
public void ActiveHostChanged_NullHost_NoEndpoints()
38+
public void ActiveHostChanged_NullHost_EndpointsRemain()
3639
{
3740
Mock<IScriptHostManager> manager = new();
38-
ExtensionsCompositeEndpointDataSource dataSource = new(manager.Object);
41+
ExtensionsCompositeEndpointDataSource dataSource = new(manager.Object, _dataSourceLogger);
3942

4043
IChangeToken token = dataSource.GetChangeToken();
4144
Assert.False(token.HasChanged);
4245
manager.Raise(x => x.ActiveHostChanged += null, new ActiveHostChangedEventArgs(null, null));
43-
Assert.True(token.HasChanged);
44-
Assert.Empty(dataSource.Endpoints);
46+
Assert.False(token.HasChanged);
4547
}
4648

4749
[Fact]
4850
public void ActiveHostChanged_NoExtensions_NoEndpoints()
4951
{
5052
Mock<IScriptHostManager> manager = new();
5153

52-
ExtensionsCompositeEndpointDataSource dataSource = new(manager.Object);
54+
ExtensionsCompositeEndpointDataSource dataSource = new(manager.Object, _dataSourceLogger);
5355

5456
IChangeToken token = dataSource.GetChangeToken();
5557
Assert.False(token.HasChanged);
@@ -62,7 +64,7 @@ public void ActiveHostChanged_NoExtensions_NoEndpoints()
6264
public void ActiveHostChanged_NewExtensions_NewEndpoints()
6365
{
6466
Mock<IScriptHostManager> manager = new();
65-
ExtensionsCompositeEndpointDataSource dataSource = new(manager.Object);
67+
ExtensionsCompositeEndpointDataSource dataSource = new(manager.Object, _dataSourceLogger);
6668
IHost host = GetHost(new TestEndpoints(new Endpoint(null, null, "Test1"), new Endpoint(null, null, "Test2")));
6769

6870
IChangeToken token = dataSource.GetChangeToken();
@@ -80,9 +82,9 @@ public async Task ActiveHostChanged_MiddlewareWaits_Success()
8082
{
8183
Mock<IScriptHostManager> manager = new();
8284

83-
ExtensionsCompositeEndpointDataSource dataSource = new(manager.Object);
85+
ExtensionsCompositeEndpointDataSource dataSource = new(manager.Object, _dataSourceLogger);
8486
ExtensionsCompositeEndpointDataSource.EnsureInitializedMiddleware middleware =
85-
new(dataSource, _logger) { Timeout = Timeout.InfiniteTimeSpan };
87+
new(dataSource, _middlewareLogger) { Timeout = Timeout.InfiniteTimeSpan };
8688
TestDelegate next = new();
8789

8890
Task waiter = middleware.InvokeAsync(null, next.InvokeAsync);
@@ -99,9 +101,9 @@ public async Task NoActiveHostChanged_MiddlewareWaits_Timeout()
99101
{
100102
Mock<IScriptHostManager> manager = new();
101103

102-
ExtensionsCompositeEndpointDataSource dataSource = new(manager.Object);
104+
ExtensionsCompositeEndpointDataSource dataSource = new(manager.Object, _dataSourceLogger);
103105
ExtensionsCompositeEndpointDataSource.EnsureInitializedMiddleware middleware =
104-
new(dataSource, _logger) { Timeout = TimeSpan.Zero };
106+
new(dataSource, _middlewareLogger) { Timeout = TimeSpan.Zero };
105107
TestDelegate next = new();
106108

107109
await middleware.InvokeAsync(null, next.InvokeAsync).WaitAsync(TimeSpan.FromSeconds(5)); // should not throw
@@ -118,7 +120,7 @@ public async Task NoActiveHostChanged_MiddlewareWaits_Timeout()
118120
public void Dispose_GetThrows()
119121
{
120122
Mock<IScriptHostManager> manager = new();
121-
ExtensionsCompositeEndpointDataSource dataSource = new(manager.Object);
123+
ExtensionsCompositeEndpointDataSource dataSource = new(manager.Object, _dataSourceLogger);
122124
IHost host = GetHost(new TestEndpoints(new Endpoint(null, null, "Test1"), new Endpoint(null, null, "Test2")));
123125
manager.Raise(x => x.ActiveHostChanged += null, new ActiveHostChangedEventArgs(null, host));
124126
dataSource.Dispose();

0 commit comments

Comments
 (0)