17
17
using Microsoft . ApplicationInsights . DataContracts ;
18
18
using Microsoft . AspNetCore . Hosting ;
19
19
using Microsoft . AspNetCore . TestHost ;
20
+ using Microsoft . Azure . Storage ;
21
+ using Microsoft . Azure . Storage . Queue ;
20
22
using Microsoft . Azure . WebJobs . Host . Config ;
21
23
using Microsoft . Azure . WebJobs . Host . Storage ;
22
24
using Microsoft . Azure . WebJobs . Logging ;
32
34
using Microsoft . Extensions . Logging ;
33
35
using Microsoft . Extensions . Options ;
34
36
using Microsoft . WebJobs . Script . Tests ;
35
- using TestFunctions ;
36
37
using Xunit ;
37
38
using Xunit . Abstractions ;
38
39
using IApplicationLifetime = Microsoft . AspNetCore . Hosting . IApplicationLifetime ;
40
+ using LogLevel = Microsoft . Extensions . Logging . LogLevel ;
39
41
40
42
namespace Microsoft . Azure . WebJobs . Script . Tests
41
43
{
@@ -797,7 +799,7 @@ public async Task Specialization_JobHostInternalStorageOptionsUpdatesWithActiveH
797
799
[ Fact ]
798
800
public async Task DotNetIsolated_PlaceholderHit ( )
799
801
{
800
- var builder = InitializeDotNetIsolatedPlaceholderBuilder ( ) ;
802
+ var builder = InitializeDotNetIsolatedPlaceholderBuilder ( "Function1" ) ;
801
803
802
804
using var testServer = new TestServer ( builder ) ;
803
805
@@ -860,14 +862,88 @@ await DotNetIsolatedPlaceholderMiss(() =>
860
862
Assert . Contains ( "Shutting down placeholder worker. Worker is not compatible for runtime: dotnet-isolated" , log ) ;
861
863
}
862
864
863
- private async Task DotNetIsolatedPlaceholderMiss ( Action additionalSpecializedSetup = null )
865
+ [ Fact ]
866
+ // Fix for https://github.com/Azure/azure-functions-host/issues/9288
867
+ public async Task SpecializedSite_StopsHostBeforeWorker ( )
864
868
{
865
- var builder = InitializeDotNetIsolatedPlaceholderBuilder ( ( ) =>
869
+ // this app has a QueueTrigger reading from "myqueue-items"
870
+ // add a few messages there before stopping the host
871
+ var storageValue = TestHelpers . GetTestConfiguration ( ) . GetWebJobsConnectionString ( "AzureWebJobsStorage" ) ;
872
+ CloudStorageAccount storageAccount = CloudStorageAccount . Parse ( storageValue ) ;
873
+ CloudQueueClient queueClient = storageAccount . CreateCloudQueueClient ( ) ;
874
+ CloudQueue queue = queueClient . GetQueueReference ( "myqueue-items" ) ;
875
+ await queue . CreateIfNotExistsAsync ( ) ;
876
+ await queue . ClearAsync ( ) ;
877
+
878
+ var builder = InitializeDotNetIsolatedPlaceholderBuilder ( "Function1" , "QueueFunction" ) ;
879
+
880
+ using var testServer = new TestServer ( builder ) ;
881
+
882
+ var client = testServer . CreateClient ( ) ;
883
+ var response = await client . GetAsync ( "api/warmup" ) ;
884
+ response . EnsureSuccessStatusCode ( ) ;
885
+
886
+ _environment . SetEnvironmentVariable ( "AzureWebJobsStorage" , storageValue ) ;
887
+ _environment . SetEnvironmentVariable ( EnvironmentSettingNames . AzureWebsiteContainerReady , "1" ) ;
888
+ _environment . SetEnvironmentVariable ( EnvironmentSettingNames . AzureWebsitePlaceholderMode , "0" ) ;
889
+
890
+ response = await client . GetAsync ( "api/function1" ) ;
891
+ response . EnsureSuccessStatusCode ( ) ;
892
+
893
+ var scriptHostManager = testServer . Services . GetService < IScriptHostManager > ( ) ;
894
+
895
+ scriptHostManager . ActiveHostChanged += ( object sender , ActiveHostChangedEventArgs e ) =>
866
896
{
867
- // remove WEBSITE_USE_PLACEHOLDER_DOTNETISOLATED
868
- _environment . SetEnvironmentVariable ( EnvironmentSettingNames . AzureWebsiteUsePlaceholderDotNetIsolated , null ) ;
897
+ // for this test, this signals the host is about to shut down, so introduce an
898
+ // intentional delay to simulate a race condition
899
+ //
900
+ // there was a bug where we'd stop the worker channel and process before the host, resulting in
901
+ // a lot of "Did not find initialized language worker" errors due to a race between the process
902
+ // and listeners shutting down
903
+ if ( e . NewHost == null )
904
+ {
905
+ Thread . Sleep ( 1000 ) ;
906
+ }
907
+ } ;
908
+
909
+ bool keepRunning = true ;
910
+
911
+ Task messageTask = Task . Run ( async ( ) =>
912
+ {
913
+ while ( keepRunning )
914
+ {
915
+ await queue . AddMessageAsync ( new CloudQueueMessage ( "test" ) ) ;
916
+ }
869
917
} ) ;
870
918
919
+ // make sure the invocations are flowing before we stop the host
920
+ await TestHelpers . Await ( ( ) =>
921
+ {
922
+ int completed = _loggerProvider . GetAllLogMessages ( ) . Count ( p => p . Category == "Function.QueueFunction" && p . EventId . Name == "FunctionCompleted" ) ;
923
+ return completed > 10 ;
924
+ } ) ;
925
+
926
+ await testServer . Host . StopAsync ( ) ;
927
+
928
+ keepRunning = false ;
929
+ await messageTask ;
930
+ await queue . ClearAsync ( ) ;
931
+
932
+ var completedLogs = _loggerProvider . GetAllLogMessages ( )
933
+ . Where ( p => p . Category == "Function.QueueFunction" )
934
+ . Where ( p => p . EventId . Name == "FunctionCompleted" ) ;
935
+
936
+ Assert . NotEmpty ( completedLogs . Where ( p => p . Level == LogLevel . Information ) ) ;
937
+ Assert . Empty ( completedLogs . Where ( p => p . Level == LogLevel . Error ) ) ;
938
+ }
939
+
940
+ private async Task DotNetIsolatedPlaceholderMiss ( Action additionalSpecializedSetup = null )
941
+ {
942
+ var builder = InitializeDotNetIsolatedPlaceholderBuilder ( "Function1" ) ;
943
+
944
+ // remove WEBSITE_USE_PLACEHOLDER_DOTNETISOLATED
945
+ _environment . SetEnvironmentVariable ( EnvironmentSettingNames . AzureWebsiteUsePlaceholderDotNetIsolated , null ) ;
946
+
871
947
using var testServer = new TestServer ( builder ) ;
872
948
873
949
var client = testServer . CreateClient ( ) ;
@@ -909,7 +985,7 @@ private static void BuildDotnetIsolated60()
909
985
p . WaitForExit ( ) ;
910
986
}
911
987
912
- private IWebHostBuilder InitializeDotNetIsolatedPlaceholderBuilder ( Action additionalSetup = null )
988
+ private IWebHostBuilder InitializeDotNetIsolatedPlaceholderBuilder ( params string [ ] functions )
913
989
{
914
990
BuildDotnetIsolated60 ( ) ;
915
991
@@ -918,9 +994,7 @@ private IWebHostBuilder InitializeDotNetIsolatedPlaceholderBuilder(Action additi
918
994
_environment . SetEnvironmentVariable ( EnvironmentSettingNames . AzureWebJobsFeatureFlags , ScriptConstants . FeatureFlagEnableWorkerIndexing ) ;
919
995
_environment . SetEnvironmentVariable ( RpcWorkerConstants . FunctionWorkerRuntimeVersionSettingName , "6.0" ) ;
920
996
921
- additionalSetup ? . Invoke ( ) ;
922
-
923
- var builder = CreateStandbyHostBuilder ( "Function1" ) ;
997
+ var builder = CreateStandbyHostBuilder ( functions ) ;
924
998
925
999
builder . ConfigureAppConfiguration ( config =>
926
1000
{
0 commit comments