Skip to content

Commit 2391b98

Browse files
kshyjuazfuncghCopilot
authored
Hotfix 4.1041.200 (#11176)
* Update version to 4.1041.200 * Clear release notes * Handles loading extensions.json with empty extensions (#11174) * Handle empty extensions array usecase. * release notes. * Liniting - remove empty line. * Fixed typo in test comment. * Update test/WebJobs.Script.Tests/ScriptStartupTypeDiscovererTests.cs Co-authored-by: Copilot <[email protected]> --------- Co-authored-by: Copilot <[email protected]> --------- Co-authored-by: Azure Functions Release <[email protected]> Co-authored-by: Copilot <[email protected]>
1 parent f85bdf5 commit 2391b98

File tree

4 files changed

+62
-22
lines changed

4 files changed

+62
-22
lines changed

release_notes.md

Lines changed: 3 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,6 @@
1+
### Release notes
2+
13
<!-- Please add your release notes in the following format:
24
- My change description (#PR)
35
-->
4-
- Memory allocation optimizations in `ScriptStartupTypeLocator.GetExtensionsStartupTypesAsync` (#11012)
5-
- Fix invocation timeout when incoming request contains "x-ms-invocation-id" header (#10980)
6-
- Warn if .azurefunctions folder does not exist (#10967)
7-
- Memory allocation & CPU optimizations in `GrpcMessageExtensionUtilities.ConvertFromHttpMessageToExpando` (#11054)
8-
- Replace `Timer` with `while`-loop in `FlexConsumptionMetricsPublisher` (#11071)
9-
- Memory allocation optimizations in `ReadLanguageWorkerFile` by reading files in buffered chunks, preventing LOH allocations (#11069)
10-
- Enhancing the capability to send startup failure logs to AppInsights/Otel. (#11055)
11-
- Added support for collecting cross-platform perf traces and generating PGO JIT traces (#11062)
12-
- Memory allocation optimizations in `DependencyHelper.GetExtensionRequirements` (#11022)
13-
- Fix Instance Manager for CV1 Migration (#11072)
14-
- Avoid setting up OTel and AzMon exporter in the placeholder mode. (#11090)
15-
- Memory allocation optimizations in `FeatureFlags.IsEnabled` by adopting zero-allocation `ContainsToken` for efficient delimited token search (#11075)
16-
- Improvements to coldstart pipeline (#11102).
17-
- Update Python Worker Version to [4.38.0](https://github.com/Azure/azure-functions-python-worker/releases/tag/4.38.0)
18-
- Only start the Diagnostic Events flush logs timer when events are present, preventing unnecessary flush attempts (#11100).
19-
- Enable HTTP proxying for custom handlers (#11035)
20-
- Switched memory usage reporting to use CGroup metrics by default for Linux consumption (#11114)
21-
- Update Java Worker Version to [2.19.2](https://github.com/Azure/azure-functions-java-worker/releases/tag/2.19.2)
22-
- Make serialization case insensitive (#11123)
23-
- Add JitTrace Files for v4.1041
6+
- Handles loading extensions.json with empty extensions(#11174)

src/Directory.Version.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<Project>
22
<PropertyGroup>
3-
<VersionPrefix>4.1041.100</VersionPrefix>
3+
<VersionPrefix>4.1041.200</VersionPrefix>
44
<UpdateBuildNumber>true</UpdateBuildNumber>
55
</PropertyGroup>
66
</Project>

src/WebJobs.Script/DependencyInjection/ScriptStartupTypeLocator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ private async Task<ExtensionReference[]> ParseExtensionsAsync(string metadataFil
235235
await using var stream = File.OpenRead(metadataFilePath);
236236
var extensionReferences = await JsonSerializer.DeserializeAsync(stream, ExtensionReferencesJsonContext.Default.ExtensionReferences);
237237

238-
if (extensionReferences?.Extensions == null || extensionReferences.Extensions.Length == 0)
238+
if (extensionReferences?.Extensions == null)
239239
{
240240
_logger.ScriptStartUpUnableParseMetadataMissingProperty(metadataFilePath);
241241
return [];

test/WebJobs.Script.Tests/ScriptStartupTypeDiscovererTests.cs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ public async Task GetExtensionsStartupTypes_AcceptsRequiredBundleVersions(string
182182
Assert.True(traces.Any(m => m.FormattedMessage.Contains($"Loading extension bundle")));
183183
}
184184
Assert.True(traces.Any(m => m.FormattedMessage.Contains($"Loading startup extension 'Storage")));
185+
AssertNoErrors(traces);
185186
}
186187
}
187188

@@ -310,6 +311,7 @@ public async Task GetExtensionsStartupTypes_AcceptsRequiredExtensionVersions(str
310311
{
311312
Assert.True(traces.Any(m => m.FormattedMessage.Contains($"Loading startup extension 'Storage")));
312313
}
314+
AssertNoErrors(traces);
313315
}
314316
}
315317

@@ -423,6 +425,7 @@ void CopyToBin(string path)
423425
Assert.Equal(typeof(AzureStorageWebJobsStartup).FullName, types.Single().FullName);
424426
Assert.True(traces.Any(m => string.Equals(m.FormattedMessage, $"The extension startup type '{references[0].TypeName}' belongs to a builtin extension")));
425427
Assert.True(traces.Any(m => string.Equals(m.FormattedMessage, $"The extension startup type '{references[1].TypeName}' belongs to a builtin extension")));
428+
AssertNoErrors(traces);
426429
}
427430
}
428431

@@ -516,6 +519,7 @@ void CopyToBin(string path)
516519
Assert.Equal(typeof(AzureStorageWebJobsStartup).FullName, types.Single().FullName);
517520
Assert.True(traces.Any(m => string.Equals(m.FormattedMessage, $"The extension startup type '{references[0].TypeName}' belongs to a builtin extension")));
518521
Assert.True(traces.Any(m => string.Equals(m.FormattedMessage, $"The extension startup type '{references[1].TypeName}' belongs to a builtin extension")));
522+
AssertNoErrors(traces);
519523
}
520524
}
521525

@@ -592,11 +596,13 @@ void CopyToBin(string path)
592596

593597
// Act
594598
var types = await discoverer.GetExtensionsStartupTypesAsync();
599+
var traces = testLoggerProvider.GetAllLogMessages();
595600

596601
// Assert
597602
AreExpectedMetricsGenerated(testMetricsLogger);
598603
Assert.Equal(types.Count(), 2);
599604
Assert.Equal(typeof(AzureStorageWebJobsStartup).FullName, types.FirstOrDefault().FullName);
605+
AssertNoErrors(traces);
600606
}
601607
}
602608

@@ -644,11 +650,13 @@ void CopyToBin(string path)
644650

645651
// Act
646652
var types = await discoverer.GetExtensionsStartupTypesAsync();
653+
var traces = testLoggerProvider.GetAllLogMessages();
647654

648655
// Assert
649656
AreExpectedMetricsGenerated(testMetricsLogger);
650657
Assert.Single(types);
651658
Assert.Equal(typeof(AzureStorageWebJobsStartup).FullName, types.Single().FullName);
659+
AssertNoErrors(traces);
652660
}
653661
}
654662

@@ -698,11 +706,13 @@ void CopyToBin(string path)
698706

699707
// Act
700708
var types = await discoverer.GetExtensionsStartupTypesAsync();
709+
var traces = testLoggerProvider.GetAllLogMessages();
701710

702711
//Assert
703712
AreExpectedMetricsGenerated(testMetricsLogger);
704713
Assert.Single(types);
705714
Assert.Equal(typeof(AzureStorageWebJobsStartup).FullName, types.Single().FullName);
715+
AssertNoErrors(traces);
706716
}
707717
}
708718

@@ -1144,13 +1154,55 @@ void CopyToBin(string path)
11441154

11451155
// Act
11461156
var types = await discoverer.GetExtensionsStartupTypesAsync();
1157+
var traces = testLoggerProvider.GetAllLogMessages();
11471158
Environment.SetEnvironmentVariable(EnvironmentSettingNames.AzureWebJobsFeatureFlags, null);
11481159
Environment.SetEnvironmentVariable(EnvironmentSettingNames.FunctionWorkerRuntime, null);
11491160

11501161
//Assert that filtering did not take place because of worker indexing
11511162
Assert.True(types.Count() == 1);
11521163
Assert.Equal(typeof(AzureStorageWebJobsStartup).FullName, types.ElementAt(0).FullName);
1164+
AssertNoErrors(traces);
1165+
}
1166+
}
1167+
1168+
[Fact]
1169+
public async Task GetExtensionsStartupTypes_EmptyExtensionsArray()
1170+
{
1171+
TestMetricsLogger testMetricsLogger = new TestMetricsLogger();
1172+
1173+
using var directory = new TempDirectory();
1174+
var binPath = Path.Combine(directory.Path, "bin");
1175+
Directory.CreateDirectory(binPath);
1176+
1177+
// extensions.json file with an empty extensions array (simulating extensions.json produced by in-proc app)
1178+
string extensionJson = """
1179+
{
1180+
"extensions": []
11531181
}
1182+
""";
1183+
File.WriteAllText(Path.Combine(binPath, "extensions.json"), extensionJson);
1184+
1185+
TestLoggerProvider testLoggerProvider = new TestLoggerProvider();
1186+
LoggerFactory factory = new LoggerFactory();
1187+
factory.AddProvider(testLoggerProvider);
1188+
var testLogger = factory.CreateLogger<ScriptStartupTypeLocator>();
1189+
1190+
var mockExtensionBundleManager = new Mock<IExtensionBundleManager>();
1191+
mockExtensionBundleManager.Setup(e => e.IsExtensionBundleConfigured()).Returns(true);
1192+
mockExtensionBundleManager.Setup(e => e.GetExtensionBundleDetails()).Returns(Task.FromResult(new ExtensionBundleDetails() { Id = "bundleID", Version = "1.0.0" }));
1193+
mockExtensionBundleManager.Setup(e => e.GetExtensionBundleBinPathAsync()).Returns(Task.FromResult(binPath));
1194+
1195+
var languageWorkerOptions = new TestOptionsMonitor<LanguageWorkerOptions>(new LanguageWorkerOptions());
1196+
var mockFunctionMetadataManager = GetTestFunctionMetadataManager(languageWorkerOptions);
1197+
OptionsWrapper<ExtensionRequirementOptions> optionsWrapper = new(new ExtensionRequirementOptions());
1198+
var discoverer = new ScriptStartupTypeLocator(directory.Path, testLogger, mockExtensionBundleManager.Object, mockFunctionMetadataManager, testMetricsLogger, optionsWrapper);
1199+
1200+
var types = await discoverer.GetExtensionsStartupTypesAsync();
1201+
var traces = testLoggerProvider.GetAllLogMessages();
1202+
1203+
AreExpectedMetricsGenerated(testMetricsLogger);
1204+
Assert.Empty(types); // Ensure no types are loaded because the extensions array is empty
1205+
AssertNoErrors(traces);
11541206
}
11551207

11561208
private IFunctionMetadataManager GetTestFunctionMetadataManager(IOptionsMonitor<LanguageWorkerOptions> options, ICollection<FunctionMetadata> metadataCollection = null, bool hasPrecompiledFunction = false, bool hasNodeFunctions = false, bool hasDotnetIsolatedFunctions = false)
@@ -1234,5 +1286,10 @@ private ILogger<ScriptStartupTypeLocator> GetTestLogger()
12341286
var testLogger = factory.CreateLogger<ScriptStartupTypeLocator>();
12351287
return testLogger;
12361288
}
1289+
1290+
private static void AssertNoErrors(IList<LogMessage> traces)
1291+
{
1292+
Assert.False(traces.Any(m => m.Level == LogLevel.Error || m.Level == LogLevel.Critical));
1293+
}
12371294
}
12381295
}

0 commit comments

Comments
 (0)