Cosmos DB 408 response in Azure Function - azure

I have an Azure Function (v2) that accesses Cosmos DB, but not through a binding (we need to use custom serialization settings). I've followed the example here for setting up an object that should then be available to all instances of the activity function. Mine is a little different because our custom CosmosDb object requires an await for setup.
public static class AnalyzeActivityTrigger
{
private static readonly Lazy<Task<CosmosDb>> LazyCosmosDb = new Lazy<Task<CosmosDb>>(InitializeDocumentClient);
private static Task<CosmosDb> CosmosDb => LazyCosmosDb.Value;
private static Task<CosmosDb> InitializeDocumentClient()
{
return StorageFramework.CosmosDb.GetCosmosDb(DesignUtilities.Storage.CosmosDbContainerDefinitions, DesignUtilities.Storage.CosmosDbMigrations);
}
[FunctionName(nameof(AnalyzeActivityTrigger))]
public static async Task<Guid> Run(
[ActivityTrigger]DurableActivityContext context,
ILogger log)
{
var analyzeActivityRequestString = context.GetInput<string>();
var analyzeActivityRequest = StorageFramework.Storage.Deserialize<AnalyzeActivityRequest>(analyzeActivityRequestString);
var componentDesign = StorageFramework.Storage.Deserialize<ComponentDesign>(analyzeActivityRequest.ComponentDesignString);
var (analysisSet, _, _) = await AnalysisUtilities.AnalyzeComponentDesignAndUploadArtifacts(componentDesign,
LogVariables.Off, new AnalysisLog(), Stopwatch.StartNew(), analyzeActivityRequest.CommitName, await CosmosDb);
return analysisSet.AnalysisReport.Guid;
}
}
We fan out, calling this activity function in parallel. Our documents are fairly large, so updating them is expensive, and that happens as part of this code.
I sometimes get this error when container.ReplaceItemAsync is called:
Response status code does not indicate success: 408 Substatus: 0 Reason: (Message: Request timed out. ...
The obvious thing to do seems to be to increase the timeout, but could this be indicative of some other problem? Increasing the timeout seems like addressing the symptom rather than the problem. We have code that scales up our RUs before all this happens, too. I'm wondering if it has to do with Azure Functions fanning out and that putting too much load on it. So I've also played around with adjusting the host.json settings for durableTask like maxConcurrentActivityFunctions and maxConcurrentOrchestratorFunctions, but to no avail so far.
How should I approach this 408 error? What steps can I consider to mitigate it other than increasing the request timeout?
Update 1: I increased the default request timeout to 5 minutes and now I'm getting 503 responses.
Update 2: Pointing to a clone published to an Azure Function on the Premium plan seems to work after multiple tests.
Update 3: We weren't testing it hard enough. The problem is exhibited on the Premium plan as well. GitHub Issue forthcoming.
Update 4: We seem to have solved this by a combination of using Gateway mode in connecting to Cosmos and increasing RUs.

A timeout can indeed signal issues regarding instance resources. Reference: https://learn.microsoft.com/azure/cosmos-db/troubleshoot-dot-net-sdk#request-timeouts
If you are running on Functions, take a look at the Connections. Also verify CPU usage in the instances. If CPU is high, it can affect requests latency and end up getting timeouts.
For Functions, you can certainly use DI to avoid the whole Lazy declaration: https://github.com/Azure/azure-cosmos-dotnet-v3/tree/master/Microsoft.Azure.Cosmos.Samples/Usage/AzureFunctions
Create a Startup.cs file with:
using System;
using Microsoft.Azure.Cosmos;
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
[assembly: FunctionsStartup(typeof(YourNameSpace.Startup))]
namespace YourNameSpace
{
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.AddSingleton((s) => {
CosmosClient cosmosClient = new CosmosClient("connection string");
return cosmosClient;
});
}
}
}
And then you can make your Functions not static and inject it:
public class AnalyzeActivityTrigger
{
private readonly CosmosClient cosmosClient;
public AnalyzeActivityTrigger(CosmosClient cosmosClient)
{
this.cosmosClient = cosmosClient;
}
[FunctionName(nameof(AnalyzeActivityTrigger))]
public async Task<Guid> Run(
[ActivityTrigger]DurableActivityContext context,
ILogger log)
{
var analyzeActivityRequestString = context.GetInput<string>();
var analyzeActivityRequest = StorageFramework.Storage.Deserialize<AnalyzeActivityRequest>(analyzeActivityRequestString);
var componentDesign = StorageFramework.Storage.Deserialize<ComponentDesign>(analyzeActivityRequest.ComponentDesignString);
var (analysisSet, _, _) = await AnalysisUtilities.AnalyzeComponentDesignAndUploadArtifacts(componentDesign,
LogVariables.Off, new AnalysisLog(), Stopwatch.StartNew(), analyzeActivityRequest.CommitName, this.cosmosClient);
return analysisSet.AnalysisReport.Guid;
}
}

