29
29
using Microsoft . Azure . WebJobs . Script . Workers ;
30
30
using Microsoft . Azure . WebJobs . Script . Workers . Rpc ;
31
31
using Microsoft . Azure . WebJobs . Script . Workers . SharedMemoryDataTransfer ;
32
+ using Microsoft . Extensions . Hosting ;
32
33
using Microsoft . Extensions . Logging ;
33
34
using Microsoft . Extensions . Options ;
34
35
using Yarp . ReverseProxy . Forwarder ;
@@ -42,6 +43,7 @@ namespace Microsoft.Azure.WebJobs.Script.Grpc
42
43
internal partial class GrpcWorkerChannel : IRpcWorkerChannel , IDisposable
43
44
{
44
45
private readonly IScriptEventManager _eventManager ;
46
+ private readonly IScriptHostManager _scriptHostManager ;
45
47
private readonly RpcWorkerConfig _workerConfig ;
46
48
private readonly string _runtime ;
47
49
private readonly IEnvironment _environment ;
@@ -81,16 +83,18 @@ internal partial class GrpcWorkerChannel : IRpcWorkerChannel, IDisposable
81
83
private TaskCompletionSource < List < RawFunctionMetadata > > _functionsIndexingTask = new TaskCompletionSource < List < RawFunctionMetadata > > ( TaskCreationOptions . RunContinuationsAsynchronously ) ;
82
84
private TimeSpan _functionLoadTimeout = TimeSpan . FromMinutes ( 1 ) ;
83
85
private bool _isSharedMemoryDataTransferEnabled ;
84
- private bool ? _cancelCapabilityEnabled ;
86
+ private bool _isHandlesInvocationCancelMessageCapabilityEnabled ;
85
87
private bool _isWorkerApplicationInsightsLoggingEnabled ;
86
88
private IHttpProxyService _httpProxyService ;
87
89
private Uri _httpProxyEndpoint ;
88
90
private System . Timers . Timer _timer ;
89
91
private bool _functionMetadataRequestSent = false ;
92
+ private IOptions < ScriptJobHostOptions > _scriptHostOptions ;
90
93
91
94
internal GrpcWorkerChannel (
92
95
string workerId ,
93
96
IScriptEventManager eventManager ,
97
+ IScriptHostManager hostManager ,
94
98
RpcWorkerConfig workerConfig ,
95
99
IWorkerProcess rpcWorkerProcess ,
96
100
ILogger logger ,
@@ -105,6 +109,7 @@ internal GrpcWorkerChannel(
105
109
{
106
110
_workerId = workerId ;
107
111
_eventManager = eventManager ;
112
+ _scriptHostManager = hostManager ;
108
113
_workerConfig = workerConfig ;
109
114
_runtime = workerConfig . Description . Language ;
110
115
_rpcWorkerProcess = rpcWorkerProcess ;
@@ -140,6 +145,10 @@ internal GrpcWorkerChannel(
140
145
141
146
// Temporary switch to allow fully testing new algorithm in production
142
147
_messageDispatcherFactory = GetProcessorFactory ( ) ;
148
+
149
+ _scriptHostManager . ActiveHostChanged += HandleActiveHostChange ;
150
+
151
+ LoadScriptJobHostOptions ( _scriptHostManager as IServiceProvider ) ;
143
152
}
144
153
145
154
private bool IsHttpProxyingWorker => _httpProxyEndpoint is not null ;
@@ -152,6 +161,30 @@ internal GrpcWorkerChannel(
152
161
153
162
public RpcWorkerConfig WorkerConfig => _workerConfig ;
154
163
164
+ public IOptions < ScriptJobHostOptions > JobHostOptions => _scriptHostOptions ;
165
+
166
+ private void HandleActiveHostChange ( object sender , ActiveHostChangedEventArgs e )
167
+ {
168
+ if ( e . NewHost is null )
169
+ {
170
+ return ;
171
+ }
172
+
173
+ LoadScriptJobHostOptions ( e . NewHost . Services ) ;
174
+ }
175
+
176
+ private void LoadScriptJobHostOptions ( IServiceProvider provider )
177
+ {
178
+ if ( provider ? . GetService ( typeof ( IOptions < ScriptJobHostOptions > ) ) is IOptions < ScriptJobHostOptions > scriptHostOptions )
179
+ {
180
+ _scriptHostOptions = scriptHostOptions ;
181
+ }
182
+ else
183
+ {
184
+ _workerChannelLogger . LogDebug ( "Unable to resolve ScriptJobHostOptions" ) ;
185
+ }
186
+ }
187
+
155
188
// Temporary switch that allows us to move between the "old" ThreadPool-only processor
156
189
// and a "new" Channel processor (for proper ordering of messages).
157
190
private IInvocationMessageDispatcherFactory GetProcessorFactory ( )
@@ -511,7 +544,7 @@ internal void ApplyCapabilities(IDictionary<string, string> capabilities, GrpcCa
511
544
UpdateCapabilities ( capabilities , strategy ) ;
512
545
513
546
_isSharedMemoryDataTransferEnabled = IsSharedMemoryDataTransferEnabled ( ) ;
514
- _cancelCapabilityEnabled ?? = ! string . IsNullOrEmpty ( _workerCapabilities . GetCapabilityState ( RpcWorkerConstants . HandlesInvocationCancelMessage ) ) ;
547
+ _isHandlesInvocationCancelMessageCapabilityEnabled = ! string . IsNullOrEmpty ( _workerCapabilities . GetCapabilityState ( RpcWorkerConstants . HandlesInvocationCancelMessage ) ) ;
515
548
516
549
if ( ! _isSharedMemoryDataTransferEnabled )
517
550
{
@@ -816,12 +849,21 @@ internal async Task SendInvocationRequest(ScriptInvocationContext context)
816
849
return ;
817
850
}
818
851
819
- // do not send an invocation request if cancellation has been requested
820
852
if ( context . CancellationToken . IsCancellationRequested )
821
853
{
822
- _workerChannelLogger . LogWarning ( "Cancellation has been requested. The invocation request with id '{invocationId}' is canceled and will not be sent to the worker." , invocationId ) ;
823
- context . ResultSource . TrySetCanceled ( ) ;
824
- return ;
854
+ _workerChannelLogger . LogDebug ( "Cancellation was requested prior to the invocation request ('{invocationId}') being sent to the worker." , invocationId ) ;
855
+
856
+ // If the worker does not support handling InvocationCancel grpc messages, or if cancellation is supported and the customer opts-out
857
+ // of sending cancelled invocations to the worker, we will cancel the result source and not send the invocation to the worker.
858
+ if ( ! _isHandlesInvocationCancelMessageCapabilityEnabled || ! JobHostOptions . Value . SendCanceledInvocationsToWorker )
859
+ {
860
+ _workerChannelLogger . LogInformation ( "Cancelling invocation '{invocationId}' due to cancellation token being signaled. "
861
+ + "This invocation was not sent to the worker. Read more about this here: https://aka.ms/azure-functions-cancellations" , invocationId ) ;
862
+
863
+ // This will result in an invocation failure with a "FunctionInvocationCanceled" exception.
864
+ context . ResultSource . TrySetCanceled ( ) ;
865
+ return ;
866
+ }
825
867
}
826
868
827
869
var invocationRequest = await context . ToRpcInvocationRequest ( _workerChannelLogger , _workerCapabilities , _isSharedMemoryDataTransferEnabled , _sharedMemoryManager ) ;
@@ -834,9 +876,10 @@ await SendStreamingMessageAsync(new StreamingMessage
834
876
InvocationRequest = invocationRequest
835
877
} ) ;
836
878
837
- if ( _cancelCapabilityEnabled != null && _cancelCapabilityEnabled . Value )
879
+ if ( _isHandlesInvocationCancelMessageCapabilityEnabled )
838
880
{
839
- context . CancellationToken . Register ( ( ) => SendInvocationCancel ( invocationRequest . InvocationId ) ) ;
881
+ var cancellationCtr = context . CancellationToken . Register ( ( ) => SendInvocationCancel ( invocationRequest . InvocationId ) ) ;
882
+ context . Properties . Add ( ScriptConstants . CancellationTokenRegistration , cancellationCtr ) ;
840
883
}
841
884
842
885
if ( IsHttpProxyingWorker && context . FunctionMetadata . IsHttpTriggerFunction ( ) )
@@ -1038,6 +1081,13 @@ internal async Task InvokeResponse(InvocationResponse invokeResponse)
1038
1081
if ( _executingInvocations . TryRemove ( invokeResponse . InvocationId , out var invocation ) )
1039
1082
{
1040
1083
var context = invocation . Context ;
1084
+
1085
+ if ( context . Properties . TryGetValue ( ScriptConstants . CancellationTokenRegistration , out CancellationTokenRegistration ctr ) )
1086
+ {
1087
+ await ctr . DisposeAsync ( ) ;
1088
+ context . Properties . Remove ( ScriptConstants . CancellationTokenRegistration ) ;
1089
+ }
1090
+
1041
1091
if ( invokeResponse . Result . IsInvocationSuccess ( context . ResultSource , capabilityEnabled ) )
1042
1092
{
1043
1093
_metricsLogger . LogEvent ( string . Format ( MetricEventNames . WorkerInvokeSucceeded , Id ) ) ;
@@ -1355,6 +1405,7 @@ protected virtual void Dispose(bool disposing)
1355
1405
_startLatencyMetric ? . Dispose ( ) ;
1356
1406
_workerInitTask ? . TrySetCanceled ( ) ;
1357
1407
_timer ? . Dispose ( ) ;
1408
+ _scriptHostManager . ActiveHostChanged -= HandleActiveHostChange ;
1358
1409
1359
1410
// unlink function inputs
1360
1411
if ( _inputLinks is not null )
0 commit comments