Skip to content

Commit 32cf5a4

Browse files
v-imohammadkshyjujviau
authored
Merging all required commits from in-proc into release/in-proc (#10289)
* JIT Trace - 4.35.0 using in-proc as base (#10281) * Adding a timeout to `IFunctionProvider.GetFunctionMetadataAsync` (#10249) (#10273) * Adding a timeout to IFunctionProvider.GetFunctionMetadataAsync so that if a provider does not return, it will not cause a deadlock state. * Adding release notes. * Added a new internal property `MetadataProviderTimeoutInSeconds` which can be set to a different value than default, from the tests. * Added comment about property being settable. * PR feedback fixes. Throws an error when the function metadata provider method throws or when the operation timeseout. * [in-proc port] Ensure extension RPC endpoints ready before processing gRPC messages (#10282) * Ensure extension RPC endpoints ready before processing gRPC messages (#10255) * Ensure extension RPC endpoints ready before processing gRPC messages * Add timeout and tests to waiting on RPC extensions. * update release_notes.md * [in-proc] Update Microsoft.Azure.WebJobs to 3.0.41 and Microsoft.Azure.WebJobs.Host.Storage to 5.0.1 (#10288) * Upgraded the following package versions: - Microsoft.Azure.WebJobs updated to 3.0.41 - Microsoft.Azure.WebJobs.Host.Storage updated to 5.0.1 - Microsoft.Extensions.Azure updated to 1.7.1 - Azure.Storage.Blobs updated to 12.19.1 * Updating deps.json for Tests. --------- Co-authored-by: Shyju Krishnankutty <[email protected]> Co-authored-by: Jacob Viau <[email protected]>
1 parent 8e66618 commit 32cf5a4

12 files changed

+4140
-4928
lines changed

release_notes.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,10 @@
1313
- Fixed incorrect function count in the log message.(#10220)
1414
- Migrate Diagnostic Events to Azure.Data.Tables (#10218)
1515
- Sanitize worker arguments before logging (#10260)
16+
- Fix race condition on startup with extension RPC endpoints not being available. (#10282)
17+
- Adding a timeout when retrieving function metadata from metadata providers (#10219)
18+
- Upgraded the following package versions (#10288):
19+
- `Microsoft.Azure.WebJobs` updated to 3.0.41
20+
- `Microsoft.Azure.WebJobs.Host.Storage` updated to 5.0.1
21+
- `Microsoft.Extensions.Azure` updated to 1.7.1
22+
- `Azure.Storage.Blobs` updated to 12.19.1

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

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@
66
using System.Diagnostics.CodeAnalysis;
77
using System.Linq;
88
using System.Threading;
9+
using System.Threading.Tasks;
910
using Microsoft.AspNetCore.Http;
1011
using Microsoft.AspNetCore.Routing;
1112
using Microsoft.Azure.WebJobs.Rpc.Core.Internal;
1213
using Microsoft.Extensions.DependencyInjection;
14+
using Microsoft.Extensions.Logging;
1315
using Microsoft.Extensions.Primitives;
1416

1517
namespace Microsoft.Azure.WebJobs.Script.Grpc
@@ -26,6 +28,7 @@ internal sealed class ExtensionsCompositeEndpointDataSource : EndpointDataSource
2628
private readonly object _lock = new();
2729
private readonly List<EndpointDataSource> _dataSources = new();
2830
private readonly IScriptHostManager _scriptHostManager;
31+
private readonly TaskCompletionSource _initialized = new();
2932

3033
private IServiceProvider _extensionServices;
3134
private List<Endpoint> _endpoints;
@@ -191,6 +194,7 @@ private void OnHostChanged(object sender, ActiveHostChangedEventArgs args)
191194
.GetService<IEnumerable<WebJobsRpcEndpointDataSource>>()
192195
?? Enumerable.Empty<WebJobsRpcEndpointDataSource>();
193196
_dataSources.AddRange(sources);
197+
_initialized.TrySetResult(); // signal we have first initialized.
194198
}
195199
else
196200
{
@@ -301,5 +305,49 @@ private void ThrowIfDisposed()
301305
throw new ObjectDisposedException(nameof(ExtensionsCompositeEndpointDataSource));
302306
}
303307
}
308+
309+
/// <summary>
310+
/// Middleware to ensure <see cref="ExtensionsCompositeEndpointDataSource"/> is initialized before routing for the first time.
311+
/// Must be registered as a singleton service.
312+
/// </summary>
313+
/// <param name="dataSource">The <see cref="ExtensionsCompositeEndpointDataSource"/> to ensure is initialized.</param>
314+
/// <param name="logger">The logger.</param>
315+
public sealed class EnsureInitializedMiddleware(ExtensionsCompositeEndpointDataSource dataSource, ILogger<EnsureInitializedMiddleware> logger) : IMiddleware
316+
{
317+
private TaskCompletionSource _initialized = new();
318+
private bool _firstRun = true;
319+
320+
// used for testing to verify initialization success.
321+
internal Task Initialized => _initialized.Task;
322+
323+
// settable only for testing purposes.
324+
internal TimeSpan Timeout { get; init; } = TimeSpan.FromSeconds(2);
325+
326+
public Task InvokeAsync(HttpContext context, RequestDelegate next)
327+
{
328+
return _firstRun ? InvokeCoreAsync(context, next) : next(context);
329+
}
330+
331+
private async Task InvokeCoreAsync(HttpContext context, RequestDelegate next)
332+
{
333+
try
334+
{
335+
await dataSource._initialized.Task.WaitAsync(Timeout);
336+
}
337+
catch (TimeoutException ex)
338+
{
339+
// In case of deadlock we don't want to block all gRPC requests.
340+
// Log an error and continue.
341+
logger.LogError(ex, "Error initializing extension endpoints.");
342+
_initialized.TrySetException(ex);
343+
}
344+
345+
// Even in case of timeout we don't want to continually test for initialization on subsequent requests.
346+
// That would be a serious performance degredation.
347+
_firstRun = false;
348+
_initialized.TrySetResult();
349+
await next(context);
350+
}
351+
}
304352
}
305353
}

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ internal class Startup
1616
public void ConfigureServices(IServiceCollection services)
1717
{
1818
services.AddSingleton<ExtensionsCompositeEndpointDataSource>();
19+
services.AddSingleton<ExtensionsCompositeEndpointDataSource.EnsureInitializedMiddleware>();
1920
services.AddGrpc(options =>
2021
{
2122
options.MaxReceiveMessageSize = MaxMessageLengthBytes;
@@ -30,12 +31,16 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
3031
app.UseDeveloperExceptionPage();
3132
}
3233

34+
// This must occur before 'UseRouting'. This ensures extension endpoints are registered before the
35+
// endpoints are collected by the routing middleware.
36+
app.UseMiddleware<ExtensionsCompositeEndpointDataSource.EnsureInitializedMiddleware>();
3337
app.UseRouting();
3438

3539
app.UseEndpoints(endpoints =>
3640
{
3741
endpoints.MapGrpcService<FunctionRpc.FunctionRpcBase>();
38-
endpoints.DataSources.Add(endpoints.ServiceProvider.GetRequiredService<ExtensionsCompositeEndpointDataSource>());
42+
endpoints.DataSources.Add(
43+
endpoints.ServiceProvider.GetRequiredService<ExtensionsCompositeEndpointDataSource>());
3944
});
4045
}
4146
}

0 commit comments

Comments
 (0)