-
Notifications
You must be signed in to change notification settings - Fork 1
Design: Node Messaging API
The current mechanism for a node to handle messages is as follows:
- a handler is registered for the
input
event - within that handler a node can call
this.send()
as many times as it wants - if an error is hit, it can call
this.error(err,msg)
var node = this;
this.on('input', function(msg) {
// do something with 'msg'
if (!err) {
node.send(msg);
} else {
node.error(err,msg);
}
});
This simple system has a few limitations that we want to address:
- we cannot correlate a message being received with one being sent - particularly when there is async code involved in the handler
- we do not know if a node has finished processing a received message
- due to 1 & 2, we cannot build any timeout feature into the node
- we know of a use case where an embedder of node-red requires nodes to be strictly one-in, one-out, and that should be policed by the runtime. Putting aside the specifics, we cannot provide any such mode of operation with the current model
This design note explores how this mechanism could be updated to satisfy these limitations.
The original draft of this node set out three options. Through the process of writing the options, I've chosen my preferred approach. I have left the other two options at the bottom of the note for reference.
If the event handler is registered with three arguments, the runtime will pass in functions that should be used to send or mark the msg as handled.
this.on('input', function(msg, send, done) {
// do something with 'msg'
if (!err) {
// send can be called as many time as needed (including not at all)
send(msg);
send(msg);
send(msg);
// Once complete, done is called
done();
} else {
// If an error occurs, call done providing the error.
done(err);
}
});
The done
function takes two arguments: done(error, message)
.
Usage | Meaning |
---|---|
done() |
success. Any success node targeting this node will be triggered using the original msg |
done(null,msg) |
success. Any success node targeting this node will be triggered using the provided msg |
done(err) |
failure. Any catch node targeting this node will be triggered using the original msg |
done(err,msg) |
failure. Any catch node targeting this node will be triggered using the provided msg |
-
a new node will be added to compliment the
Catch
node that can be used to trigger a flow when a node finished processing a message. It's current name is theSuccess
node - but it needs to change. -
this feels the most 'node.js-like'. The presence of a
done
callback is familiar to many apis. -
the functions can be scoped to the received message so the node does not need to provide the message back
-
the runtime can tell if the handler expects these extra arguments or not, so can adapt its behaviour to match
-
node.send
should not be used in this case as its use will stop the runtime from being able to correlate message received with message sent. We probably won't enforce this - tbd.(HN) With current proposal, do we need correlation of
node.send
and received message? In addition, how to deal with N-input, M-output node such asswitch
,join
, etc.?(HN) In the case
send
anddone
is not specified (i.e. original form of event handler definition), I thinkdone
is implicitly called after event handler completed. - needs discussion -
node.error
can still be used as a handler may need to log multiple errors before completing.
What if done
is never called? - If a handler is registered that takes the send
and done
arguments, the runtime requires it to eventually call done
for each message received. Not calling done
should be considered a bug with the node implementation. The question is what happens if it doesn't get called.
The easy option is to do nothing. But that will allow buggy implementations to exist, so we should avoid this option.
The right approach will be to timeout the function. A timeout would be considered an error and logged as such. The runtime will set a default timeout of 30 seconds (TBD). A node will be able to set its own timeout value by setting a property on itself (this.TIMEOUT = 60000
(TBD)). This also allows a future extension where a user can set custom timeout values per node in the editor (but this proposal does not extend that far today).
(HN) As described above, I want to make
done
is implecitly after completion of event handler if callback parameter is not specified.(HN) I think fixed timeout value sometimes causes problem such as unstable behavior. So the timeout value should be inifite by default and make user or node developer can specify timeout value. May be specified in
catch
node (may need new API from receiver to sender) ?
What if a node that has been timed out then wakes up and calls done
or send
? - should the runtime then block a timed out node from calling send
or done
(at least... prevent any messages it then sends from being passed on? I can see use cases for both allowing the message to pass on and for stopping it. Does this need to be a per-node policy? Or a choice made in the editor? Hmmm.
(HN) Correct behavior depends on each use-case. So I think we catch timeout by
catch
node and handle in each way.(HN) Do we need to handle successfull completion of
success
node? Or should it be excluded from source node? Any other candidates?
The first proposal is to add a new function to the Node object that can be called when a node has finished handling a message.
this.on('input', function(msg) {
// do something with 'msg'
if (!err) {
node.send(msg);
} else {
node.error(err,msg);
}
node.done(null,msg);
});
- this relies on the user passing msg through - something that could be a source of programming error.
- it would need clear semantics over when it was called and how it relates to
node.error
.
The second proposal is similar to the first, but the complete
function can also be used to indicate a failure:
this.on('input', function(msg) {
// do something with 'msg'
if (!err) {
node.send(msg);
node.complete(msg, null, msg);
} else {
// Log the error, but don't provide the msg obj here
node.error(err);
// Provide the err - which will trigger any Catch nodes
node.complete(msg,err);
}
});
- this relies on the user passing msg through - something that could be a source of programming error.
- it would need clear semantics over when it was called and how it relates to
node.error
.