Skip to content

Commit 23ebd15

Browse files
authored
Increase default grpc max message size (#2651)
* Increase default grpc max message size to 32MB for dynamic sku, 128MB for dedicated
1 parent 6e4b2eb commit 23ebd15

File tree

14 files changed

+435
-267
lines changed

14 files changed

+435
-267
lines changed

schemas/json/host.json

Lines changed: 253 additions & 239 deletions
Large diffs are not rendered by default.

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

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the MIT License. See License.txt in the project root for license information.
33

44
using System;
5+
using System.Collections.Generic;
56
using System.Linq;
67
using System.Threading.Tasks;
78
using Grpc.Core;
@@ -15,9 +16,13 @@ public class GrpcServer : IRpcServer, IDisposable
1516
private Server _server;
1617
private bool _disposed = false;
1718

18-
public GrpcServer(FunctionRpc.FunctionRpcBase serviceImpl)
19+
public GrpcServer(FunctionRpc.FunctionRpcBase serviceImpl, int grpcMaxMessageLength)
1920
{
20-
_server = new Server
21+
ChannelOption maxReceiveMessageLength = new ChannelOption(ChannelOptions.MaxReceiveMessageLength, grpcMaxMessageLength);
22+
ChannelOption maxSendMessageLength = new ChannelOption(ChannelOptions.MaxSendMessageLength, grpcMaxMessageLength);
23+
ChannelOption[] grpcChannelOptions = { maxReceiveMessageLength, maxSendMessageLength };
24+
25+
_server = new Server(grpcChannelOptions)
2126
{
2227
Services = { FunctionRpc.BindService(serviceImpl) },
2328
Ports = { new ServerPort("127.0.0.1", ServerPort.PickUnused, ServerCredentials.Insecure) }

src/WebJobs.Script/Config/ScriptHostConfiguration.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,11 @@ public ScriptHostConfiguration()
8686
/// </summary>
8787
public TimeSpan? FunctionTimeout { get; set; }
8888

89+
/// <summary>
90+
/// Gets or sets a value for grpc_max_message_length.
91+
/// </summary>
92+
public int MaxMessageLengthBytes { get; set; }
93+
8994
/// <summary>
9095
/// Gets or sets a value indicating whether the host is running
9196
/// outside of the normal Azure hosting environment. E.g. when running

src/WebJobs.Script/Host/ScriptHost.cs

Lines changed: 52 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ public class ScriptHost : JobHost
7272
private static readonly Regex FunctionNameValidationRegex = new Regex(@"^[a-z][a-z0-9_\-]{0,127}$(?<!^host$)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
7373
private static readonly Regex ProxyNameValidationRegex = new Regex(@"[^a-zA-Z0-9_-]", RegexOptions.Compiled | RegexOptions.IgnoreCase);
7474
public static readonly string Version = GetAssemblyFileVersion(typeof(ScriptHost).Assembly);
75+
internal static readonly int DefaultMaxMessageLengthBytesDynamicSku = 32 * 1024 * 1024;
76+
internal static readonly int DefaultMaxMessageLengthBytes = 128 * 1024 * 1024;
7577
private ScriptSettingsManager _settingsManager;
7678
private bool _shutdownScheduled;
7779
private ILogger _startupLogger;
@@ -498,16 +500,22 @@ internal void InitializeFunctionDescriptors(Collection<FunctionMetadata> functio
498500
_descriptorProviders = new List<FunctionDescriptorProvider>();
499501
if (string.IsNullOrEmpty(language))
500502
{
503+
_startupLogger.LogTrace("Adding all the Function descriptors.");
501504
_descriptorProviders.Add(new DotNetFunctionDescriptorProvider(this, ScriptConfig));
502505
_descriptorProviders.Add(new WorkerFunctionDescriptorProvider(this, ScriptConfig, _functionDispatcher));
503506
}
504-
else if (language.Equals(ScriptConstants.DotNetLanguageWorkerName, StringComparison.OrdinalIgnoreCase))
505-
{
506-
_descriptorProviders.Add(new DotNetFunctionDescriptorProvider(this, ScriptConfig));
507-
}
508507
else
509508
{
510-
_descriptorProviders.Add(new WorkerFunctionDescriptorProvider(this, ScriptConfig, _functionDispatcher));
509+
_startupLogger.LogTrace($"Adding Function descriptor for language {language}.");
510+
switch (language.ToLower())
511+
{
512+
case ScriptConstants.DotNetLanguageWorkerName:
513+
_descriptorProviders.Add(new DotNetFunctionDescriptorProvider(this, ScriptConfig));
514+
break;
515+
default:
516+
_descriptorProviders.Add(new WorkerFunctionDescriptorProvider(this, ScriptConfig, _functionDispatcher));
517+
break;
518+
}
511519
}
512520

513521
Collection<FunctionDescriptor> functions;
@@ -645,7 +653,7 @@ private JObject ApplyHostConfiguration()
645653
string sanitizedJson = SanitizeHostJson(hostConfigObject);
646654
string readFileMessage = $"Host configuration file read:{Environment.NewLine}{sanitizedJson}";
647655

648-
ApplyConfiguration(hostConfigObject, ScriptConfig);
656+
ApplyConfiguration(hostConfigObject, ScriptConfig, _startupLogger);
649657

650658
if (_settingsManager.FileSystemIsReadOnly)
651659
{
@@ -698,7 +706,7 @@ private JObject ApplyHostConfiguration()
698706
private void InitializeWorkers(string language)
699707
{
700708
var serverImpl = new FunctionRpcService(EventManager);
701-
var server = new GrpcServer(serverImpl);
709+
var server = new GrpcServer(serverImpl, ScriptConfig.MaxMessageLengthBytes);
702710

703711
// TODO: async initialization of script host - hook into startasync method?
704712
server.StartAsync().GetAwaiter().GetResult();
@@ -733,7 +741,7 @@ private void InitializeWorkers(string language)
733741
// TODO: We still have some hard coded languages, so we need to handle them. Remove this switch once we've moved away from that.
734742
switch (language.ToLower())
735743
{
736-
case ScriptConstants.NodeLanguageWrokerName:
744+
case ScriptConstants.NodeLanguageWorkerName:
737745
providers.Add(new NodeWorkerProvider());
738746
break;
739747
case ScriptConstants.JavaLanguageWrokerName:
@@ -1405,11 +1413,6 @@ internal Collection<FunctionDescriptor> GetFunctionDescriptors(IEnumerable<Funct
14051413
ValidateFunction(descriptor, httpFunctions);
14061414
functionDescriptors.Add(descriptor);
14071415
}
1408-
else
1409-
{
1410-
string functionLanguage = _settingsManager.Configuration[ScriptConstants.FunctionWorkerRuntimeSettingName];
1411-
throw new ArgumentException($"Could not find a valid provider. {ScriptConstants.FunctionWorkerRuntimeSettingName} Appsetting is set to {functionLanguage}. Check that you have the correct language provider enabled and installed");
1412-
}
14131416
}
14141417
catch (Exception ex)
14151418
{
@@ -1491,7 +1494,7 @@ internal static bool HttpRoutesConflict(HttpTriggerAttribute httpTrigger, HttpTr
14911494
return httpTrigger.Methods.Intersect(otherHttpTrigger.Methods).Any();
14921495
}
14931496

1494-
internal static void ApplyConfiguration(JObject config, ScriptHostConfiguration scriptConfig)
1497+
internal static void ApplyConfiguration(JObject config, ScriptHostConfiguration scriptConfig, ILogger logger = null)
14951498
{
14961499
var hostConfig = scriptConfig.HostConfig;
14971500

@@ -1600,6 +1603,7 @@ internal static void ApplyConfiguration(JObject config, ScriptHostConfiguration
16001603
}
16011604
}
16021605

1606+
value = null;
16031607
if (config.TryGetValue("functionTimeout", out value))
16041608
{
16051609
TimeSpan requestedTimeout = TimeSpan.Parse((string)value, CultureInfo.InvariantCulture);
@@ -1620,10 +1624,44 @@ internal static void ApplyConfiguration(JObject config, ScriptHostConfiguration
16201624
}
16211625
scriptConfig.HostConfig.FunctionTimeout = ScriptHost.CreateTimeoutConfiguration(scriptConfig);
16221626

1627+
ApplyLanguageWorkerConfig(config, scriptConfig, logger);
16231628
ApplyLoggerConfig(config, scriptConfig);
16241629
ApplyApplicationInsightsConfig(config, scriptConfig);
16251630
}
16261631

1632+
private static void ApplyLanguageWorkerConfig(JObject config, ScriptHostConfiguration scriptConfig, ILogger logger)
1633+
{
1634+
JToken value = null;
1635+
JObject languageWorkerSection = (JObject)config["languageWorker"];
1636+
int requestedGrpcMaxMessageLength = ScriptSettingsManager.Instance.IsDynamicSku ? DefaultMaxMessageLengthBytesDynamicSku : DefaultMaxMessageLengthBytes;
1637+
if (languageWorkerSection != null)
1638+
{
1639+
if (languageWorkerSection.TryGetValue("maxMessageLength", out value))
1640+
{
1641+
int valueInBytes = int.Parse((string)value) * 1024 * 1024;
1642+
if (ScriptSettingsManager.Instance.IsDynamicSku)
1643+
{
1644+
string message = $"Cannot set {nameof(scriptConfig.MaxMessageLengthBytes)} on Consumption plan. Default MaxMessageLength: {DefaultMaxMessageLengthBytesDynamicSku} will be used";
1645+
logger?.LogWarning(message);
1646+
}
1647+
else
1648+
{
1649+
if (valueInBytes < 0 || valueInBytes > 2000 * 1024 * 1024)
1650+
{
1651+
// Current grpc max message limits
1652+
string message = $"MaxMessageLength must be between 4MB and 2000MB.Default MaxMessageLength: {DefaultMaxMessageLengthBytes} will be used";
1653+
logger?.LogWarning(message);
1654+
}
1655+
else
1656+
{
1657+
requestedGrpcMaxMessageLength = valueInBytes;
1658+
}
1659+
}
1660+
}
1661+
}
1662+
scriptConfig.MaxMessageLengthBytes = requestedGrpcMaxMessageLength;
1663+
}
1664+
16271665
internal static void ApplyLoggerConfig(JObject configJson, ScriptHostConfiguration scriptConfig)
16281666
{
16291667
scriptConfig.LogFilter = new LogCategoryFilter();

src/WebJobs.Script/Rpc/Configuration/GenericWorkerProvider.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public static List<IWorkerProvider> ReadWorkerProviderFromConfig(ScriptHostConfi
4848

4949
if (!string.IsNullOrEmpty(language))
5050
{
51-
logger.LogInformation($"Reading Worker config for the lanuage: {language}");
51+
logger.LogInformation($"Reading Worker config for the language: {language}");
5252
string languageWorkerDirectory = Path.Combine(workerDirPath, language);
5353
var provider = GetProviderFromConfig(languageWorkerDirectory, logger);
5454
if (provider != null)

src/WebJobs.Script/Rpc/Configuration/NodeWorkerProvider.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@ namespace Microsoft.Azure.WebJobs.Script.Rpc
1414
{
1515
internal class NodeWorkerProvider : IWorkerProvider
1616
{
17-
private string pathToWorkerDir = WorkerProviderHelper.BuildWorkerDirectoryPath(ScriptConstants.NodeLanguageWrokerName);
17+
private string pathToWorkerDir = WorkerProviderHelper.BuildWorkerDirectoryPath(ScriptConstants.NodeLanguageWorkerName);
1818

1919
public WorkerDescription GetDescription() => new WorkerDescription
2020
{
21-
Language = ScriptConstants.NodeLanguageWrokerName,
21+
Language = ScriptConstants.NodeLanguageWorkerName,
2222
Extension = ".js",
2323
DefaultExecutablePath = "node",
2424
DefaultWorkerPath = Path.Combine("dist", "src", "nodejsWorker.js"),

src/WebJobs.Script/Rpc/LanguageWorkerChannel.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ internal void StartWorker()
122122
var workerContext = new WorkerCreateContext()
123123
{
124124
RequestId = Guid.NewGuid().ToString(),
125+
MaxMessageLength = _scriptConfig.MaxMessageLengthBytes,
125126
WorkerId = _workerId,
126127
Arguments = _workerConfig.Arguments,
127128
WorkingDirectory = _scriptConfig.RootScriptPath,

src/WebJobs.Script/Rpc/ProcessManagement/DefaultWorkerProcessFactory.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ public string GetArguments(WorkerCreateContext context)
3232
var argumentsBuilder = context.Arguments.ExecutableArguments.Aggregate(new StringBuilder(), MergeArguments);
3333
argumentsBuilder.AppendFormat(" \"{0}\"", context.Arguments.WorkerPath);
3434
context.Arguments.WorkerArguments.Aggregate(argumentsBuilder, MergeArguments);
35-
argumentsBuilder.AppendFormat(" --host {0} --port {1} --workerId {2} --requestId {3}",
36-
context.ServerUri.Host, context.ServerUri.Port, context.WorkerId, context.RequestId);
35+
argumentsBuilder.AppendFormat(" --host {0} --port {1} --workerId {2} --requestId {3} --grpcMaxMessageLength {4}",
36+
context.ServerUri.Host, context.ServerUri.Port, context.WorkerId, context.RequestId, context.MaxMessageLength);
3737
return argumentsBuilder.ToString();
3838
}
3939
}

src/WebJobs.Script/Rpc/ProcessManagement/WorkerCreateContext.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,7 @@ internal class WorkerCreateContext
1818
public string RequestId { get; set; }
1919

2020
public string WorkingDirectory { get; set; }
21+
22+
public int MaxMessageLength { get; set; }
2123
}
2224
}

src/WebJobs.Script/ScriptConstants.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ public static class ScriptConstants
7070
public const string ColdStartEventName = "ColdStart";
7171
public const string FunctionWorkerRuntimeSettingName = "FUNCTIONS_WORKER_RUNTIME";
7272
public const string DotNetLanguageWorkerName = "dotnet";
73-
public const string NodeLanguageWrokerName = "node";
73+
public const string NodeLanguageWorkerName = "node";
7474
public const string JavaLanguageWrokerName = "java";
7575
public const string DefaultWorkersDirectoryName = "workers";
7676

0 commit comments

Comments
 (0)