An interesting question. After a week with no answers, I’ll have a go.
In the world of transactional processing, some language-systems have evolved to permit multiple-transaction sequences with a commit/rollback command that either confirms all transactions in the sequence group, or reverses any already processed back to the starting point. The classic example is booking a flight, an hotel, and a rental car - you either want all to succeed or none.
Node-RED is an event-based language and does not have such features built-in. As far as I understand it, Node-RED (back end, not the editor) operates as a single-thread on one core, hence only one thing is ever done at any one point in time. NR now runs pseudo concurrency, so that two messages active in one sequence (flow) will be processed together node by node rather than one message running to conclusion first. The actual node processing is run by a task manager with a queue, such that the next node in the queue is given the input message and left to process (execute) until the node completes and the output message is then put back onto the queue, allocated for the next node in the sequence.
So, at the level of the Node-RED process engine, cancelling any in-flight work would require asking Node-RED to remove specific queue items from the work queue. I agree that ‘Deploy’ can wipe the queue clean, and this works for either all flows or just one flow. Since Node-RED has back-end API calls, including GET and SET flows/state, it may indeed be possible to send an API call to ‘stop’ all/one flow.
I do not believe that there is an easy way to execute such a ‘break’ in an NR flow, and forcing a hard-stop of a flow from within the flow itself will probably cause the entire flow to stop, including the post-stop processing required. The Catch node only operates on the same flow, so stopping the entire flow would close all sequences including the Catch node. The alternative is to build the ‘work queue’ inside a flow and manage that. Node-RED certainly has the tools to queue messages.
As long as you are single-point processing the work, then a single queue is already there. The Delay node can either delay for a given period of time, or rate-limit as x messages in a period. Both can be used to queue any backlog, and there are several input-flags that can be used to manipulate the queue.
The msg.reset option is probably the one you are interested in. Adding in a new or existing message with the field ‘reset’ set to anything will cause the node to discard any queued messages (and the ‘reset’ input message). I use this quite a lot with queued tasks to cancel pending work in a queue, which can be either a data working message or a semaphore message.
There is also the option for ‘flush’ and ‘toFront’ permitting more advanced queue management. Flush is useful for example in the Join node to end a manual join on a given trigger.
msg.reset is actually used across several queuing nodes - delay, trigger, join, rbe (report by exception).
A test-bed flow:
[{"id":"ef225326f9d90494","type":"delay","z":"fbaea1aaab14874b","name":"","pauseType":"delay","timeout":"1","timeoutUnits":"days","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":620,"y":400,"wires":[["b5bce079c5c8a642"]]},{"id":"7e0ac074b6f99299","type":"inject","z":"fbaea1aaab14874b","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[1..6].(\"M\" & $)","payloadType":"jsonata","x":150,"y":320,"wires":[["df5467c8c9eee08d"]]},{"id":"df5467c8c9eee08d","type":"split","z":"fbaea1aaab14874b","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","property":"payload","x":290,"y":320,"wires":[["ed41d3d3a5af0991"]]},{"id":"a3f0d061b1386a6c","type":"inject","z":"fbaea1aaab14874b","name":"reset","props":[{"p":"reset","v":"yes please","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":330,"y":380,"wires":[["ef225326f9d90494"]]},{"id":"34b1d53d8ac98ecd","type":"inject","z":"fbaea1aaab14874b","name":"flush 1","props":[{"p":"flush","v":"1","vt":"num"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":330,"y":420,"wires":[["ef225326f9d90494"]]},{"id":"b5bce079c5c8a642","type":"debug","z":"fbaea1aaab14874b","name":"debug 518","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":850,"y":400,"wires":[]},{"id":"2f15020007292266","type":"inject","z":"fbaea1aaab14874b","name":"flush all","props":[{"p":"flush","v":"true","vt":"bool"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":330,"y":460,"wires":[["ef225326f9d90494"]]},{"id":"ed41d3d3a5af0991","type":"change","z":"fbaea1aaab14874b","name":"tidy","rules":[{"t":"delete","p":"parts","pt":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":410,"y":320,"wires":[["ef225326f9d90494","9df2dc75ab140164"]]},{"id":"6b9ddad0908832b3","type":"inject","z":"fbaea1aaab14874b","name":"to front","props":[{"p":"toFront","v":"true","vt":"bool"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":330,"y":600,"wires":[["9df2dc75ab140164"]]},{"id":"9df2dc75ab140164","type":"delay","z":"fbaea1aaab14874b","name":"","pauseType":"rate","timeout":"1","timeoutUnits":"days","rate":"1","nbRateUnits":"1","rateUnits":"day","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":630,"y":520,"wires":[["895d47e4336682eb"]]},{"id":"895d47e4336682eb","type":"debug","z":"fbaea1aaab14874b","name":"debug 519","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":850,"y":520,"wires":[]},{"id":"801e361105b5762f","type":"inject","z":"fbaea1aaab14874b","name":"to front & flush 1","props":[{"p":"toFront","v":"true","vt":"bool"},{"p":"flush","v":"1","vt":"num"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":360,"y":640,"wires":[["9df2dc75ab140164"]]},{"id":"99d8ca0477f8de7b","type":"inject","z":"fbaea1aaab14874b","name":"reset","props":[{"p":"reset","v":"yes please","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":330,"y":520,"wires":[["9df2dc75ab140164"]]},{"id":"bd341784b4f66e8f","type":"inject","z":"fbaea1aaab14874b","name":"flush 1","props":[{"p":"flush","v":"1","vt":"num"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":330,"y":560,"wires":[["9df2dc75ab140164"]]}]
In any computer language where the options are not already available in either code or operating system, I find that you end up having to write the stuff yourself…