Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[Workflow] Verify race condition #53179

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
qRoC opened this issue Dec 21, 2023 · 10 comments · Fixed by #53865 or #54272
Closed

[Workflow] Verify race condition #53179

qRoC opened this issue Dec 21, 2023 · 10 comments · Fixed by #53865 or #54272

Comments

@qRoC
Copy link

qRoC commented Dec 21, 2023

Symfony version(s) affected

all

Description

The behavior of transitions depends on the order in which they are declared.

How to reproduce

    private static function createWorkflowWithSameNameBackTransition(): Definition
    {
        $places = range('a', 'c');

        $transitions = [];
        $transitions[] = new Transition('a_to_bc', 'a', ['b', 'c']);
        $transitions[] = new Transition('back1', 'b', 'a');
        $transitions[] = new Transition('back1', 'c', 'b');
        $transitions[] = new Transition('back2', 'c', 'b');
        $transitions[] = new Transition('back2', 'b', 'a');

        return new Definition($places, $transitions);

        // The graph looks like:
        //       +--------+
        //   +---| back_N |<------+ +-----------------+
        //   |   +--------+       | |                 |
        //   v                    | v                 |
        // +---+    +---------+  +---+  +---+     +--------+
        // | a | -> | a_to_bc |  | b |  | c | --> | back_N |
        // +---+    +---------+  +---+  +---+     +--------+
        //                |        ^      ^
        //                +--------+------+
    }

    public static function backVariants()
    {
        return [['back1'], ['back2']];
    }

    /**
     * @dataProvider backVariants
     */
    public function testApplyWithSameNameBackTransition($transition)
    {
        $subject = new Subject();
        $definition = $this->createWorkflowWithSameNameBackTransition();
        $workflow = new Workflow($definition, new MethodMarkingStore());

        $marking = $workflow->apply($subject, 'a_to_bc');

        $this->assertFalse($marking->has('a'));
        $this->assertTrue($marking->has('b'));
        $this->assertTrue($marking->has('c'));

        $marking = $workflow->apply($subject, $transition);

        $this->assertTrue($marking->has('a'));
        $this->assertTrue($marking->has('b'));
        $this->assertFalse($marking->has('c'));
    }

Possible Solution

No response

Additional Context

No response

@qRoC qRoC added the Bug label Dec 21, 2023
@qRoC qRoC changed the title [Workflow] Support race condition [Workflow] Verify race condition Dec 21, 2023
@xabbuh
Copy link
Member

xabbuh commented Dec 22, 2023

What is the behaviour when executing this test? And what did you expect instead?

@qRoC
Copy link
Author

qRoC commented Dec 22, 2023

What is the behaviour when executing this test

back1:

  1. take token from b, set to a
  2. take token from c, set to b

Result: tokens in a and b.

back2:

  1. take token from c, set to b (but the marking in the current implementation can only contain one token per place)
  2. take token from b, set to a

Result: token in a.

The implementation is similar to Petri Nets, but with the difference that if another token moved to place, it will be ignored. That is the problem.
If fix this, it will change the behaviour of current workflows. It is also impossible to leave it as is, because the graph cannot depend on the order of declarations of its structure (see example, when visualizing there is no difference betweenback1 and back2.) Probably the only reasonable way is to validate and prohibit such transitions.

And what did you expect instead

Like Petri Nets:

back1:

  1. take token from b, set to a: a(1), b(0), c(1)
  2. take token from c, set to b: a(1), b(1), c(0)

Result: tokens in a(1) and b(1).

back2:

  1. take token from c, set to b: a(0), b(2), c(0)
  2. take token from b, set to a: a(1), b(1), c(0)

Result: tokens in a(1) and b(1).

@qRoC
Copy link
Author

qRoC commented Dec 22, 2023

Possible safe solution (need validate):

