0

In service fabric stateful services, there is RunAsync(cancellationToken) with using() for state manager transaction.

The legacy code I want to refactor contains two queues with dequeue attempts inside the while(true) with 1 second delay. I would like to get rid of this unnecessary delay and instead use two distinct reactive queues (semaphores with reliable queues).

The problem is, now the two distinct workflows depending on these two queues need to be separated into two separate threads because if these two queues run in single thread one wait() will block the other code from running. (I know probably best practice would separate these two tasks into two microservices, next project.)

I came up with below code as a solution:

    protected override async Task RunAsync(CancellationToken cancellationToken)
    {
        await Task.WhenAll(AsyncTask1(cancellationToken), AsyncTask2(cancellationToken)).ConfigureAwait(false);
    }

And each task contains something like:

    while (true)
    {
        cancellationToken.ThrowIfCancellationRequested();

        using (var tx = this.StateManager.CreateTransaction())
        {
            var maybeMessage = await messageQueue.TryDequeueAsync(tx, cancellationToken).ConfigureAwait(false);

            if (maybeMessage.HasValue)
            {
                DoWork();
            }

            await tx.CommitAsync().ConfigureAwait(false);
        }
    }

Seems working but I just want to make sure if the using(statemanger.createTansaction()) is ok to be used in this parallel way..

swcraft
  • 2,014
  • 3
  • 22
  • 43

1 Answers1

1

According to documentation

Depending on the replica role for single-entry operation (like TryDequeueAsync) the ITransaction uses the Repeatable Read isolation level (when primary) or Snapshot isolation level (when **secondary).


Repeatable Read

Any Repeatable Read operation by default takes Shared locks.

Snapshot

Any read operation done using Snapshot isolation is lock free.


So if DoWork doesn't modifies the reliable collection then multiple transaction can be executed in parallel with no problems.

In case of multiple reads / updates - this can cause deadlocks and should be done with care.

Oleg Karasik
  • 959
  • 6
  • 17
  • In case of secondary for stateful service, my understanding is that they don't participate in actual operation so I guess the above statement means syncronization of reliable collections to Secondary done in Snapshot fashion. – swcraft Oct 11 '18 at 15:04
  • @JP_medevice when you execute an operation like `AddAsync` on primary replica it sends serialized data to all secondary replicas. This is done to make sure that all secondary replicas would have the same data when `ITransaction` is committed (see [this](https://github.com/MicrosoftDocs/azure-docs/blob/master/articles/service-fabric/service-fabric-work-with-reliable-collections.md) for more details). The snapshot isolation here means that you won't see any changes committed by primary replica after you've started the read operation. These changes will be available only in the new transaction. – Oleg Karasik Oct 12 '18 at 07:47
  • That is my understanding as well and any handling on execution based on the queue state is only done in primary, one cautious examination I have done regarding this logic is applying semaphore to release queue only based on enqueue request to the reliable queue and it only makes sense the semaphore with reliable queue working on single node, not multiple nodes. – swcraft Oct 12 '18 at 14:46