Related

CosmosDB Container not getting created

CreateContainerIfNotExistsAsync is throwing an exception with status code "Bad Request" if the container does not exist in the db. If the container exists in the db, then no exception is thrown. Can anyone help me why this is happening.
(Hid the url and key for online posting)
using Microsoft.Azure.Cosmos;
using Microsoft.Azure.Cosmos.Linq;
using System.Threading.Tasks;
namespace CosmosDB // Note: actual namespace depends on the project name.
{
class Program
{
public static async Task Main(string[] args)
{
var cosmosUrl = "###########################";
var cosmoskey = "###########################";
var databaseName = "TestDB";
// var containerId = "ToDo";
CosmosClient client = new CosmosClient(cosmosUrl, cosmoskey);
Database database = await client.CreateDatabaseIfNotExistsAsync(databaseName);
Container container = await database.CreateContainerIfNotExistsAsync(
id: "ToDoList",
partitionKeyPath: "/category",
throughput: 100
);
}
}
}
The command fails because your input is invalid. The throughput must be a value between 400 and 10,000 RU/s (for a normal database or container) and since you are using 100 it will throw the exception.
The error won't occur if your container already exists, because it will not check (server-side) or perform an update on the throughput.
Edit:
Link to Microsoft documentation regarding service limits.
Link to Microsoft REST API (used by the SDK).

Unable to Connect from Azure Function to Azure Cosmos DB. Getting "Microsoft.Azure.Cosmos.Direct: Object reference not set to an instance of an object