Change Marking:

    public function mark(string $place): void
    {
        $this->places[$place] = ($this->places[$place] ?? 0) + 1;
    }

    public function unmark(string $place): void
    {
        $this->places[$place] = ($this->places[$place] ?? 0) - 1;

        if ($this->places[$place] == 0) {
            unset($this->places[$place]);
        }
    }

    public function toState(): void
    {
        foreach ($this->places as &$place) {
            $place = 1;
        }
    }

Add new argument toWorkflow:

    bool $tokenAsState = true

In apply, after do all approved transitions:

if ($this->tokenAsState) {
    $marking->toState();
}

This allows:

  • Safe parallel transitions (fix this issue) with saving current behaviour.
  • Work like Petri Net when tokenAsState is false.

@Nyholm
Copy link
Member

Nyholm commented Dec 22, 2023

Hm... This has indeed been the case since forever. Interesting.

Since we are building a list of "approved transitions" and then apply them one by one (and not as a unit) we will have this issue.

With your proposed solution, do you see how/if that would effect listeners to Transition('back2', 'c', 'b')?

@qRoC
Copy link
Author

qRoC commented Dec 22, 2023

Like Petri Nets (#53179 (comment)), but only in apply when tokenAsState=true

listeners must not be affected

@qRoC
Copy link
Author

qRoC commented Dec 22, 2023

Behaviour should change when we set tokenAsState=false.

For example we can use createWorkflowWithSameNameTransition (https://github.com/symfony/symfony/blob/7.0/src/Symfony/Component/Workflow/Tests/WorkflowBuilderTrait.php#L90):

//   +------------------------------------------------------------+
//   |                                                            |
//   |                                                            |
//   |         +----------------------------------------+         |
//   v         |                                        v         |
// +---+     +---------+     +---+     +--------+     +---+     +------+
// | a | --> | a_to_bc | --> | b | --> | b_to_c | --> | c | --> | to_a | -+
// +---+     +---------+     +---+     +--------+     +---+     +------+  |
//   ^                         |                                  ^       |
//   |                         +----------------------------------+       |
//   |                                                                    |
//   |                                                                    |
//   +--------------------------------------------------------------------+

// a: 1
// b: 0
// c: 0
$workflow->getMarking();

// a: 0
// b: 1
// c: 1
$workflow->apply('a_to_bc');

//   a: 1
//   b: 0
//   c: 1
// or
//   a: 1
//   b: 1
//   c: 0
//
//.a: 2
// b: 0
//.c: 0
$workflow->apply('to_a');

@qRoC
Copy link
Author

qRoC commented Dec 22, 2023

Alternatively - we can simply add a check for places in transitions with the same names in the WorkflowValidator :)

@HeahDude
Copy link
Contributor

It would not be a bug fix but a feature, since this was always intended this way, cf #22521 (comment).
And a very old proposal #20508.

I would be happy to have such feature though, it has been on my todo list for a long time :).

@lyrixx
Copy link
Member

lyrixx commented Feb 8, 2024

Hello, Sorry for the delay. I confirm this is something not expected... It's a current limitation of the workflow component as @HeahDude told previously.

I would be happy to have such feature though, it has been on my todo list for a long time :).

Me too. So here we go : #53865

lyrixx added a commit that referenced this issue Mar 11, 2024
…tokens (lyrixx)

This PR was merged into the 5.4 branch.

Discussion
----------

[Workflow]Fix Marking when it must contains more than one tokens

| Q             | A
| ------------- | ---
| Branch?       | 5.4
| Bug fix?      | yes
| New feature?  | no
| Deprecations? | no
| Issues        | Fix #53179
| License       | MIT

Commits
-------

02b54a1 [Workflow] Fix Marking when it must contains more than one tokens
@lyrixx lyrixx closed this as completed Mar 11, 2024
@lyrixx lyrixx reopened this Mar 12, 2024
@lyrixx
Copy link
Member

lyrixx commented Mar 12, 2024

The PR has been reverted. I'll send another one against 7.1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment