Doubt about Design Patterns - Idempotency Messages

Asked

Viewed 280 times

4

I’m trying to create an application example that can call a function/method and that after a certain time it checks if the process has already been executed, if not, call the same function/method again and check the previous progress if it is running or is finished and return the process.

I have searched long enough, but I have not found an example that can be followed. The links below describe what Design Patterns - Idempotency Messages actually does.

https://msdn.microsoft.com/pt-br/library/dn589781.aspx

http://blog.jonathanoliver.com/idempotency-patterns/

I’m trying this way;

using System;

namespace Idempotent_Messages
{
    public class Program
    {
        static IdePotencyHelper idePotencyHelper = new IdePotencyHelper();
        public static int passo = 1;

        static void Main(string[] args)
        {          
            var func = new Funcionario();
            var command = new CqrsBase<Int32, Funcionario>();
            command.Execute(func);

            command.IdPotency((f) =>
            {
                var funcBanco = new Funcionario();
                funcBanco.Id = 1;

                return f.Id == funcBanco.Id;
            },
            (x)=> { return x.Id; }
            , func);
        }
    }

    public class Funcionario
    {
        public int Id { get; set; }
    }

    public class CqrsBase<T, TArgs> 
    {
        public T Execute(TArgs args)
        {
            return default(T);
        }
    }

    public static class CqrsBase
    {
        public static TReturn IdPotency<TReturn, TArgs>(this CqrsBase<TReturn, TArgs> cqrs, Func<TArgs,bool> verify, Func<TArgs, TReturn> existsFunc, TArgs args)
        {
            var existe = verify(args);
            if (existe)
            {
                return existsFunc(args);
            }
            else
            {
                return cqrs.Execute(args);
            }
        }
    }
}

The Design Patterns - Idempotency

An operation is idempotent if it has no additional effects if it is called more than once with the same parameters. You can’t control how your public API is called by your customers, so you should make sure that it doesn’t lead to any unwanted effects if they repeat their calls over and over again.

A common solution is to provide a correlation ID for any potentially state-changed operation. The service checks in some repository if the request has been processed. If yes, the previously stored response must be provided.

Here is a flowchart that shows communication with a correlation identification:

inserir a descrição da imagem aqui

Image copied from the website.

The problem is that I don’t know how to create an identifier for each process to store its status and after the determinant time check the process by the identifier.

  • 2

    It’s not bad, but Voce needs to greatly improve the quality of your code.

  • in fact, I have to retrieve it completely, in a blog I found a good description of the subject. http://blog.jonathanoliver.com/idempotency-patterns/, but does not have an example of application.

  • @Brunocosta, I made an improved edition , already very close to what I really need.

  • What I don’t understand is what this has to do with repeating tasks after a certain period of time...

  • You understand the concept of idempotency-Patterns, ??

  • Already knew. Idempotency is not a pattern, but rather a property/feature of certain operation.

  • get it, revise the concepts then microsoft itself says it’s a Patterns . https://msdn.microsoft.com/pt-br/library/dn589781.aspx

  • No need to read this article. A Wikiedia says right away that Idempotency is a property in the first sentence ... is the property of certain operations in mathematics and computer science

  • @Brunocosta, okay, what I need is what’s in the article, not what you posted.

  • I edited the answer.

Show 5 more comments

1 answer

6


This function does not do what Oce specified, but is closer to achieving its goal. It experiences performing the function action a certain number of times. If the action complete within the specified time then the value is returned. Adapt the function to meet your additional requirement to complete a previous call.

static async Task<T> Retry<T>(Func<T> action, TimeSpan timeout, int? attempts = 3){
    for(int i = 0; i < attempts; ++i){
        var task = Task.Factory
            .FromAsync(action.BeginInvoke(null, null), r => action.EndInvoke(r));
        var timeoutTask = Task.Delay(timeout);
        var completedTask = await Task.WhenAny(task, timeoutTask);
        if(completedTask == task){
            return task.Result; 
        }
    }
    throw new InvalidOperationException(
        $"Was not able to execute the task within {attempts} attempts");
}