Got a .Net code from Udemy course and ran in my local. Wrote an Azure Function which connects to Azure Cosmos DB and creates an item. But not getting connected to Azure Cosmos DB. See below the code and error. Appreciate any help. In the debug, found out some issue with the line
_container.CreateItemAsync(_blobdetails, newPartitionKey(_message.VideoName)).GetAwaiter().GetResult();
Code :
using System;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using Azure.Messaging.ServiceBus;
using Azure.Storage.Blobs;
using Azure.Storage.Blobs.Models;
using Microsoft.Azure.Cosmos;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Host;
using Microsoft.Extensions.Logging;
namespace ReceiveMessages
{
public static class Process
{
private static string blob_connection_string = "<blob connection string>";
private static string source_container_name = "unprocessed";
private static string destination_container_name = "processed";
private static readonly string _connection_string = "<cosmos connection string>";
private static readonly string _database_name = "appdb";
private static readonly string _container_name = "video";
[FunctionName("ProcessVideos")]
public static async Task Run([ServiceBusTrigger("videoqueue", Connection = "connection")]ServiceBusReceivedMessage myQueueItem, ILogger log)
{
try
{
ReceivedMessage _message = JsonSerializer.Deserialize<ReceivedMessage>(Encoding.UTF8.GetString(myQueueItem.Body));
BlobServiceClient _client = new BlobServiceClient(blob_connection_string);
BlobContainerClient _source_container_client = _client.GetBlobContainerClient(source_container_name);
BlobClient _source_blob_client = _source_container_client.GetBlobClient(_message.VideoName);
BlobContainerClient _destination_container_client = _client.GetBlobContainerClient(destination_container_name);
BlobClient _destination_blob_client = _destination_container_client.GetBlobClient(_message.VideoName);
CosmosClient _cosmosclient = new CosmosClient(_connection_string, new CosmosClientOptions());
Container _container = _cosmosclient.GetContainer(_database_name, _container_name);
BlobDownloadInfo _info = _source_blob_client.Download();
// Copy the blob to the destination container
await _destination_blob_client.StartCopyFromUriAsync(_source_blob_client.Uri);
log.LogInformation(_info.Details.LastModified.ToString());
log.LogInformation(_info.ContentLength.ToString());
BlobDetails _blobdetails = new BlobDetails();
_blobdetails.BlobName = _message.VideoName;
_blobdetails.BlobLocation = "https://videostorage100.blob.core.windows.net/processed/" + _message.VideoName;
_blobdetails.ContentLength = _info.ContentLength.ToString();
_blobdetails.LastModified = _info.Details.LastModified.ToString();
_blobdetails.id = Guid.NewGuid().ToString();
_container.CreateItemAsync(_blobdetails, new PartitionKey(_message.VideoName)).GetAwaiter().GetResult();
Console.WriteLine("Item created");
// Delete the blob from the unprocessed container
_source_blob_client.Delete();
// Add the details of the blob to an Azure Cosmos DB account
}
catch (Exception ex)
{
string s = ex.Message;
}
}
}
}
* Executed 'ProcessVideos' (Failed, Id=53b3d0b2-d46a-4ba9-bf26-d8de76af0bce, Duration=41001ms)
[2022-03-19T23:07:25.845Z] Executed 'ProcessVideos' (Failed, Id=48b50a3d-f69f-436f-accf-5140c3d7f8a0, Duration=41001ms)
[2022-03-19T23:07:25.854Z] System.Private.CoreLib: Exception while executing function: ProcessVideos. Microsoft.Azure.Cosmos.Direct: Object reference not set to an instance of an object.{"name":"CreateItemAsync","id":"c16e23cd-badc-4f0b-a940-3fac7f52c4f7","caller info":{"member":"OperationHelperWithRootTraceAsync","file":"ClientContextCore.cs","line":219},"start time":"11:06:46:894","duration in milliseconds":36808.7271,"data":{"Client Configuration":{"Client Created Time Utc":"2022-03-19T23:06:45.5706176Z","NumberOfClientsCreated":3,"User Agent":"cosmos-netstandard-sdk/3.19.0|3.19.1|08|X64|Microsoft Windows 10.0.19043|.NET Core 3.1.20|N|","ConnectionConfig":{"gw":"(cps:50, urto:10, p:False, httpf: False)","rntbd":"(cto: 5, icto: -1, mrpc: 30, mcpe: 65535, erd: False, pr: ReuseUnicastPort)","other":"(ed:False, be:False)"},"ConsistencyConfig":"(consistency: NotSet, prgns:[])"}},"children":[{"name":"ItemSerialize","id":"647e3fbb-bd9f-4b12-9367-3ed1f6c4d436","caller info":{"member":"ExtractPartitionKeyAndProcessItemStreamAsync","file":"ContainerCore.Items.cs","line":931},"start time":"11:06:46:921","duration in milliseconds":20.2124},{"name":"Microsoft.Azure.Cosmos.Handlers.RequestInvokerHandler","id":"23650859-deb4-44e1-a696-daeeab7564c8","start time":"11:06:47:893","duration in milliseconds":35799.6699,"children":[{"name":"Microsoft.Azure.Cosmos.Handlers.DiagnosticsHandler","id":"f18265a9-2a93-46c8-aa05-78b69ed30086","start time":"11:06:47:927","duration in milliseconds":35763.5861,"data":{"CPU Load History":{"CPU History":"(2022-03-19T23:06:47.9376170Z 38.049)"}},"children":[{"name":"Microsoft.Azure.Cosmos.Handlers.RetryHandler","id":"876ad64c-0dc3-42a2-9a47-4054ec301b57","start time":"11:06:47:945","duration in milliseconds":35744.6099,"children":[{"name":"Microsoft.Azure.Cosmos.Handlers.RouterHandler","id":"01ca2b19-39c0-477d-ad85-f930397e682a","start time":"11:06:47:954","duration in milliseconds":35729.8987,"children":[{"name":"Microsoft.Azure.Cosmos.Handlers.TransportHandler","id":"e372af16-b68c-438d-9ae1-2fdaad2d5f23","start time":"11:06:47:955","duration in milliseconds":35720.3525,"children":[{"name":"Microsoft.Azure.Documents.ServerStoreModel Transport Request","id":"9995478e-5933-4b21-8c08-cf03501ebe03","caller info":{"member":"ProcessMessageAsync","file":"TransportHandler.cs","line":109},"start time":"11:06:47:963","duration in milliseconds":35704.0196,"data":{"Client Side Request Stats":{"Id":"AggregatedClientSideRequestStatistics","ContactedReplicas":[{"Count":1,"Uri":"rntbd://cdb-ms-prod-westus1-fd76.documents.azure.com:14059/apps/0152c08e-edca-4977-bca0-40bb4325ee70/services/117845df-eb50-4f9a-8f97-0a5981cfeaae/partitions/1e48d158-7844-4a7c-89a0-aa99c17adcb8/replicas/132920901445077053s/"},{"Count":1,"Uri":"rntbd://cdb-ms-prod-westus1-fd76.documents.azure.com:14352/apps/0152c08e-edca-4977-bca0-40bb4325ee70/services/117845df-eb50-4f9a-8f97-0a5981cfeaae/partitions/1e48d158-7844-4a7c-89a0-aa99c17adcb8/replicas/132920901532421814s/"},{"Count":1,"Uri":"rntbd://cdb-ms-prod-westus1-fd76.documents.azure.com:14095/apps/0152c08e-edca-4977-bca0-40bb4325ee70/services/117845df-eb50-4f9a-8f97-0a5981cfeaae/partitions/1e48d158-7844-4a7c-89a0-aa99c17adcb8/replicas/132920901532421816s/"}],"RegionsContacted":["https://videodbupdate-westus.documents.azure.com/"],"FailedReplicas":[],"AddressResolutionStatistics":[{"StartTimeUTC":"2022-03-19T23:06:48.3431877Z","EndTimeUTC":"2022-03-19T23:06:48.4598190Z","TargetEndpoint":"https://videodbupdate-westus.documents.azure.com//addresses/?$resolveFor=dbs%2fHdYjAA%3d%3d%2fcolls%2fHdYjAIRIK9s%3d%2fdocs&$filter=protocol eq rntbd&$partitionKeyRangeIds=0"},{"StartTimeUTC":"2022-03-19T23:06:54.7678280Z","EndTimeUTC":"2022-03-19T23:06:54.8820135Z","TargetEndpoint":"https://videodbupdate-westus.documents.azure.com//addresses/?$resolveFor=dbs%2fHdYjAA%3d%3d%2fcolls%2fHdYjAIRIK9s%3d%2fdocs&$filter=protocol eq rntbd&$partitionKeyRangeIds=0"},{"StartTimeUTC":"2022-03-19T23:07:01.6288211Z","EndTimeUTC":"2022-03-19T23:07:01.7399788Z","TargetEndpoint":"https://videodbupdate-westus.documents.azure.com//addresses/?$resolveFor=dbs%2fHdYjAA%3d%3d%2fcolls%2fHdYjAIRIK9s%3d%2fdocs&$filter=protocol eq rntbd&$partitionKeyRangeIds=0"},{"StartTimeUTC":"2022-03-19T23:07:09.6372169Z","EndTimeUTC":"2022-03-19T23:07:09.7484346Z","TargetEndpoint":"https://videodbupdate-westus.documents.azure.com//addresses/?$resolveFor=dbs%2fHdYjAA%3d%3d%2fcolls%2fHdYjAIRIK9s%3d%2fdocs&$filter=protocol eq rntbd&$partitionKeyRangeIds=0"},{"StartTimeUTC":"2022-03-19T23:07:17.9939496Z","EndTimeUTC":"2022-03-19T23:07:18.1025134Z","TargetEndpoint":"https://videodbupdate-westus.documents.azure.com//addresses/?$resolveFor=dbs%2fHdYjAA%3d%3d%2fcolls%2fHdYjAIRIK9s%3d%2fdocs&$filter=protocol eq rntbd&$partitionKeyRangeIds=0"}],"*
*[2022-03-19T23:07:25.848Z] System.Private.CoreLib: Exception while executing function: ProcessVideos. Microsoft.Azure.Cosmos.Client: Response status code does not indicate success: ServiceUnavailable (503); Substatus: 0; ActivityId: 349d6ef1-4696-4ec8-88c9-5913129164ec; Reason: (Service is currently unavailable. More info: https://aka.ms/cosmosdb-tsg-service-unavailable
[2022-03-19T23:07:26.012Z] System.Private.CoreLib: Exception while executing function: ProcessVideos. Microsoft.Azure.Cosmos.Client: Response status code does not indicate success: ServiceUnavailable (503); Substatus: 0; ActivityId: 40f51724-2bf3-46b2-ac99-c9030aed41c6; Reason: (Service is currently unavailable. More info: https://aka.ms/cosmosdb-tsg-service-unavailable
[2022-03-19T23:07:26.040Z] ActivityId: 349d6ef1-4696-4ec8-88c9-5913129164ec, Microsoft.Azure.Cosmos.Tracing.TraceData.ClientSideRequestStatisticsTraceDatum, Windows/10.0.19043 cosmos-netstandard-sdk/3.19.1);. Microsoft.Azure.Cosmos.Direct: Message: The requested resource is no longer available at the server.*
This was fixed in SDK 3.20: https://github.com/Azure/azure-cosmos-dotnet-v3/blob/master/changelog.md#-3200---2021-06-21
Please upgrade to the recommended version to get this and other fixes.
More fixes
You are creating a client (both Blob and Cosmos) per Function execution, that goes against the recommendations and will bring problems as the number of queue messages increase, please follow https://learn.microsoft.com/azure/azure-functions/manage-connections?tabs=csharp#static-clients and use Singleton/static instances. We have a complete example on how to use DI and Functions with the CosmosClient at https://github.com/Azure/azure-cosmos-dotnet-v3/tree/master/Microsoft.Azure.Cosmos.Samples/Usage/AzureFunctions.
Also, do not block threads, since your Function is already async, do await _container.CreateItemAsync(_blobdetails, new PartitionKey(_message.VideoName)) instead.
These 2 points is what will generate these Service Unavailable errors in most cases.
ALSO VERY IMPORTANT, YOUR POST CONTAINED SERVICE KEYS AND CONNECTIONSTRINGS (I EDITED TO REMOVE THEM BUT THEY WERE ALREADY EXPOSED), ROTATE THEM IMMEDIATELY

Azure connection attempt failed

I'm using the following code to connect. I can connect to other Azure Resources ok.
But for one resource I get the following error: URL and Key are correct.
{"A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond"}
The Code is as follows
_searchClient = new SearchServiceClient(searchServiceName, new
SearchCredentials(apiKey));
_httpClient.DefaultRequestHeaders.Add("api-key", apiKey);
_searchServiceEndpoint = String.Format("https://{0}.{1}",
searchServiceName, _searchClient.SearchDnsSuffix);
bool result = RunAsync().GetAwaiter().GetResult();
Any ideas? thx in advance? How can I troubleshoot this?
I will show how this is done in c#
you will need a appsettings.json
you will need this code in the program.cs file
there are a lot of other files in the example from the document
that you may need to use , learn and edit for ur usecase
When working in c# and azure, always know what is unique about the file structured your solution first. This is why we build the examples from the docs as we learn the solution. Next we must study the different blocks of code that when executed deliver one feature or functionality to the solution as a whole.
appsettings.json
{
"SearchServiceName": "[Put your search service name here]",
"SearchIndexName": "hotels",
"SearchServiceAdminApiKey": "[Put your primary or secondary Admin API key here]",
"SearchServiceQueryApiKey": "[Put your primary or secondary Query API key here]"
}
Program.cs
namespace AzureSearch.SDKHowTo
{
using System;
using System.Linq;
using System.Threading;
using Microsoft.Azure.Search;
using Microsoft.Azure.Search.Models;
using Microsoft.Extensions.Configuration;
using Microsoft.Spatial;
// This sample shows how to delete, create, upload documents and query an index
static void Main(string[] args)
{
IConfigurationBuilder builder = new ConfigurationBuilder().AddJsonFile("appsettings.json");
IConfigurationRoot configuration = builder.Build();
SearchServiceClient serviceClient = CreateSearchServiceClient(configuration);
string indexName = configuration["SearchIndexName"];
Console.WriteLine("{0}", "Deleting index...\n");
DeleteIndexIfExists(indexName, serviceClient);
Console.WriteLine("{0}", "Creating index...\n");
CreateIndex(indexName, serviceClient);
ISearchIndexClient indexClient = serviceClient.Indexes.GetClient(indexName);
Console.WriteLine("{0}", "Uploading documents...\n");
UploadDocuments(indexClient);
ISearchIndexClient indexClientForQueries = CreateSearchIndexClient(indexName, configuration);
RunQueries(indexClientForQueries);
Console.WriteLine("{0}", "Complete. Press any key to end application...\n");
Console.ReadKey();
}
private static SearchServiceClient CreateSearchServiceClient(IConfigurationRoot configuration)
{
string searchServiceName = configuration["SearchServiceName"];
string adminApiKey = configuration["SearchServiceAdminApiKey"];
SearchServiceClient serviceClient = new SearchServiceClient(searchServiceName, new SearchCredentials(adminApiKey));
return serviceClient;
}
private static SearchIndexClient CreateSearchIndexClient(string indexName, IConfigurationRoot configuration)
{
string searchServiceName = configuration["SearchServiceName"];
string queryApiKey = configuration["SearchServiceQueryApiKey"];
SearchIndexClient indexClient = new SearchIndexClient(searchServiceName, indexName, new SearchCredentials(queryApiKey));
return indexClient;
}
private static void DeleteIndexIfExists(string indexName, SearchServiceClient serviceClient)
{
if (serviceClient.Indexes.Exists(indexName))
{
serviceClient.Indexes.Delete(indexName);
}
}
private static void CreateIndex(string indexName, SearchServiceClient serviceClient)
{
var definition = new Index()
{
Name = indexName,
Fields = FieldBuilder.BuildForType<Hotel>()
};
serviceClient.Indexes.Create(definition);
}}
Azure concepts to learn
How and why we create azure clients
Why do we use appsettings.json
What is some example file structures for azure search solutions
What coding lanague do you want to use to build that solutio
do u want to use the azure sdk
How to find and create api keys
C# concepts to learn
What is an interface and how do you use it
How to import one file in the file structure into another
How the main function works
How to call variables in to a function
How to call a function with a function
How to write server side code vs client side code
How to deploy c# code to azure
What version of c# are u using What’s is asp.net and what version will u use
What is asp.net core and what version will u use
As u can see azure and c# have a high learning curve.
Luckily you have stack overflow and documentation to research all of the above questions and more:)
For how u would troubleshoot...what I do is research each block of code in the documentation example and run all of the code locally. Then I test each block of code one at a time. Ur always testing data flowing thought the block of code. So you can just console log the result of a block a code by creating a test varable and print that varable to the console.
Because each block of Code represents one feature or functionality, each test will output either a pass or fail delivery of that feature or functionality. Thus you can design functionality, implement that design and create a test for new Feature.

How to send Azure SignalR messages to clients on all instances of a multi-instance Azure Application

We are evaluating how to send messages to connected clients via SignalR. Our application is published in Azure, and has multiple instances. We are able to successfully pass messages to clients connected to the same instance, but not other instances.
We initially were looking at ServiceBus, but we (perhaps mistakenly) found out that AzureSignalR should basically be a service bus that handles all of the backend stuff for us.
We set up signalR in Startup.cs such as:
public void ConfigureServices(IServiceCollection services)
{
var signalRConnString = Configuration.GetConnectionString("AxiomSignalRPrimaryEndPoint");
services.AddSignalR()
.AddAzureSignalR(signalRConnString)
.AddJsonProtocol(options =>
{
options.PayloadSerializerSettings.ContractResolver = new DefaultContractResolver();
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseAzureSignalR(routes =>
{
routes.MapHub<CallRegistrationHub>("/callRegistrationHub");
routes.MapHub<CaseHeaderHub>("/caseHeaderHub");
routes.MapHub<EmployeesHub>("/employeesHub");
});
}
Issue
We have to store some objects that should probably be on the service bus, and not stored in an individual instance; However, I am unsure of how to tell the hub that the objects should be on the bus and not internal to that specific instance of the hub, as below:
public class EmployeesHub : Hub
{
private static volatile List<Tuple<string, string, string,string, int>> UpdateList = new List<Tuple<string, string, string,string,int>>();
private static volatile List<Tuple<string, int>> ConnectedClients = new List<Tuple<string, int>>();
}
We have functions that need to send messages to all connected clients that are looking at the current record regardless of in what instance they reside:
public async void LockField(string fieldName, string value, string userName, int IdRec)
{
var clients = ConnectedClients.Where(x => x.Item1 != Context.ConnectionId && x.Item2 == IdRec).Select(x => x.Item1).Distinct().ToList();
clients.ForEach(async x =>
{
await Clients.Client(x).SendAsync("LockField", fieldName, value, userName, true);
});
if (!UpdateList.Any(x=> x.Item1 == Context.ConnectionId && x.Item3 == fieldName && x.Item5 == IdRec))
{
UpdateList.Add(new Tuple<string, string, string,string,int>(Context.ConnectionId,userName, fieldName, value, IdRec));
}
}
This is not working for different instances (which makes sense, because each instance will have its own objects.. However, we were hoping that by using AzureSignalR instead of SignalR (AzureSignalR conn string has an endpoint to the Azure service) that it would handle the service bus functionality for us. We are not sure what steps to take to get this functioning correctly.
Thanks.
The reason for this issue is that I was preemptively attempting to limit message traffic. I was attempting to only send messages to clients that were looking at the same record. However, because my objects were instance-specific, it would only grab the connection IDs from the current instance's object.
Further testing (using ARR affinity) proves that on a Clients.All() call, all clients, including those in different instances, receive the message.
So, our AzureSignalR setup appears to be correct.
Current POC Solution - currently testing
-When a client registers, we will broadcast to all connected clients "What field do you have locked for this Id?"
-If client is on a different Id, it will ignore the message.
-If client does not have any fields locked, it will ignore the message.
-If client has a field locked, it will respond to the message with required info.
-AzureSignalR will then rebroadcast the data required to perform a lock.
This increases message count, but not significantly. But it will resolve the multiple instances holding different connected ClientIds issue.
Just a thought, but have you tried using SignalR Groups? https://learn.microsoft.com/en-us/aspnet/core/signalr/groups?view=aspnetcore-2.2#groups-in-signalr
You could try creating a group for each combination of IdRec and fieldName and then just broadcast messages to the group. This is the gist of how I think your LockField function might look:
public async void LockField(string fieldName, string value, string userName, int IdRec)
{
string groupName = GetGroupName(IdRec, fieldName);
await Clients.Group(groupName).SendAsync("LockField", fieldName, value, userName, true);
await this.Groups.AddToGroupAsync(Context.ConnectionId, groupName);
}
You could implement the GetGroupName method however you please, so long as it produces unique strings. A simple solution might be something like
public string GetGroupName(int IdRec, string fieldName)
{
return $"{IdRec} - {fieldName}";
}

How to add more entries in customDimensions in to Application Insights Telemetry in Azure Function

I am trying to integrate Azure App Insights with an Azure Function App (HttpTriggered). I want to add my own keys and values in the "customDimensions" object of the requests table. Right now it only shows the following:
On query
requests
| where iKey == "449470fb-****" and id == "5e17e23e-****"
I get this:
LogLevel: Information
Category: Host.Results
FullName: Functions.FTAID
StartTime: 2017-07-14T14:24:10.9410000Z
param__context: ****
HttpMethod: POST
param__req: Method: POST, Uri: ****
Succeeded: True
TriggerReason: This function was programmatically called via the host APIs.
EndTime: 2017-07-14T14:24:11.6080000Z
I want to add more key values such as:
EnvironmentName: Development
ServiceLine: Business
Based on this answer, I implemented the ITelemetryInitializer interface as follows:
public class CustomTelemetry : ITelemetryInitializer
{
public void Initialize(ITelemetry telemetry)
{
var requestTelemetry = telemetry as RequestTelemetry;
if (requestTelemetry == null) return;
requestTelemetry.Context.Properties.Add("EnvironmentName", "Development");
}
}
Here is how the run.csx code for the Azure Function App looks like:
public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, ExecutionContext context, TraceWriter log)
{
// Initialize the App Insights Telemetry
TelemetryConfiguration.Active.InstrumentationKey = System.Environment.GetEnvironmentVariable("APPINSIGHTS_INSTRUMENTATIONKEY", EnvironmentVariableTarget.Process);
TelemetryConfiguration.Active.TelemetryInitializers.Add(new CustomTelemetry());
TelemetryClient telemetry = new TelemetryClient();
var jsonBody = await req.Content.ReadAsStringAsync();
GetIoItemID obj = new GetIoItemID();
JArray output = obj.GetResponseJson(jsonBody, log, telemetry);
var response = req.CreateResponse(HttpStatusCode.OK);
response.Content = new StringContent(output.ToString(), System.Text.Encoding.UTF8, "application/json");
return response;
}
But this did not work...
I believe, since you're creating the TelemetryClient yourself in this example, you don't need to bother with the telemetry initializer, you could just do
var telemetry = new TelemetryClient();
telemetry.Context.Properties["EnvironmentName"] = "Development";
directly, and everything sent by that instance of that telemetry client will have those properties set.
You'd need that telemetry initializer if you don't have control over who's creating the telemetry client and want to touch every item of telemetry created wherever?
I don't know how that TelemetryClient instance gets used downstream in azure functions though, so i'm not entirely positive, though.
Edit: from azure functions post about this, it says:
We’ll be working hard to get Application Insights ready for production
workloads. We’re also listening for any feedback you have. Please file
it on our GitHub. We’ll be adding some new features like better
sampling controls and automatic dependency tracking soon. We hope
you’ll give it a try and start to gain more insight into how your
Functions are behaving. You can read more about how it works at
https://aka.ms/func-ai
and the example from that func-ai link has a couple things:
1) it creates the telemetry client statically up front once (instead of in each call to the function)
private static TelemetryClient telemetry = new TelemetryClient();
private static string key = TelemetryConfiguration.Active.InstrumentationKey = System.Environment.GetEnvironmentVariable("APPINSIGHTS_INSTRUMENTATIONKEY", EnvironmentVariableTarget.Process);
and inside the function it is doing:
telemetry.Context.Operation.Id = context.InvocationId.ToString();
to properly do correlation with events you might create with your telemetry client so you might want to do that too.
2) it appears that the telemetry client you create you can use, but they create their own telemetry client and send data there, so anything you touch in your telemetry client's context isn't seen by azure functions itself.
so, to me that leads me to something you can try:
add a static constructor in your class, and in that static constructor, do the telemetry initializer thing you were doing above. possibly this gets your telemetry initializer added to the context before azure functions starts creating its request and calling your method?
If that doesn't work, you might need to post on their GitHub or email the person listed in the article for more details on how to do this?

Resources