Skip to content

Commit 2d6b315

Browse files
[v3x] Include minimum durable payload to sync trigger (#8292)
* Include minimum durable payload to synctrigger * update release note * added missed change on v4
1 parent eb93c2a commit 2d6b315

File tree

8 files changed

+292
-14
lines changed

8 files changed

+292
-14
lines changed

release_notes.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
-->
55
- Read FuncitonsHostingConfigurations to enable worker concurrency feature (#8125)
66
- Updated Java Worker Version to [1.11.0](https://github.com/Azure/azure-functions-java-worker/releases/tag/1.11.0)
7+
- Add Include minimum durable payload to sync trigger (#8292)
78

89
**Release sprint:** Sprint 118
910
[ [bugs](https://github.com/Azure/azure-functions-host/issues?q=is%3Aissue+milestone%3A%22Functions+Sprint+118%22+label%3Abug+is%3Aclosed) | [features](https://github.com/Azure/azure-functions-host/issues?q=is%3Aissue+milestone%3A%22Functions+Sprint+118%22+label%3Afeature+is%3Aclosed) ]

src/WebJobs.Script.WebHost/Management/FunctionsSyncManager.cs

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ public class FunctionsSyncManager : IFunctionsSyncManager, IDisposable
3434
private const string DurableTaskV1StorageConnectionName = "azureStorageConnectionStringName";
3535
private const string DurableTaskV2StorageOptions = "storageProvider";
3636
private const string DurableTaskV2StorageConnectionName = "connectionStringName";
37+
private const string DurableTaskV2MaxConcurrentActivityFunctions = "maxConcurrentActivityFunctions";
38+
private const string DurableTaskV2MaxConcurrentOrchestratorFunctions = "maxConcurrentOrchestratorFunctions";
3739
private const string DurableTask = "durableTask";
3840

3941
// 45 alphanumeric characters gives us a buffer in our table/queue/blob container names.
@@ -483,6 +485,21 @@ private static JObject UpdateDurableFunctionConfig(JObject trigger, DurableConfi
483485
{
484486
trigger[Connection] = durableTaskConfig.Connection;
485487
}
488+
489+
if (durableTaskConfig.StorageProvider != null)
490+
{
491+
trigger[DurableTaskV2StorageOptions] = durableTaskConfig.StorageProvider;
492+
}
493+
494+
if (durableTaskConfig.MaxConcurrentOrchestratorFunctions != 0)
495+
{
496+
trigger[DurableTaskV2MaxConcurrentOrchestratorFunctions] = durableTaskConfig.MaxConcurrentOrchestratorFunctions;
497+
}
498+
499+
if (durableTaskConfig.MaxConcurrentActivityFunctions != 0)
500+
{
501+
trigger[DurableTaskV2MaxConcurrentActivityFunctions] = durableTaskConfig.MaxConcurrentActivityFunctions;
502+
}
486503
}
487504
return trigger;
488505
}
@@ -597,6 +614,18 @@ private DurableConfig GetDurableV2Config(JObject durableHostConfig)
597614
{
598615
config.Connection = nameValue.ToString();
599616
}
617+
618+
config.StorageProvider = storageOptions;
619+
}
620+
621+
if (durableHostConfig.TryGetValue(DurableTaskV2MaxConcurrentOrchestratorFunctions, StringComparison.OrdinalIgnoreCase, out JToken maxConcurrentOrchestratorFunctions) && maxConcurrentOrchestratorFunctions != null)
622+
{
623+
config.MaxConcurrentOrchestratorFunctions = int.Parse(maxConcurrentOrchestratorFunctions.ToString());
624+
}
625+
626+
if (durableHostConfig.TryGetValue(DurableTaskV2MaxConcurrentActivityFunctions, StringComparison.OrdinalIgnoreCase, out JToken maxConcurrentActivityFunctions) && maxConcurrentActivityFunctions != null)
627+
{
628+
config.MaxConcurrentActivityFunctions = int.Parse(maxConcurrentActivityFunctions.ToString());
600629
}
601630
}
602631

@@ -723,9 +752,15 @@ private class DurableConfig
723752

724753
public string Connection { get; set; }
725754

755+
public JToken StorageProvider { get; set; }
756+
757+
public int MaxConcurrentActivityFunctions { get; set; }
758+
759+
public int MaxConcurrentOrchestratorFunctions { get; set; }
760+
726761
public bool HasValues()
727762
{
728-
return this.HubName != null || this.Connection != null;
763+
return this.HubName != null || this.Connection != null || this.StorageProvider != null || this.MaxConcurrentOrchestratorFunctions != 0 || this.MaxConcurrentOrchestratorFunctions != 0;
729764
}
730765
}
731766
}

test/WebJobs.Script.Tests.Integration/Management/FunctionsSyncManagerTests.cs

Lines changed: 100 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -143,12 +143,13 @@ public FunctionsSyncManagerTests()
143143
_functionsSyncManager = new FunctionsSyncManager(configuration, hostIdProviderMock.Object, optionsMonitor, loggerFactory.CreateLogger<FunctionsSyncManager>(), httpClient, _secretManagerProviderMock.Object, _mockWebHostEnvironment.Object, _mockEnvironment.Object, _hostNameProvider, functionMetadataManager, azureStorageProvider);
144144
}
145145

