c# - SqlWorkflowInstanceStore WaitForEvents returns HasRunnableWorkflowEvent but LoadRunnableInstance fails -
dears
please me restoring delayed (and persisted) workflows.
i'm trying check on self-hosted workflow store there instance delayed , can resumed. testing purposes i've created dummy activity delayed , persists on delay.
generally resume process looks like:
get wf definition configure sql instance store call waitforevents there event hasrunnableworkflowevent.value name , if create workflowapplication object , execute loadrunnableinstance method
it works fine if store created|initialized
, waitforevents
called, store closed. in such case store reads available workflows persisted db , throws timeout exception if there no workflows
available resume.
the problem happens if store created , loop started waitforevents
(the same thing happens beginwaitforevents
). in such case reads available workflows
db
(with proper ids) instead of timeout exception
going read 1 more instance (i know how many workflows
there ready resumed because using separate test database
). fails read , throws instancenotreadyexception
. in catch
i'm checking workflowapplication.id
, not saved test before.
i've tried run on new (empty) persistent database , result same :(
this code fails:
using (var storewrapper = new storewrapper(wf, connstr)) (int q = 0; q < 5; q++) { var id = resume(storewrapper); // instancenotreadyexception here when activities resumed
but 1 works expected:
for (int q = 0; q < 5; q++) using (var storewrapper = new storewrapper(wf, connstr)) { var id = resume(storewrapper); // timeout exception here or beginwaitforevents continues wait
what best solution in such case? add empty catch
instancenotreadyexception
, ignore it?
here tests
const int delaytime = 15; string connstr = "server=db;database=appfabricdb_test;integrated security=true;"; [testmethod] public void persistoneonidleandresume() { var wf = getdelayactivity(); using (var storewrapper = new storewrapper(wf, connstr)) { var id = createandrun(storewrapper); trace.writeline(string.format("done {0}", id)); } using (var storewrapper = new storewrapper(wf, connstr)) (int q = 0; q < 5; q++) { var id = resume(storewrapper); trace.writeline(string.format("resumed {0}", id)); } } activity getdelayactivity(string addname = "") { var name = new variable<string>(string.format("incr{0}", addname)); activity wf = new sequence { displayname = "testdelayactivity", variables = { name, new variable<string>("customdatacontext") }, activities = { new writeline { text = string.format("before delay {0}", delaytime) }, new delay { duration = new inargument<timespan>(new timespan(0, 0, delaytime)) }, new writeline { text = "after delay" } } }; return wf; } guid createandrun(storewrapper sw) { var idleevent = new autoresetevent(false); var wfapp = sw.getapplication(); wfapp.idle = e => idleevent.set(); wfapp.aborted = e => idleevent.set(); wfapp.completed = e => idleevent.set(); wfapp.run(); idleevent.waitone(40 * 1000); var res = wfapp.id; wfapp.unload(); return res; } guid resume(storewrapper sw) { var res = guid.empty; var events = sw.getstore().waitforevents(sw.handle, new timespan(0, 0, delaytime)); if (events.any(e => e.equals(hasrunnableworkflowevent.value))) { var idleevent = new autoresetevent(false); var obj = sw.getapplication(); try { obj.loadrunnableinstance(); //instancenotready here if same store has read instances db , no delayed left obj.idle = e => idleevent.set(); obj.completed = e => idleevent.set(); obj.run(); idleevent.waitone(40 * 1000); res = obj.id; obj.unload(); } catch (instancenotreadyexception) { trace.traceerror("failed resume {0} {1} {2}", obj.id , obj.definitionidentity == null ? null : obj.definitionidentity.name , obj.definitionidentity == null ? null : obj.definitionidentity.version); foreach (var e in events) { trace.tracewarning("event {0}", e.name); } throw; } } return res; }
here store wrapper definition i'm using test:
public class storewrapper : idisposable { activity wfdefinition { get; set; } public static readonly xname workflowhosttypepropertyname = xnamespace.get("urn:schemas-microsoft-com:system.activities/4.0/properties").getname("workflowhosttype"); public storewrapper(activity wfdefinition, string connectionstr) { _store = new sqlworkflowinstancestore(connectionstr); hosttypename = xname.get(wfdefinition.displayname, "ttt.workflow"); wfdefinition = wfdefinition; } sqlworkflowinstancestore _store; public sqlworkflowinstancestore getstore() { if (handle == null) { initstore(_store, wfdefinition); handle = _store.createinstancehandle(); var view = _store.execute(handle, new createworkflowownercommand { instanceownermetadata = { { workflowhosttypepropertyname, new instancevalue(hosttypename) } } }, timespan.fromseconds(30)); _store.defaultinstanceowner = view.instanceowner; //trace.writeline(string.format("{0} owns {1}", view.instanceowner.instanceownerid, hosttypename)); } return _store; } protected virtual void initstore(sqlworkflowinstancestore store, activity wfdefinition) { } public instancehandle handle { get; protected set; } xname hosttypename { get; set; } public void dispose() { if (handle != null) { var deleteowner = new deleteworkflowownercommand(); //trace.writeline(string.format("{0} frees {1}", store.defaultinstanceowner.instanceownerid, hosttypename)); _store.execute(handle, deleteowner, timespan.fromseconds(30)); handle.free(); handle = null; _store = null; } } public workflowapplication getapplication() { var wfapp = new workflowapplication(wfdefinition); wfapp.instancestore = getstore(); wfapp.persistableidle = e => persistableidleaction.persist; dictionary<xname, object> wfscope = new dictionary<xname, object> { { workflowhosttypepropertyname, hosttypename } }; wfapp.addinitialinstancevalues(wfscope); return wfapp; } }
i'm not workflow foundation expert answer based on official examples microsoft. first 1 wf4 host resumes delayed workflow (cswf4longrunninghost) , second microsoft.samples.absolutedelay. in both samples find code similar yours i.e.:
try { wfapp.loadrunnableinstance(); ... } catch (instancenotreadyexception) { //some logging }
taking account answer right , empty catch instancenotreadyexception
solution.
Comments
Post a Comment