1717using Microsoft . ApplicationInsights . DataContracts ;
1818using Microsoft . AspNetCore . Hosting ;
1919using Microsoft . AspNetCore . TestHost ;
20+ using Microsoft . Azure . Storage ;
21+ using Microsoft . Azure . Storage . Queue ;
2022using Microsoft . Azure . WebJobs . Host . Config ;
2123using Microsoft . Azure . WebJobs . Host . Storage ;
2224using Microsoft . Azure . WebJobs . Logging ;
3234using Microsoft . Extensions . Logging ;
3335using Microsoft . Extensions . Options ;
3436using Microsoft . WebJobs . Script . Tests ;
35- using TestFunctions ;
3637using Xunit ;
3738using Xunit . Abstractions ;
3839using IApplicationLifetime = Microsoft . AspNetCore . Hosting . IApplicationLifetime ;
40+ using LogLevel = Microsoft . Extensions . Logging . LogLevel ;
3941
4042namespace Microsoft . Azure . WebJobs . Script . Tests
4143{
@@ -797,7 +799,7 @@ public async Task Specialization_JobHostInternalStorageOptionsUpdatesWithActiveH
797799 [ Fact ]
798800 public async Task DotNetIsolated_PlaceholderHit ( )
799801 {
800- var builder = InitializeDotNetIsolatedPlaceholderBuilder ( ) ;
802+ var builder = InitializeDotNetIsolatedPlaceholderBuilder ( "Function1" ) ;
801803
802804 using var testServer = new TestServer ( builder ) ;
803805
@@ -860,14 +862,88 @@ await DotNetIsolatedPlaceholderMiss(() =>
860862 Assert . Contains ( "Shutting down placeholder worker. Worker is not compatible for runtime: dotnet-isolated" , log ) ;
861863 }
862864
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 ( )
864868 {
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 ) =>
866896 {
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+ }
869917 } ) ;
870918
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+
871947 using var testServer = new TestServer ( builder ) ;
872948
873949 var client = testServer . CreateClient ( ) ;
@@ -909,7 +985,7 @@ private static void BuildDotnetIsolated60()
909985 p . WaitForExit ( ) ;
910986 }
911987
912- private IWebHostBuilder InitializeDotNetIsolatedPlaceholderBuilder ( Action additionalSetup = null )
988+ private IWebHostBuilder InitializeDotNetIsolatedPlaceholderBuilder ( params string [ ] functions )
913989 {
914990 BuildDotnetIsolated60 ( ) ;
915991
@@ -918,9 +994,7 @@ private IWebHostBuilder InitializeDotNetIsolatedPlaceholderBuilder(Action additi
918994 _environment . SetEnvironmentVariable ( EnvironmentSettingNames . AzureWebJobsFeatureFlags , ScriptConstants . FeatureFlagEnableWorkerIndexing ) ;
919995 _environment . SetEnvironmentVariable ( RpcWorkerConstants . FunctionWorkerRuntimeVersionSettingName , "6.0" ) ;
920996
921- additionalSetup ? . Invoke ( ) ;
922-
923- var builder = CreateStandbyHostBuilder ( "Function1" ) ;
997+ var builder = CreateStandbyHostBuilder ( functions ) ;
924998
925999 builder . ConfigureAppConfiguration ( config =>
9261000 {
0 commit comments