I am working on two functions in Azure Functions
Timer Function and Http Trigger Function
My timer functions runs every 1 hour and it executes the http function via an Http Client.
Now I do get an error Synchronous operations are disallowed
And I know how to solve this using the article on stack overflow
But I am curious as why am I getting this error?
Whats the cause of it?
The error doesn't occur when using Postman.
My Timer Code
Azure Functions Core Tools (3.0.2245 Commit hash: 1d094e2f3ef79b9a478a1621ea7ec3f93ac1910d)
Function Runtime Version: 3.0.13139.0
Host configuration file read:
{
"version": "2.0"
}
public static class DoSomeStuffTimer
{
[FunctionName("DoSomeStuffTimer")]
public static void Run([TimerTrigger("0 0 7 * * *")]TimerInfo myTimer, ILogger log)
{
try
{
log.LogInformation($"C# DoSomeStuffTimer executing at: {DateTime.Now}");
string url = Environment.GetEnvironmentVariable(EnvironmentKey.HostKey) + "/api/DoSomeStuff";
HttpClient client = new HttpClient();
client.PostAsJsonAsync(url, new DoSomeStuffRequest());
log.LogInformation($"C# DoSomeStuffTimer executed at: {DateTime.Now}");
}
catch (Exception e)
{
log.LogInformation(e.ToString());
}
}
}
My Http Code
public class DoSomeStuffFunction
{
[FunctionName("DoSomeStuffFunction")]
public async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = "DoSomeStuff")]
HttpRequestMessage req,
ILogger log)
{
var response = new ContentResult {ContentType = "application/json", StatusCode = 200};
try
{
DoSomeStuffRequest
request = req.Content.ReadAsAsync<DoSomeStuffRequest>().Result;
}
catch (Exception e)
{
log.LogInformation(e.ToString());
}
}
}
Starting with ASP.NET Core 3.0 synchronous calls are disabled by default So any function running on 3.0 will encounter this when it tries to do synchronous calls
I looked into it and found the reason why it happens in your function is that ReadAsAsync<>() somewhere in it's operation does something synchronously. I am not sure exactly why it does this or why it doesn't break when you call the httptrigger directly. That'll require quite a bit more work to figure out.
To make your code work without FUNCTIONS_V2_COMPATIBILITY_MODE set to True you can use one of the other readers, for example ReadAsStreamAsync() instead.
Below you can find the method that works (I tested it locally). However, I would not recommend you call another function in your function app directly and instead follow the recommendations by Microsoft or create an abstraction that contains the logic that both functions can call on independently.
public class DoSomeStuffFunction
{
[FunctionName("DoSomeStuffFunction")]
public async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = "DoSomeStuff")]
HttpRequestMessage req,
ILogger log)
{
var response = new ContentResult { ContentType = "application/json", StatusCode = 200 };
try
{
var request = await req.Content.ReadAsStreamAsync();
using (StreamReader rd = new StreamReader(request))
{
var result = JsonConvert.DeserializeObject<DoSomeStuffRequest>(await rd.ReadToEndAsync()).;
}
return new OkObjectResult(response);
}
catch (Exception e)
{
log.LogInformation(e.ToString());
return new BadRequestObjectResult("It went wrong");
}
}
}
The solution you mentioned is to set the variable FUNCTIONS_V2_COMPATIBILITY_MODE to true. We can see some information about this variable in this page.
So,did you do the operation to upgraded your function from v2 to v3 ? It may cause this issue.
Update:
I test it in my side on local visual studio. When I create the function app in visual studio, I choose azure function v3(.net core). And below is my code, it works fine without any error.
namespace FunctionApp8
{
public static class Function2
{
[FunctionName("Function2")]
public static void Run([TimerTrigger("0 */1 * * * *")]TimerInfo myTimer, ILogger log)
{
log.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}");
string url = "http://localhost:7071/api/triggerFunction";
HttpClient client = new HttpClient();
client.PostAsJsonAsync(url, "");
log.LogInformation($"C# DoSomeStuffTimer executed at: {DateTime.Now}");
}
}
}
namespace FunctionApp8
{
public static class Function1
{
[FunctionName("triggerFunction")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
return (ActionResult)new OkObjectResult($"Hello");
}
}
}
I'm not sure if your function app is related to .net core 2.x. So could you please follow the steps recreate another function app(check if your visual studio has been updated to the lastest version and choose Azure Function v3 .NET core at the beginning) and test if it works fine.
Related
I'm creating an Azure Function to process files. The Azure Function will be activated with a HTTP Trigger, so the function should be executed whenever a page from a site makes a HTTP request to it.
The Function will take some time to finish processing the files, but I can't get the site waiting for the Function to finish to know If everything was ok. So what I want is some kind of "received" message from the Azure Function just to know that it received the HTTP request, before it starts processing.
Is there any way to do that with a HTTP Trigger? Can I let the caller know that its request was correctly received and, after that, start executing the Azure Function?
Yes, it's super easy to do that using Durable Functions:
1- Install 'Microsoft.Azure.WebJobs.Extensions.DurableTask' nuget package;
2-
[FunctionName("Function1")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
[DurableClient] IDurableOrchestrationClient starter,
ILogger log)
{
Guid instanceId = Guid.NewGuid();
string x = await starter.StartNewAsync("Processor", instanceId.ToString(), null);
log.LogInformation($"Started orchestration with ID = '{instanceId}'.");
return starter.CreateCheckStatusResponse(req, x);
}
3-
[FunctionName("Processor")]
public static async Task<string> Search([OrchestrationTrigger] IDurableOrchestrationContext context)
{
var output= await context.CallActivityAsync<string>("DoSomething", null);
return output;
}
[FunctionName("DoSomething")]
public static async Task<string> Execute([ActivityTrigger] string termo, ILogger log)
{
//do your work in here
}
In the previous code we're creating an Orchestrator (Processor) function, and it will start an activity which will do the process DoSomething function.
More info: https://learn.microsoft.com/en-us/azure/azure-functions/durable/durable-functions-overview
In Visual Studio, I have created 2 Azure function apps f1 and f2.
I have already change the port for both function apps.
I want to call f2 from f1 but I'm getting a NotFound error.
f1 is using http://localhost:999/demo1
f2 is using http://localhost:1212/demo2
I have tried calling one Function to Another Function within the same project and different project too both works fine.
Function 1 Example:
[FunctionName("Function1")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
try
{
//Extract Request Param
var content = await new StreamReader(req.Body).ReadToEndAsync();
QnAMakerQuestion objQnAMakerQuestion = JsonConvert.DeserializeObject<QnAMakerQuestion>(content);
//Global Variable for containing message
dynamic validationMessage;
// Validate param
if (string.IsNullOrEmpty(objQnAMakerQuestion.question))
{
validationMessage = new OkObjectResult("Question is required!");
return (IActionResult)validationMessage;
}
//Selialize Request Param
var json = JsonConvert.SerializeObject(objQnAMakerQuestion);
var stringContent = new StringContent(json, UnicodeEncoding.UTF8, "application/json");
// Call Function 2
HttpClient newClient = new HttpClient();
HttpResponseMessage responseFromAnotherFunction = await newClient.PostAsync("http://localhost:7073/api/Function2FromApp2", stringContent);
dynamic response = "";
if (responseFromAnotherFunction.IsSuccessStatusCode)
{
response = responseFromAnotherFunction.Content.ReadAsStringAsync().Result;
}
validationMessage = new OkObjectResult(response);
return (IActionResult)validationMessage;
}
catch (Exception ex)
{
dynamic validationMessage = new OkObjectResult(string.Format("Something went wrong, please try agian! Reason:{0}", ex.Message));
return (IActionResult)validationMessage;
}
}
Function 2 Example:
[FunctionName("Function2FromApp2")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
try
{
var content = await new StreamReader(req.Body).ReadToEndAsync();
QnAMakerQuestion objQnAMakerQuestion = JsonConvert.DeserializeObject<QnAMakerQuestion>(content);
//Global Variable for containing message
dynamic validationMessage;
// Validate param
if (string.IsNullOrEmpty(objQnAMakerQuestion.question))
{
validationMessage = new OkObjectResult("Question is required!");
return (IActionResult)validationMessage;
}
validationMessage = new OkObjectResult(objQnAMakerQuestion);
return (IActionResult)validationMessage;
}
catch (Exception ex)
{
dynamic validationMessage = new OkObjectResult(string.Format("Something went wrong, please try agian! Reason:{0}", ex.Message));
return (IActionResult)validationMessage;
}
}
Class Used:
public class QnAMakerQuestion
{
public string question { get; set; }
}
Note: If you run in same project then you wouldn't encounter any problem. But if you run in different project encounter a issue
regarding port. To resolve that in local.settings.json file replace
below code :
"Host": {
"LocalHttpPort": 7073
}
And Update Project Properties -> Debug to following
host start --port 7073 --pause-on-error See the screen shot below:
Post Man Test:
I have invoked Function 1 on PostMan it Invoked Function 1 as Function 1 Invoked Function 2 and Send Response or vice-versa from function 2 to function 1. See the screen shot below:
Just plug and play, let me know if you have any additional question.
I do not want to use service bus trigger using .net code http trigger function app to read messages from topic.
Run below code locally or deploy it on azure portal. send a message
into topic/sub it will read messages successfully.
wait for 1 minute
again send messages to topic/sub , it wont read messages from
topic/sub. if running locally keep fucntion running and make a http
call.
public static class Function1
{
[FunctionName("Function1")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
string serviceBusConnectionString = "connectionstring";
var messageReceiver = new MessageReceiver(serviceBusConnectionString, "topicName/subscriptions/subscriptionName, ReceiveMode.PeekLock, null, 500);
var messages = await messageReceiver.ReceiveAsync(500, TimeSpan.FromSeconds(1));
if (messages.Count > 0)
{
foreach (var item in messages)
{
await messageReceiver.CompleteAsync(item.SystemProperties.LockToken);
}
}
return new OkObjectResult("");
}
}
Is there another code which will work for .net code http trigger function app?
I have a simple azure function as below
public static class Function1
{
[FunctionName("Function1")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
[Table("MyTable")] CloudTable cloudTable,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
var entity = new CustomerEntity("Jon", "Snow")
{
Email = "babc#email.com",
PhoneNumber = "04026753432"
};
// Create the InsertOrReplace table operation
TableOperation insertOrMergeOperation = TableOperation.InsertOrMerge(entity);
// Execute the operation.
TableResult result = await cloudTable.ExecuteAsync(insertOrMergeOperation);
CustomerEntity insertedCustomer = result.Result as CustomerEntity;
// Get the request units consumed by the current operation. RequestCharge of a TableResult is only applied to Azure Cosmos DB
if (result.RequestCharge.HasValue)
{
Console.WriteLine("Request Charge of InsertOrMerge Operation: " + result.RequestCharge);
}
return new OkObjectResult($"Hello");
}
}
I am using Microsoft.Azure.Cosmos.Table instead of Microsoft.WindowsAzure.Storage.Table. However the "Table" attribute is not being recognize. Is there any extension nuget that I need to include here ?
-Alan-
It sounds like you may be trying to mix and match SDKs. The binding that you are using in the function is not using the Cosmos version of the table SDK, so you either need to use the storage one outright, or not leverage a binding and just use the Cosmos Table SDK directly to create your own instance of a cloudTable
Add Microsoft.Azure.WebJobs.Extensions.Storage package and your done
So I'm prototyping some Azure Durable Functions, to try and understand to see if they will fit within a proposed solution for our internal API system.
Based on examples, I've created a Orchestrator Client (HelloOrchestratorClient.cs), that responds to a HttpTrigger. This client extracts some information from the original request, then proceeds to fire off a Orchestrator Function (HelloOrchestrator.cs) passing in some of the information extracted:
Complex HelloOrchestratorClient.cs:
[FunctionName("HttpSyncStart")]
public static async Task<HttpResponseMessage> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, methods: "get", Route = "orchestrators/{functionName}/wait")]
HttpRequestMessage req,
[OrchestrationClient] DurableOrchestrationClient starter,
string functionName,
ILogger log)
{
HttpReq originalRequest = new HttpReq() {
DeveloperId = GetDevKey(req,apiHeaderKey),
QueryString = req.RequestUri.Query,
APIName = GetQueryStringValue(req,APIName),
APIVersion = GetQueryStringValue(req,APIVersion)
};
string instanceId = await starter.StartNewAsync(functionName, originalRequest);
TimeSpan timeout = GetTimeSpan(req, Timeout) ?? TimeSpan.FromSeconds(30);
TimeSpan retryInterval = GetTimeSpan(req, RetryInterval) ?? TimeSpan.FromSeconds(1);
return await starter.WaitForCompletionOrCreateCheckStatusResponseAsync(
req,
instanceId,
timeout,
retryInterval);
}
The HelloOrchestrator.cs simply for now is just calling off to one of our internal API's and returning a JsonProduct payload (Simple POCO describing, you guessed it, a title), using a ActivityTigger named HelloOrchestrator.APICall to make the API call itself.
Complex HelloOrchestrator.cs:
[FunctionName("E1_JsonProduct")]
public static async Task<List<JsonProduct>> Run(
[OrchestrationTrigger] DurableOrchestrationContextBase context,
ILogger log)
{
List<JsonProduct> output = new List<JsonProduct>();
HttpReq r = context.GetInput<HttpReq>();
if(r != null)
{
if(r.DeveloperId == null)
{
return output;
}
output.Add(await context.CallActivityAsync<JsonProduct>("E1_CallAPI",r));
return output;
}
return output;
}
[FunctionName("E1_CallAPI")]
public async static Task<JsonProduct> APICall([ActivityTrigger] HttpReq req,
ILogger log)
{
JsonProduct products = null;
string u = $"{baseAddress}{req.APIVersion}/{req.APIName}{req.QueryString}";
var request = new HttpRequestMessage(HttpMethod.Get, u);
request.Headers.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json")
);
request.Headers.Add("x-apikey",req.DeveloperId);
log.LogInformation($"URL calling = '{request.RequestUri.AbsoluteUri}'.");
HttpResponseMessage response = await client.SendAsync(request);
// return await response.Content.ReadAsStringAsync();
if(response.IsSuccessStatusCode)
{
var formatter = new JsonMediaTypeFormatter
{
SerializerSettings = HelloProj.CosmosDB.Models.Products.Converter.Settings
};
products = await response.Content.ReadAsAsync<JsonProduct>(new [] {formatter});
}
return products;
}
Side Note: The plan is if I can get this to work, is to fan out a bunch of processes to different API's and fan back in again and merge the JSON payload and return it back to the originator.
Issue I'm experiencing
So, when my List<JsonProduct> is returned back from HelloOrchestrator.Run, I receive the following NullReferenceException found on this Gist (Big stack trace) and I receive a 500 response from the Orchestrator Client.
The following proves the output returned does actually have an object at runtime:
Could it be due to the complexity of JsonProduct (Again find the model classes here)? I ask, because when I swap out my Orchestrator Function for a simpler model structure, I don't receive a 500, I receive my JSON Payload.
This example shows the Simple Orchestrator Function HelloOrchestrator.cs, returning a simple TestToDo.cs (Gist for model) flat object that doesn't error:
Simple HelloOrchestrator.cs:
[FunctionName("E1_Todo")]
public static async Task<TestToDo> RunToDo(
[OrchestrationTrigger] DurableOrchestrationContextBase context,
ILogger log)
{
HttpReq r = context.GetInput<HttpReq>();
TestToDo todo = new TestToDo();
if(r != null)
{
todo = await context.CallActivityAsync<TestToDo>("E1_CallAPITodo",r);
}
return todo;
}
[FunctionName("E1_CallAPITodo")]
public async static Task<TestToDo> APITodoCall([ActivityTrigger] HttpReq req,
ILogger log)
{
var request = new HttpRequestMessage(HttpMethod.Get, "https://jsonplaceholder.typicode.com/todos/1");
request.Headers.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json")
);
log.LogInformation($"URL calling = '{request.RequestUri.AbsoluteUri}'. for {req.QueryString}");
HttpResponseMessage response = await client.SendAsync(request);
return await response.Content.ReadAsAsync<TestToDo>();
}
More Information
If you require my full prototype projects, you can find them here:
Complex Project (Throws 500 and exception)
When you run it, use the following in something like Postman (After F5ing it):
http://localhost:7071/api/orchestrators/E1_JsonProduct/wait?timeout=20&retryInterval=0.25&api=products&apiVersion=v1&filterByImprints=W%26N&N
Simple Project (No 500 or Exception thrown)
When you run it, use the following in something like Postman (after F5ing it):
http://localhost:7071/api/orchestrators/E1_Todo/wait?timeout=20&retryInterval=0.25
Looking at the callstack you posted, the NullReferenceException appears to be a bug in the DurableOrchestrationClient class. Looking at the code (which you can find here) is seems possible that if the query string you're using cannot be parsed correctly, a null-ref is possible.
You mentioned you're using the following URL for testing:
http://localhost:7071/api/orchestrators/E1_JsonProduct/wait?timeout=20&retryInterval=0.25&api=products&apiVersion=v1&filterByImprints=W%26N&N
I wonder if the last two characters (&N) are the source of the problem. Is is possible to encode the & or remove it entirely to isolate the problem?
Either way, it would be great if you could log an issue here: https://github.com/Azure/azure-functions-durable-extension/issues