146-
private string GetExpectedSyncTriggersPayload(string postedConnection = DefaultTestConnection, string postedTaskHub = DefaultTestTaskHub)
146+
private string GetExpectedSyncTriggersPayload(string postedConnection = DefaultTestConnection, string postedTaskHub = DefaultTestTaskHub, string durableVersion = "V2")
147147
{
148148
string taskHubSegment = postedTaskHub != null ? $",\"taskHubName\":\"{postedTaskHub}\"" : "";
149+
string storageProviderSegment = postedConnection != null && durableVersion == "V2" ? $",\"storageProvider\":{{\"connectionStringName\":\"DurableConnection\"}}" : "";
149150
return "[{\"authLevel\":\"anonymous\",\"type\":\"httpTrigger\",\"direction\":\"in\",\"name\":\"req\",\"functionName\":\"function1\"}," +
150-
$"{{\"name\":\"myQueueItem\",\"type\":\"orchestrationTrigger\",\"direction\":\"in\",\"queueName\":\"myqueue-items\",\"connection\":\"{postedConnection}\",\"functionName\":\"function2\"{taskHubSegment}}}," +
151-
$"{{\"name\":\"myQueueItem\",\"type\":\"activityTrigger\",\"direction\":\"in\",\"queueName\":\"myqueue-items\",\"connection\":\"{postedConnection}\",\"functionName\":\"function3\"{taskHubSegment}}}]";
151+
$"{{\"name\":\"myQueueItem\",\"type\":\"orchestrationTrigger\",\"direction\":\"in\",\"queueName\":\"myqueue-items\",\"connection\":\"{postedConnection}\",\"functionName\":\"function2\"{taskHubSegment}{storageProviderSegment}}}," +
152+
$"{{\"name\":\"myQueueItem\",\"type\":\"activityTrigger\",\"direction\":\"in\",\"queueName\":\"myqueue-items\",\"connection\":\"{postedConnection}\",\"functionName\":\"function3\"{taskHubSegment}{storageProviderSegment}}}]";
152153
}
153154

154155
private void ResetMockFileSystem(string hostJsonContent = null, string extensionsJsonContent = null)
@@ -235,18 +236,18 @@ public async Task TrySyncTriggers_PostsExpectedContent(bool cacheEnabled)
235236

236237
if (cacheEnabled)
237238
{
238-
VerifyResultWithCacheOn();
239+
VerifyResultWithCacheOn(durableVersion: "V1");
239240
}
240241
else
241242
{
242-
VerifyResultWithCacheOff();
243+
VerifyResultWithCacheOff(durableVersion: "V1");
243244
}
244245
}
245246
}
246247

