I am new to Azure. I would like to create a function that returns the sequence number. I have created a function using Thread mutex to lock the sequence number. I tested the below code with around 10k parallel requests. The problem is I am getting duplicates sequence number when doing the testing, mutex is not working. I am not sure what to do to avoid duplication instead generate running number for each request
Public class MySharedMutexCounter {
public static long count = 0;
public static Mutex ObjMutex = new Mutex(false,"SeqGenerator");
}
public long GetSequnceNo(){
long seqId = -1;
try{
MySharedMutexCounter.ObjMutex.waitOne();
seqId = MySharedMutexCounter.count;
if(seqId > 100){
MySharedMutexCounter.count = 0;
seqId = MySharedMutexCounter.count;
}
return seqId;
}finally{
MySharedMutexCounter.ObjMutex.RelaseMutex();
}
return -1;
}
CodePudding user response:
Thing is, an azure function can scale to multiple instances running on different machines so you need a distributed lock of some kind or another way to guarantee there won't be concurrent access to the state.
How about using a Durable Entity? It is basically a piece of state that can be accessed by a Durable Function and operations against the state are performed in a safe way:
To prevent conflicts, all operations on a single entity are guaranteed to execute serially, that is, one after another.
(source)
A durable entity is like a distributed object, so other instances of the function will use the same entity.
The Developer Guide demonstrates a nice example using a counter. Kind of fits your scenario.
CodePudding user response:
Hi @Peter Bons I tried the below code but taking lot of time. May be something wrong in my code. Is it possible to get the value in a fraction of second bcos I shd return the value less than a second.
[FunctionName("FunctionOrchestrator")]
public static async Task<int> RunOrchestrator(
[OrchestrationTrigger] IDurableOrchestrationContext context)
{
int currentValue = -1;
var input = context.GetInput<CounterParameter>();
if (input != null && !string.IsNullOrWhiteSpace(input.OperationName))
{
var entityId = new EntityId("Counter", "myCounter");
// Perform the requested operation on the entity
currentValue = await context.CallEntityAsync<int>(entityId, input.OperationName);
}
return currentValue;
}
[FunctionName("Counter")]
public static int Counter([EntityTrigger] IDurableEntityContext ctx, ILogger log)
{
log.LogInformation($"Request for operation {ctx.OperationName} on entity.");
switch (ctx.OperationName.Trim().ToLowerInvariant())
{
case "increment":
ctx.SetState(ctx.GetState<int>() 1);
break;
}
// Return the latest value
return ctx.GetState<int>();
}
[FunctionName("AutoIncrement")]
public static async Task<HttpResponseMessage> HttpAutoIncrement(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestMessage req,
[DurableClient] IDurableOrchestrationClient starter,
[DurableClient] IDurableEntityClient client,
ILogger log)
{
// Function input comes from the request content.
var input = new CounterParameter { OperationName = "Increment" };
string instanceId = await starter.StartNewAsync("FunctionOrchestrator", input);
log.LogInformation($"Started orchestration with ID = '{instanceId}'.");
await starter.WaitForCompletionOrCreateCheckStatusResponseAsync(req, instanceId);
var entityId = new EntityId("Counter", "myCounter");
try
{
// An error will be thrown if the counter is not initialised.
var stateResponse = await client.ReadEntityStateAsync<int>(entityId);
return new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent(stateResponse.EntityState.ToString())
};
}
catch (System.NullReferenceException)
{
return new HttpResponseMessage(HttpStatusCode.NotFound)
{
Content = new StringContent("Counter is not yet initialised. "
"Initialise it by calling increment or decrement HTTP Function.")
};
}
}