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