247-
private void VerifyResultWithCacheOn(string connection = DefaultTestConnection, string expectedTaskHub = "TestHubValue")
248+
private void VerifyResultWithCacheOn(string connection = DefaultTestConnection, string expectedTaskHub = "TestHubValue", string durableVersion = "V2")
248249
{
249-
string expectedSyncTriggersPayload = GetExpectedSyncTriggersPayload(postedConnection: connection, postedTaskHub: expectedTaskHub);
250+
string expectedSyncTriggersPayload = GetExpectedSyncTriggersPayload(postedConnection: connection, postedTaskHub: expectedTaskHub, durableVersion);
250251
// verify triggers
251252
var result = JObject.Parse(_contentBuilder.ToString());
252253
var triggers = result["triggers"];
@@ -286,9 +287,9 @@ private void VerifyResultWithCacheOn(string connection = DefaultTestConnection,
286287
Assert.False(triggersLog.Contains("secrets"));
287288
}
288289

289-
private void VerifyResultWithCacheOff()
290+
private void VerifyResultWithCacheOff(string durableVersion)
290291
{
291-
string expectedSyncTriggersPayload = GetExpectedSyncTriggersPayload();
292+
string expectedSyncTriggersPayload = GetExpectedSyncTriggersPayload(durableVersion: durableVersion);
292293
var triggers = JArray.Parse(_contentBuilder.ToString());
293294
Assert.Equal(expectedSyncTriggersPayload, triggers.ToString(Formatting.None));
294295

@@ -363,7 +364,7 @@ public async Task TrySyncTriggers_BackgroundSync_PostsExpectedContent()
363364
Assert.Equal(1, _mockHttpHandler.RequestCount);
364365
var result = JObject.Parse(_contentBuilder.ToString());
365366
var triggers = result["triggers"];
366-
Assert.Equal(GetExpectedSyncTriggersPayload(), triggers.ToString(Formatting.None));
367+
Assert.Equal(GetExpectedSyncTriggersPayload(durableVersion: "V1"), triggers.ToString(Formatting.None));
367368

368369
string hash = string.Empty;
369370
var downloadResponse = await hashBlob.DownloadAsync();
@@ -429,7 +430,7 @@ public async Task TrySyncTriggers_BackgroundSync_SetTriggersFailure_HashNotUpdat
429430
Assert.Equal(1, _mockHttpHandler.RequestCount);
430431
var result = JObject.Parse(_contentBuilder.ToString());
431432
var triggers = result["triggers"];
432-
Assert.Equal(GetExpectedSyncTriggersPayload(), triggers.ToString(Formatting.None));
433+
Assert.Equal(GetExpectedSyncTriggersPayload(durableVersion: "V1"), triggers.ToString(Formatting.None));
433434
bool hashBlobExists = await hashBlob.ExistsAsync();
434435
Assert.False(hashBlobExists);
435436

@@ -488,7 +489,7 @@ public async Task TrySyncTriggers_NoDurableTaskHub_DurableV1ExtensionJson_V1Defa
488489
Assert.True(syncResult.Success, "SyncTriggers should return success true");
489490
Assert.True(string.IsNullOrEmpty(syncResult.Error), "Error should be null or empty");
490491

491-
VerifyResultWithCacheOn(expectedTaskHub: null, connection: null);
492+
VerifyResultWithCacheOn(expectedTaskHub: null, connection: null, durableVersion: "V1");
492493
}
493494
}
494495

@@ -567,7 +568,7 @@ public async Task TrySyncTriggers_DurableV1ExtensionJson_V1ConfigPosted()
567568
Assert.True(syncResult.Success, "SyncTriggers should return success true");
568569
Assert.True(string.IsNullOrEmpty(syncResult.Error), "Error should be null or empty");
569570

570-
VerifyResultWithCacheOn(expectedTaskHub: "DurableTask", connection: "DurableConnection");
571+
VerifyResultWithCacheOn(expectedTaskHub: "DurableTask", connection: "DurableConnection", durableVersion: "V1");
571572
}
572573
}
573574

@@ -602,6 +603,92 @@ public async Task TrySyncTriggers_DurableV2ExtensionJson_V2ConfigPosted()
602603
}
603604
}
604605