To use:

Retry(() => 4, TimeSpan.FromSeconds(1)).Result.Dump();
Retry(() => 4, TimeSpan.FromMilliseconds(1)).Result.Dump();
Retry(() => {Thread.Sleep(500); return 4;}, TimeSpan.FromMilliseconds(500)).Result.Dump();
Retry(() => {Thread.Sleep(500); return 4;}, TimeSpan.FromMilliseconds(1)).Result.Dump();

Requests for clarification of comments:

How can I identify which action is being executed

One task is being executed if its status is equal to Running

var task = Task.Delay(500);
task.Status == TaskStatus.Running // está a ser executada

Another less correct way is to assume that your Task is running until she completes the await whether it comes out by exception, or by full process

var task = Task.Delay(500);
await task; //a task está a ser executada até á próxima instrucao
Debug.Assert(task.Status == TaskStatus.RanToCompletion) // a task completou

Then check if a process finished in 10 minutes can be done as follows:

async Task<bool> Timeout(Action action, TimeSpan timeout){
    var task = Task.Factory
        .FromAsync(action.BeginInvoke(null, null), r => action.EndInvoke(r));
    var timeoutTask = Task.Delay(timeout);
    var completedTask = await Task.WhenAny(task, timeoutTask);
    return completedTask == task;
}

Timeout(()=> Thread.Sleep(TimeSpan.FromSeconds(5)), TimeSpan.FromMinutes(10)).Result.Dump();

If you paid attention you saw that the code is similar to the code I showed earlier for the Retry.

I just want you to execute again if I haven’t had anything done of that same action

I don’t know what this means. If Voce ordered a task then from the moment it enters the state TaskStatus.Running she’s already done something.

That is, Voce will never have anything executed again because something has already been executed.


It seems the question has changed a bit, but you criticize it apart...

The problem is I don’t know how to create an identifier for each process store its status and after the determining time verify the process by the identifier.

The only way I know how to solve this problem is to create an identifier when creating the process and provide that API consumer identifier.

Minimalist example:

/*********** API ***********/
private Dictionary<string, Task<int>> tarefas = new Dictionary<string, Task<int>>();
private Random r = new Random();

private async Task<int> Tarefa(){
    await Task.Delay(TimeSpan.FromSeconds(5));
    return r.Next();
}

public string CriaTarefa(){
    var id = Guid.NewGuid().ToString();
    tarefas.Add(id, Tarefa());
    return id;
}

public int? VerificaTarefa(string id){
    Task<int> tarefa;
    if(!tarefas.TryGetValue(id, out tarefa)){
        return null;
    }
    if(!tarefa.IsCompleted){
        return null;
    }
    return tarefa.Result;
}

/*********** Cliente ***********/
var id = CriaTarefa();

while(VerificaTarefa(id) == null){
    Thread.Sleep(500);
}

VerificaTarefa(id).Dump();

There are better techniques that avoid the Polling of the operation

Instead of verifying the process, what Voce should do is create an event that allows the customer to be notified.

/*********** API ***********/   
public class Tarefa{
    public event EventHandler<int> OperationCompleted;
    private Random r = new Random();

    public async void CriaTarefa(){
        await Task.Delay(TimeSpan.FromSeconds(5));
        OperationCompleted?.Invoke(this, r.Next());
    }

}

/*********** Cliente ***********/
var tarefa = new Tarefa();
tarefa.OperationCompleted += (ctx, r) => r.Dump();
tarefa.CriaTarefa();
Thread.Sleep(TimeSpan.FromSeconds(6));
  • 1

    But this is not what I understood that was asked. I understood that you want to know how to return the result of an operation already performed previously. And that has nothing to do with "Retry".

  • 1

    @Thiagolunardi My answer was for the first version of the question. se não, **chama novamente a mesma função/método** e verificar o andamento anterior se esta sendo executado ou se já acabou e devolver o retorno do processo.. I don’t know if I’ll edit the answer or not. but if you edit it I’ll let you know

  • 2

    @Thiagolunardi I edited the answer.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.