Skip to content

Commit dd8e853

Browse files
Fallback to Host ID if WEBSITE_SITE_NAME isn't defined (#1178)
* Fall back to using Host ID if WEBSITE_SITE_NAME doesn't exist * cleanup * Pass in env vars to startup * verify test fails * try * remove default site name settings * Update comment * update docs
1 parent 7e44445 commit dd8e853

17 files changed

+83
-72
lines changed

docs/BindingsOverview.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
- [Sql\_Trigger\_MaxBatchSize](#sql_trigger_maxbatchsize)
2121
- [Sql\_Trigger\_PollingIntervalMs](#sql_trigger_pollingintervalms)
2222
- [Sql\_Trigger\_MaxChangesPerWorker](#sql_trigger_maxchangesperworker)
23+
- [WEBSITE\_SITE\_NAME](#website_site_name)
2324
- [Scaling for Trigger Bindings](#scaling-for-trigger-bindings)
2425
- [Retry support for Trigger Bindings](#retry-support-for-trigger-bindings)
2526
- [Startup retries](#startup-retries)
@@ -154,11 +155,10 @@ The upper limit on the number of pending changes in the user table that are allo
154155

155156
#### WEBSITE_SITE_NAME
156157

157-
The unique name used in creating the lease tables. The local apps depend on this setting for creating unique leases tables, please give a unique name for each app.
158+
If this setting exists, it will be used to generate a unique identifier for the function that is used for tracking function state. If not specified, this unique identifier will be generated from the [IHostIdProvider.GetHostIdAsync](https://github.com/Azure/azure-webjobs-sdk/blob/dev/src/Microsoft.Azure.WebJobs.Host/Executors/IHostIdProvider.cs#L14).
158159

159160
> **NOTE:**
160161
> * If the setting is re-used across apps, having the same function name could cause the functions to use the same lease tables and the function runs to not work as expected.
161-
> * If you have 2 different SQL trigger functions with same functionName locally, not having WEBSITE_SITE_NAME would mean that the same leasees table would be used for both triggers resulting in only one of the functions being triggered.
162162
> * This is a read-only variable that is provided by the Azure App service for deployed functions and the user provided value will be overridden. Refer to [Environment variables](https://learn.microsoft.com/azure/app-service/reference-app-settings?tabs=kudu%2Cdotnet#app-environment) for apps.
163163

164164
### Scaling for Trigger Bindings

docs/TriggerBinding.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -96,11 +96,12 @@ To find the name of the leases table associated with your function, look in the
9696

9797
This log message is at the `Information` level, so make sure your log level is set correctly.
9898

99-
NOTE: `FunctionId` is generated from a couple of inputs:
100-
- The [WEBSITE_SITE_NAME](https://github.com/Azure/azure-functions-sql-extension/blob/main/docs/BindingsOverview.md#website_site_name) setting
101-
- The name of the function
99+
NOTE: `FunctionId` is generated from the name of the function and either
102100

103-
If either of these values are changed then a new FunctionId will be generated and result in the function starting over from the beginning, including creating a new Leases table.
101+
* The [WEBSITE_SITE_NAME](https://github.com/Azure/azure-functions-sql-extension/blob/main/docs/BindingsOverview.md#website_site_name) setting
102+
* [IHostIdProvider.GetHostIdAsync](https://github.com/Azure/azure-webjobs-sdk/blob/dev/src/Microsoft.Azure.WebJobs.Host/Executors/IHostIdProvider.cs#L14) as a fallback if the WEBSITE_SITE_NAME setting doesn't exist
103+
104+
If either the name of the function or the `WEBSITE_SITE_NAME`/`GetHostIdAsync` values are changed then a new FunctionId will be generated and result in the function starting over from the beginning, including creating a new Leases table.
104105

105106
This table is used to ensure that all changes are processed and that no change is processed more than once. This table consists of two groups of columns:
106107

samples/samples-csharp/local.settings.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
55
"FUNCTIONS_WORKER_RUNTIME": "dotnet",
66
"SqlConnectionString": "",
7-
"WEBSITE_SITE_NAME": "SamplesCSharp",
87
"Sp_SelectCost": "SelectProductsCost",
98
"ProductCost": 100
109
}

samples/samples-csx/local.settings.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
55
"FUNCTIONS_WORKER_RUNTIME": "dotnet",
66
"SqlConnectionString": "",
7-
"WEBSITE_SITE_NAME": "SamplesCsx",
87
"Sp_SelectCost": "SelectProductsCost",
98
"ProductCost": 100
109
}

samples/samples-java/local.settings.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
55
"FUNCTIONS_WORKER_RUNTIME": "java",
66
"SqlConnectionString": "",
7-
"WEBSITE_SITE_NAME": "SamplesJava",
87
"Sp_SelectCost": "SelectProductsCost",
98
"ProductCost": 100
109
}

samples/samples-js-v4/local.settings.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
55
"FUNCTIONS_WORKER_RUNTIME": "node",
66
"AzureWebJobsFeatureFlags": "EnableWorkerIndexing",
7-
"SqlConnectionString": "",
8-
"WEBSITE_SITE_NAME": "SamplesNodeV4"
7+
"SqlConnectionString": ""
98
}
109
}

samples/samples-js/local.settings.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
55
"FUNCTIONS_WORKER_RUNTIME": "node",
66
"SqlConnectionString": "",
7-
"WEBSITE_SITE_NAME": "SamplesJavascript",
87
"Sp_SelectCost": "SelectProductsCost",
98
"ProductCost": 100
109
}

samples/samples-outofproc/local.settings.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
55
"FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated",
66
"SqlConnectionString": "",
7-
"WEBSITE_SITE_NAME": "SamplesOOP",
87
"Sp_SelectCost": "SelectProductsCost",
98
"ProductCost": 100
109
}

samples/samples-powershell/local.settings.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
"FUNCTIONS_WORKER_RUNTIME": "powershell",
66
"FUNCTIONS_WORKER_RUNTIME_VERSION" : "~7.2",
77
"SqlConnectionString": "",
8-
"WEBSITE_SITE_NAME": "SamplesPowershell",
98
"Sp_SelectCost": "SelectProductsCost",
109
"ProductCost": 100
1110
}

samples/samples-python-v2/local.settings.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
66
"AzureWebJobsFeatureFlags": "EnableWorkerIndexing",
77
"SqlConnectionString": "",
8-
"WEBSITE_SITE_NAME": "SamplesPythonV2",
98
"PYTHON_ISOLATE_WORKER_DEPENDENCIES": "1"
109
}
1110
}

samples/samples-python/local.settings.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
55
"FUNCTIONS_WORKER_RUNTIME": "python",
66
"SqlConnectionString": "",
7-
"WEBSITE_SITE_NAME": "SamplesPython",
87
"Sp_SelectCost": "SelectProductsCost",
98
"ProductCost": 100
109
}

src/SqlBindingUtilities.cs

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -54,21 +54,6 @@ public static string GetConnectionString(string connectionStringSetting, IConfig
5454
return connectionString;
5555
}
5656

57-
public static string GetWebSiteName(IConfiguration configuration)
58-
{
59-
if (configuration == null)
60-
{
61-
throw new ArgumentNullException(nameof(configuration));
62-
}
63-
string websitename = configuration.GetConnectionStringOrSetting(SqlBindingConstants.WEBSITENAME);
64-
// We require a WEBSITE_SITE_NAME for avoiding duplicates if users use the same function name accross apps.
65-
if (string.IsNullOrEmpty(websitename))
66-
{
67-
throw new ArgumentException($"WEBSITE_SITE_NAME cannot be null or empty in your function app settings, please update the setting with a string value. Please refer to https://github.com/Azure/azure-functions-sql-extension/blob/main/docs/BindingsOverview.md#website_site_name for more information.");
68-
}
69-
return websitename;
70-
}
71-
7257
/// <summary>
7358
/// Parses the parameter string into a list of parameters, where each parameter is separated by "," and has the form
7459
/// "@param1=param2". "@param1" is the parameter name to be used in the query or stored procedure, and param1 is the

src/TriggerBinding/SqlTriggerBinding.cs

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,9 @@ public async Task<IListener> CreateListenerAsync(ListenerFactoryContext context)
7878
{
7979
_ = context ?? throw new ArgumentNullException(nameof(context), "Missing listener context");
8080

81-
string userFunctionId = this.GetUserFunctionId();
82-
string oldUserFunctionId = await this.GetOldUserFunctionIdAsync();
83-
return new SqlTriggerListener<T>(this._connectionString, this._tableName, this._leasesTableName, userFunctionId, oldUserFunctionId, context.Executor, this._sqlOptions, this._logger, this._configuration);
81+
string websiteSiteNameFunctionId = this.GetWebsiteSiteNameFunctionId();
82+
string hostIdFunctionId = await this.GetHostIdFunctionIdAsync();
83+
return new SqlTriggerListener<T>(this._connectionString, this._tableName, this._leasesTableName, websiteSiteNameFunctionId, hostIdFunctionId, context.Executor, this._sqlOptions, this._logger, this._configuration);
8484
}
8585

8686
public ParameterDescriptor ToParameterDescriptor()
@@ -94,18 +94,23 @@ public ParameterDescriptor ToParameterDescriptor()
9494
}
9595

