#015184: Output paths from Exclusive Choice randomly switch

Description:

The conditions for each path out of an exclusive choice node are frequently reversed when loading the workflow from a database or an XML file.


Environment:

Operating System: CentOS 5.4
PHP Version: (please be specific, like '4.4.3' or '5.1.5') 5.2.9
Database and version: Oracle 11g
Browser (and version): Firefox


Steps to Reproduce:

Using the following workflow I:
1. create the workflow in memory
2. save the workflow to a database (Oracle) and to an XML file
3. I load the workflow back in from the database and/or the XML file
4. Create graphs of the workflow as initially built, the workflow as read from memory, and the workflow as read from the XML file.
5. Compare the graphs and they are different. The XML file will show the same condition for both paths out of Exclusive Choice node (created using the if - else form). The database version will frequently but not always have some of the output nodes from an exclusive choice node switched.


<?php

$workflow = new ezcWorkflow($name);

        // Initial variables that are expected to be set by the calling workflow
        $nodeInputStart = new ezcWorkflowNodeInput(array('start_date'        => new ezcWorkflowConditionIsInteger,
                                                         'current_date'      => new ezcWorkflowConditionIsInteger,
                                                         'node_id'           => new ezcWorkflowConditionIsInteger,
                                                         'page_number_match' => new ezcWorkflowConditionIsString
                                                         )
                                                    );

        // Terminate processing branch. Start: nodeTermMerge, End: nodeTermEnd
        $nodeTermMerge = new ezcWorkflowNodeSimpleMerge();
        $nodeTermAck = new ezcWorkflowNodeInput(array('rescan_batch' => new ezcWorkflowConditionIsString));
        $nodeTermEnd = new ezcWorkflowNodeEnd();
        $nodeTermMerge->addOutNode($nodeTermAck);
        $nodeTermAck->addOutNode($nodeTermEnd);

        // Page_number_match branch. Start: startNode, End: nodePageOKTrue
        $nodePageOKDecide = new ezcWorkflowNodeExclusiveChoice();
        $nodePageOKTrue = new ezcWorkflowNodeSimpleMerge();
        $nodePageOKFalse = new ezcWorkflowNodeInput(array('pages_present' => new ezcWorkflowConditionIsInteger));
        $nodeCRFMissing = new ezcWorkflowNodeInput(array('crfs_missing' => new ezcWorkflowConditionIsString));
            // Connect the nodes
        $workflow->startNode->addOutNode($nodeInputStart);
        $nodeInputStart->addOutNode($nodePageOKDecide);

        $nodePageOKDecide->addConditionalOutNode(new ezcWorkflowConditionVariable
                                                        ( 'page_number_match',
                                                          new ezcWorkflowConditionIsEqual('Yes')
                                                        ),
                                                 $nodePageOKTrue,
                                                 $nodePageOKFalse);

        $nodePageOKFalse->addOutNode($nodeCRFMissing);
        $nodeCRFMissing->addOutNode($nodePageOKTrue);
        
        // Sufficient_contrast_branch. Start: nodeContrast, End: nodeContrastOKTrue
        $nodeContrast = new ezcWorkflowNodeInput(array('sufficient_contrast' => new ezcWorkflowConditionIsString));
        $nodeContrastOKDecide = new ezcWorkflowNodeExclusiveChoice();
        $nodeContrastOKTrue = new ezcWorkflowNodeSimpleMerge();
        $nodeContrastFailDecide = new ezcWorkflowNodeExclusiveChoice();
        $nodeContrastMissing = new ezcWorkflowNodeInput(array('crfs_bad_contrast' => new ezcWorkflowConditionIsString));
            // Connect the nodes
        $nodePageOKTrue->addOutNode($nodeContrast);
        $nodeContrast->addOutNode($nodeContrastOKDecide);
        $nodeContrastOKDecide->addConditionalOutNode(new ezcWorkflowConditionVariable
                                                        ( 'sufficient_contrast',
                                                          new ezcWorkflowConditionIsEqual('Yes')
                                                        ),
                                                 $nodeContrastOKTrue,
                                                 $nodeContrastFailDecide );

        $nodeContrastFailDecide->addConditionalOutNode(new ezcWorkflowConditionVariable
                                                        ( 'page_number_match',
                                                          new ezcWorkflowConditionIsEqual('Yes')
                                                        ),
                                                 $nodeContrastMissing,
                                                 $nodeTermMerge );

        $nodeContrastMissing->addOutNode($nodeContrastOKTrue);

        // Data present batch: Start: nodeData, End: nodeDataOKTrue
        $nodeData = new ezcWorkflowNodeInput(array('sufficient_data' => new ezcWorkflowConditionIsString));
        $nodeDataOKDecide = new ezcWorkflowNodeExclusiveChoice();
        $nodeDataOKTrue = new ezcWorkflowNodeSimpleMerge();
        $nodeDataFailDecideA = new ezcWorkflowNodeExclusiveChoice();
        $nodeDataFailDecideB = new ezcWorkflowNodeExclusiveChoice();
        $nodeDataMissing = new ezcWorkflowNodeInput(array('crfs_bad_data' => new ezcWorkflowConditionIsString));
            // Connect the nodes
        $nodeContrastOKTrue->addOutNode($nodeData);
        $nodeData->addOutNode($nodeDataOKDecide);
        $nodeDataOKDecide->addConditionalOutNode(new ezcWorkflowConditionVariable
                                                        ( 'sufficient_data',
                                                          new ezcWorkflowConditionIsEqual('Yes')
                                                        ),
                                                 $nodeDataOKTrue,
                                                 $nodeDataFailDecideA );

        $nodeDataFailDecideA->addConditionalOutNode(new ezcWorkflowConditionVariable
                                                        ( 'page_number_match',
                                                          new ezcWorkflowConditionIsEqual('Yes')
                                                        ),
                                                 $nodeDataFailDecideB,
                                                 $nodeTermMerge );

        $nodeDataFailDecideB->addConditionalOutNode(new ezcWorkflowConditionVariable
                                                        ( 'sufficient_contrast',
                                                          new ezcWorkflowConditionIsEqual('Yes')
                                                        ),
                                                 $nodeDataMissing,
                                                 $nodeTermMerge );

        $nodeDataMissing->addOutNode($nodeDataOKTrue);
        
        // Add in the final termination node
        $nodeTermOK = new ezcWorkflowNodeEnd();
        $nodeTermOK->addInNode($nodeDataOKTrue);