606+
[Theory]
607+
[InlineData("DurableMsSQLProviderPayload", "DurableMsSQLProviderPayload")]
608+
[InlineData("DurableAdditionalPayload", "DurableMsSQLProviderPayload")] // Payload trimed to the minimum payload
609+
[InlineData("DurableHasHubNameAndOrchestrationConfig", "DurableHasHubNameAndOrchestrationConfigPayload")]
610+
[InlineData("DurableHasStorageAndActivityConfig", "DurableHasStorageAndActivityConfigPayload")]
611+
[InlineData("Empty", "EmptyDurablePayload")]
612+
public async Task TrySyncTriggers_DurableV2ExtensionJson_StorageProviderPosted(string scenarioName, string expectedPayload)
613+
{
614+
_mockEnvironment.Setup(p => p.GetEnvironmentVariable(EnvironmentSettingNames.AzureWebsiteName)).Returns("TestHubValue");
615+
using (var env = new TestScopedEnvironmentVariable(_vars))
616+
{
617+
var durableConfig = GetDurableConfig(scenarioName);
618+
619+
var hostConfig = GetHostConfig(durableConfig, useBundles: false);
620+
621+
// See what happens when extension.json is not present but bundles are used.
622+
SetupDurableExtension(hostConfig.ToString(), durableExtensionJsonVersion: "2.0.0.0");
623+
624+
// Act
625+
var syncResult = await _functionsSyncManager.TrySyncTriggersAsync();
626+
627+
// Assert
628+
Assert.True(syncResult.Success, "SyncTriggers should return success true");
629+
Assert.True(string.IsNullOrEmpty(syncResult.Error), "Error should be null or empty");
630+
631+
//Verify Result
632+
var result = JObject.Parse(_contentBuilder.ToString());
633+
var triggers = result["triggers"];
634+
var triggersString = triggers.ToString();
635+
Assert.Equal(GetPayloadFromFile($"{expectedPayload}.json"), triggers.ToString());
636+
}
637+
}
638+
639+
private JObject GetDurableConfig(string scenarioName)
640+
{
641+
var durableConfig = new JObject();
642+
durableConfig["hubName"] = "DurableTask";
643+
644+
var azureStorageConfig = new JObject();
645+
azureStorageConfig["type"] = "mssql";
646+
azureStorageConfig["connectionStringName"] = "SQLDB_Connection";
647+
azureStorageConfig["taskEventLockTimeout"] = "00:02:00";
648+
azureStorageConfig["createDatabaseIfNotExists"] = true;
649+
durableConfig["storageProvider"] = azureStorageConfig;
650+
durableConfig["maxConcurrentActivityFunctions"] = 12;
651+
durableConfig["maxConcurrentOrchestratorFunctions"] = 10;
652+
653+
switch (scenarioName)
654+
{
655+
case "DurableMsSQLProviderPayload":
656+
return durableConfig;
657+
case "DurableAdditionalPayload":
658+
// These additional parameters are exptected to be ignored.
659+
durableConfig["extendedSessionsEnabled"] = true;
660+
durableConfig["extendedSessionIdleTimeoutInSeconds"] = 30;
661+
return durableConfig;
662+
case "DurableHasHubNameAndOrchestrationConfig":
663+
durableConfig = new JObject();
664+
durableConfig["hubName"] = "DurableTask";
665+
durableConfig["maxConcurrentOrchestratorFunctions"] = 10;
666+
return durableConfig;
667+
case "DurableHasStorageAndActivityConfig":
668+
durableConfig = new JObject();
669+
azureStorageConfig = new JObject();
670+
azureStorageConfig["type"] = "mssql";
671+
azureStorageConfig["connectionStringName"] = "SQLDB_Connection";
672+
azureStorageConfig["taskEventLockTimeout"] = "00:02:00";
673+
azureStorageConfig["createDatabaseIfNotExists"] = true;
674+
durableConfig["storageProvider"] = azureStorageConfig;
675+
durableConfig["maxConcurrentActivityFunctions"] = 12;
676+
return durableConfig;
677+
case "Empty":
678+
return new JObject();
679+
default: return new JObject();
680+
}
681+
682+
}
683+
684+
685+
private string GetPayloadFromFile(string fileName)
686+
{
687+
var fullPath = Path.Combine("Management", "Payload", fileName);
688+
return File.ReadAllText(fullPath);
689+
}
690+
691+
605692
[Fact]
606693
public async Task UpdateHashAsync_Succeeds()
607694
{
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
[
2+
{
3+
"authLevel": "anonymous",
4+
"type": "httpTrigger",
5+
"direction": "in",
6+
"name": "req",
7+
"functionName": "function1"
8+
},
9+
{
10+
"name": "myQueueItem",
11+
"type": "orchestrationTrigger",
12+
"direction": "in",
13+
"queueName": "myqueue-items",
14+
"connection": "",
15+
"functionName": "function2",
16+
"taskHubName": "DurableTask",
17+
"maxConcurrentOrchestratorFunctions": 10
18+
},
19+
{
20+
"name": "myQueueItem",
21+
"type": "activityTrigger",
22+
"direction": "in",
23+
"queueName": "myqueue-items",
24+
"connection": "",
25+
"functionName": "function3",
26+
"taskHubName": "DurableTask",
27+
"maxConcurrentOrchestratorFunctions": 10
28+
}
29+
]
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
[
2+
{
3+
"authLevel": "anonymous",
4+
"type": "httpTrigger",
5+
"direction": "in",
6+
"name": "req",
7+
"functionName": "function1"
8+
},
9+
{
10+
"name": "myQueueItem",
11+
"type": "orchestrationTrigger",
12+
"direction": "in",
13+
"queueName": "myqueue-items",
14+
"connection": "SQLDB_Connection",
15+
"functionName": "function2",
16+
"taskHubName": "TestHubValue",
17+
"storageProvider": {
18+
"type": "mssql",
19+
"connectionStringName": "SQLDB_Connection",
20+
"taskEventLockTimeout": "00:02:00",
21+
"createDatabaseIfNotExists": true
22+
},
23+
"maxConcurrentActivityFunctions": 12
24+
},
25+
{
26+
"name": "myQueueItem",
27+
"type": "activityTrigger",
28+
"direction": "in",
29+
"queueName": "myqueue-items",
30+
"connection": "SQLDB_Connection",
31+
"functionName": "function3",
32+
"taskHubName": "TestHubValue",
33+
"storageProvider": {
34+
"type": "mssql",
35+
"connectionStringName": "SQLDB_Connection",
36+
"taskEventLockTimeout": "00:02:00",
37+
"createDatabaseIfNotExists": true
38+
},
39+
"maxConcurrentActivityFunctions": 12
40+
}
41+
]
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
[
2+
{
3+
"authLevel": "anonymous",
4+
"type": "httpTrigger",
5+
"direction": "in",
6+
"name": "req",
7+
"functionName": "function1"
8+
},
9+
{
10+
"name": "myQueueItem",
11+
"type": "orchestrationTrigger",
12+
"direction": "in",
13+
"queueName": "myqueue-items",
14+
"connection": "SQLDB_Connection",
15+
"functionName": "function2",
16+
"taskHubName": "DurableTask",
17+
"storageProvider": {
18+
"type": "mssql",
19+
"connectionStringName": "SQLDB_Connection",
20+
"taskEventLockTimeout": "00:02:00",
21+
"createDatabaseIfNotExists": true
22+
},
23+
"maxConcurrentOrchestratorFunctions": 10,
24+
"maxConcurrentActivityFunctions": 12
25+
},
26+
{
27+
"name": "myQueueItem",
28+
"type": "activityTrigger",
29+
"direction": "in",
30+
"queueName": "myqueue-items",
31+
"connection": "SQLDB_Connection",
32+
"functionName": "function3",
33+
"taskHubName": "DurableTask",
34+
"storageProvider": {
35+
"type": "mssql",
36+
"connectionStringName": "SQLDB_Connection",
37+
"taskEventLockTimeout": "00:02:00",
38+
"createDatabaseIfNotExists": true
39+
},
40+
"maxConcurrentOrchestratorFunctions": 10,
41+
"maxConcurrentActivityFunctions": 12
42+
}
43+
]

0 commit comments

Comments
 (0)