processi

about processes and engines

ruote, process state and tags

A : “What state is this process in ?”
B : “Well, it’s running”
A : “I know, that’s not what I meant. Where is it now ?”
B : “Let’s check which participants have workitems (tasks) about this process now”
A : “No, I mean, the document is it still being reviewed ? What’s its state ?”
B : “Ah you mean, the document state, not the process state ?”
A : “Is there a difference ?”
B : “Well, if a business process deals with the state of multiple resources, you can’t equate process state and resource state”

Initially, for ruote (a ruby workflow engine), the “state of a process” was limited to “the set of visible workitems for that process”.

Ruote.process_definition do
  cursor do
    production
    concurrence do
      qa1
      qa2
    end
    rewind :unless => '${qa_ok}'
    packaging
    delivery
  end
end

For this process, the states are “nil” (not running), “production”, “qa1” and/or “qa2”, “delivery”, and “packaging”.

(note that there is no “terminated” state, since, out of the box, ruote doesn’t do ‘process archiving’ for you).

Asking all the participants about the workitems could get expensive, especially if they’re remote participants. It’s easier to ask the engine :

p engine.process(wfid)
  # =>
  # == Ruote::ProcessStatus ==
  #    expressions : 3
  #      0!69176db85a0651e7a8d8a16426bd93df!20110107-betesuguto : define {}
  #      0_0!be7ac163b2c6ba47d6d4b24bbb83fd8c!20110107-betesuguto : cursor {}
  #      0_0_0!3b19bdf68a953597969bac507229bcf1!20110107-betesuguto : participant {"ref"=>"production"}
  #    schedules : 0
  #    stored workitems : 1
  #    variables :     {}
  #    all_variables : {"0!69176db85a0651e7a8d8a16426bd93df!20110107-betesuguto"=>{}}
  #    errors : 0

p engine.process(wfid).position
  # =>
  # [
  #   [ "0_0_0!3b19bdf68a953597969bac507229bcf1!20110107-betesuguto",
  #     "production",
  #     {} ] ]

(full gist at https://gist.github.com/769362)

What if the “state”, business-wise, has a different granularity than the simple one we derive from the participants ? Something more like a “stage”.

The “tag” attribute could help :

Ruote.process_definition do
  cursor do
    sequence :tag => 'production-stage' do
      production
      concurrence do
        qa1
        qa2
      end
    end
    rewind :unless => '${qa_ok}'
    sequence :tag => 'delivery-stage' do
      packaging
      delivery
    end
  end
end

Our process [instance] can be in “production-stage” or “delivery-stage” (or nowhere).

We can then ask for the tags of a process status :

p engine.process(wfid).tags
  # =>
  #  { "production-stage" => {
  #      "engine_id" => "engine",
  #      "wfid" => "20110107-hopakeze",
  #      "subid" => "0f62bbb3a0a19411aaef1524ebde657c",
  #      "expid" => "0_0_0" } }

(full gist at https://gist.github.com/769371)

Tags were originally meant to be used in conjunction with the undo/cancel and the redo expressions.

They later were used within cursors for jumps (along with participant names) and when “piloting” a cursor from outside.

Asking the engine about the ‘process state’ / ‘stage’ is fine, but what if a [remote] participant wants to know about the stage is in ?

Let’s add a ‘qa_stage’ tag.

Ruote.define do
  cursor do
    sequence :tag => 'production-stage' do
      production
      concurrence :tag => 'qa_stage' do
        qa1
        qa2
      end
    end
    rewind :unless => '${qa_ok}'
    sequence :tag => 'delivery-stage' do
      packaging
      delivery
    end
  end
end

The upcoming ruote 2.1.12 adds a #tags method to its workitems (a shortcut for workitem.fields[‘__tags__’]).

With a participant implementation that looks like

class MyParticipant
  include Ruote::LocalParticipant

  def consume(workitem)
    p [ workitem.participant_name, :tags, workitem.tags ]
    workitem.fields['qa_ok'] = true
    reply_to_engine(workitem)
  end
end

a run of our process will output

["production", :tags, ["production-stage"]]
["qa1", :tags, ["production-stage", "qa_stage"]]
["qa2", :tags, ["production-stage", "qa_stage"]]
["packaging", :tags, ["delivery-stage"]]
["delivery", :tags, ["delivery-stage"]]

(full gist at https://gist.github.com/769406)

There is a difference between asking the engine for the tags of a process intance and the list of tags returned by workitem#tags. The former returns all the tags currently active for the process instance, while the latter returns the list of tags that were traversed to reach the participant expression that emitted the workitem.

Another novelty brought in by ruote 2.1.12 is the possibility to listen to tag events (entering and leaving tags).

Until now, the http://ruote.rubyforge.org/exp/listen.html expression could only listen to participants (workitems reaching and returning from participants). The upcoming ruote 2.1.12 lets us listen to process instances entering and leaving tags.

For example, we could have a monitoring process that listen to process instances entering the ‘final-stage’ tag. Each time it happens, the participant ‘supervisor’ receives a copy of the workitem :

Ruote.process_definition do
  listen :to => 'final-stage', :upon => 'entering' do
    participant 'supervisor'
  end
end

By setting the :wfid attribute of the listen expression to true, we can limit the listening to the process instance to which the listen expression belongs.

It can be useful to approximate the ‘milestone’ workflow pattern :

Ruote.define do

  concurrence :count => 1 do
    # will terminate as soon as 1 branch replies (and cancel the other)

    sequence do
      participant 'a'
      participant 'b', :tag => 'milestone'
      participant 'c'
    end

    listen :to => 'milestone', :upon => 'entering', :wfid => true do

      concurrence :count => 1 do
        # will terminate as soon as 1 branch replies (and cancel the other)

        listen :to => 'milestone', :upon => 'leaving', :wfid => true
          # as soon as the tag 'milestone' is left, this listen will
          # exit (reply to the parent concurrence) and d will get cancelled

        participant 'd'
      end
    end
  end
end

The participant ‘d’ will only receive a workitem when the milestone is reached. As soon as the milestone is left, the workitem of participant ‘d’ is cancelled (removed from him).

Note how this implementation relies on “concurrence :count => 1”, a concurrence that exits as soon as 1 of its branches replies (and cancels the remaining branches).

In conclusion, tags have multiple uses, designating process state / stages, letting undo / redo segments of processes. The next ruote (2.1.12) adds tag information in workitems and lets the listen expression observe tag events (entering or leaving a tag).

 

* web : http://ruote.rubyforge.org
* source : http://github.com/jmettraux
* mailing list : http://groups.google.com/group/openwferu-users
* irc : freenode.net #ruote

 

Written by John Mettraux

January 7, 2011 at 1:24 pm

Posted in bpm, openwferu, ruby, ruote, workflow