?>

- Attachments
ScanFlow-LoadedDB.png (182.9 kb)
[Download] [Permanent Link]
Graph created from the workflow loaded from the database.
ScanFlow-Created.png (183.2 kb)
[Download] [Permanent Link]
The image created after creating the workflow.

- Comments

The issue was traced to the manner in which the workflow was saved and loaded. As the workflow is being saved entries are inserted into the table node_connection in the order of the connections and, when loaded, are read in the presumed order in which they were inserted. On Oracle, the order of entries returned from a query is not guaranteed to be in the same order they were inserted. I don't know if this is also true in MySql.

The resolution was to add a column to the node_connection table that stored the order in which rows were inserted so they could then be read back in order.

#262370 by Greg Strylewicz on July 26th, 2009 [Permanent Link]

Fixed in r10853.

I was not able to reproduce the problem with MySQL and SQLite. However, "messing" with the order of the result rows from node_connection exposed the fact that the code for loading a workflow definition from the database somehow depends on the correct order.

I implemented the workaround proposed by the original reporter for now.

#262879 by Sebastian Bergmann on September 1st, 2009 [Permanent Link]

- History
Properties
Type Bug
Priority Medium
Component Components » Workflow
Affects Unknown
Fix Version 2009.2 sprint 2 - eZ components 2009.2 sprint 2
Reporter Greg Strylewicz
Responsible Sebastian Bergmann
Status 0 Closed
Resolution Fixed
Created July 25th, 2009
Updated September 1st, 2009
Resolved September 1st, 2009
 
Navigation [Permanent Link]
Previous Issue
Back to Issues List
Next Issue: #015537
  Graph shows to small and truncated rotated axis labels