Hi, I'm interested in using Fulcro Statecharts to manage a long-running flow in a Datastar webpage. I've done all my experimentation with a setup like the one in the guide; using a simple env and the core.async event loop. I have some questions:
Question 1
I see that completed statechart sessions (those with :com.fulcrologic.statecharts/running? = false) still have an entry in the :com.fulcrologic.statecharts/event-queue. Is this something that is eventually cleaned up?
Question 2
[edit: I was able to figure this one out]
I need to clean up abandoned sessions - those that we know won't get any new events or which haven't gotten any events for a long time. I see three ways to do this, but I can't make option 3 work:
1. Keep track of a "user sessions" -> "statechart sessions" mapping and clean up statechart sessions when cleaning up user sessions. Basically a manual approach.
2. Use a custom InvocationProcessor, forward events to it, and inside the InvocationProcessor, reset a timer whenever getting an event. If the timer expires, send an "abandoned" event back to the statechart to make it transition to a final state.
3. Like the traffic light example, have a parallel :timer state. Unlike the traffic light example, this state listens for all events and exits and re-enters itself in response. Here's the part I can't make work: Using an invoke of a future or send with a delay, send the :abandoned event if the :timer state has survived longer than the timeout window without an exit/enter cycle. e.g:
(sce/invoke {:type :future
:params (fn [env data-model] {:env env})
:src (fn [{:keys [env]}]
(Thread/sleep 5000)
(let [session-id (env/session-id env)
event {:target session-id
:event :abandon}]
(simple/send! env event)))})
or
(sce/on-entry {}
(sce/send {:event :abandon
:delay 5000}))
but with both of these, it seems like exiting and re-entering the state doesn't cancel the invoke/send. Is something like that supported?
Question 3
I want to be able to react to any change to the configuration or data model of a session (basically just want to trigger a new SSE "patch elements" event). Is there an API for "tell me when this session changes"?What you need to do is implement the protocols of the system and design them to react the way you want. The send primitive does not Auto cancel. An event queue that sees an event being targeted at a non-existent session should just silently drop the event. If you want an event canceled to do an exit, then simply add it on exit handler with a cancel primitive. The state chart is driven essentially by the external event queue. So if you would implement your own external event queue, then you can trigger whatever you want as you process the events. In terms of triggering on internal State transition, EG as the state chart is trying to stabilize internally, that doesn't have specific instrumentation points, but the algorithm is plugable, so you could actually make a copy of the algorithm, instrumented as you see fit, and use that. I personally have not needed anything beyond responding to external events.
100% of the reason that I parameterized the library so heavily with protocols, was exactly because I wanted people to be able to morph the library to work the way they wanted while still also having a semantically solid, and complete implementation of state chart interpretation. In my daily use, I typically write a custom external event queue. As a minimum. You can of course use the simple one that's provided with core async support, but I never intended that one to be any more than kind of a demo of how you would go about leveraging the library.
The execution model, for example, would allow you to have users that could specify a state chart from a user interface, and plug in JavaScript expressions as strings. Or use sci to allow the same thing with stringified clojure expressions. Or perhaps you want to store state charts in a database where you need to be able to serialize the expressions. Etc
I have server side protocol implementations that do things like create an SQL based external event queue, and working storage, so that my state chart runtime state can live for long periods of time, been through server reboots, and also run the charts via a cluster of nodes. I use this for things like subscription management, where I want some timed event to fire a long time from now, like a month, or even a year.
And I can also set up that cluster to have many threads that can run state chart events in parallel, of course, as long as they're not the same session ID. This allows me to have as many simultaneous state charts running in my cluster, across tens of thousands of user sessions.
Again, in that case, it's mainly about long-running charts that need to survive across a cluster of nodes and a long period of time