9696
/// <summary>
97-
/// Returns an ID that uniquely identifies the user function.
97+
/// Returns an ID that uniquely identifies the user function, based on the WEBSITE_SITE_NAME configuration value.
9898
///
9999
/// We call the WEBSITE_SITE_NAME from the configuration and use that to create the hash of the
100100
/// user function id. Appending another hash of class+method in here ensures that if there
101101
/// are multiple user functions within the same process and tracking the same SQL table, then each one of them
102102
/// gets a separate view of the table changes.
103103
/// </summary>
104-
private string GetUserFunctionId()
104+
/// <returns>The function ID, or NULL if there isn't a config value for WEBSITE_SITE_NAME</returns>
105+
private string GetWebsiteSiteNameFunctionId()
105106
{
106107
// Using read-only App name for the hash https://learn.microsoft.com/en-us/azure/app-service/reference-app-settings?tabs=kudu%2Cdotnet#app-environment
107-
string websiteName = SqlBindingUtilities.GetWebSiteName(this._configuration);
108-
108+
string websiteName = this._configuration.GetConnectionStringOrSetting(SqlBindingConstants.WEBSITENAME);
109+
if (string.IsNullOrEmpty(websiteName))
110+
{
111+
this._logger.LogWarning("WEBSITE_SITE_NAME configuration is not set, will fall back to using function ID based on the host ID. This will mean consumption plan scaling will not work as intended.");
112+
return null;
113+
}
109114
var methodInfo = (MethodInfo)this._parameter.Member;
110115
// Get the function name from FunctionName attribute for .NET functions and methodInfo.Name for non .Net
111116
string functionName = ((FunctionNameAttribute)methodInfo.GetCustomAttribute(typeof(FunctionNameAttribute)))?.Name ?? $"{methodInfo.Name}";
@@ -118,7 +123,7 @@ private string GetUserFunctionId()
118123
}
119124

120125
/// <summary>
121-
/// Returns the deprecated ID that was used to identify the user function.
126+
/// Returns an ID that uniquely identifies the user function, based on the host ID.
122127
///
123128
/// We call the WebJobs SDK library method to generate the host ID. The host ID is essentially a hash of the
124129
/// assembly name containing the user function(s). This ensures that if the user ever updates their application,
@@ -127,7 +132,7 @@ private string GetUserFunctionId()
127132
/// are multiple user functions within the same process and tracking the same SQL table, then each one of them
128133
/// gets a separate view of the table changes.
129134
/// </summary>
130-
private async Task<string> GetOldUserFunctionIdAsync()
135+
private async Task<string> GetHostIdFunctionIdAsync()
131136
{
132137
string hostId = await this._hostIdProvider.GetHostIdAsync(CancellationToken.None);
133138

0 commit comments

Comments
 (0)