I haven’t read the book, and I didn’t know that methods send an updated
ddp message, but you got me curious.
First off I had a look at the websocket messages from the project I’m working on
⬆️ ["{\"msg\":\"method\",\"method\":\"logger.log\",\"params\":[\"debug\",\"(CLIENT-ID: kPAosM7sJcsuaXxm6) method modules.update called with args {\\\"id\\\":\\\"FSGyLdZ7YWEAo5ve8\\\",\\\"modifier\\\":{\\\"$set\\\":{\\\"dimensions\\\":{\\\"x\\\":41,\\\"y\\\":81,\\\"width\\\":763,\\\"height\\\":385}}}}\"],\"id\":\"40\"}"]
⬆️ ["{\"msg\":\"method\",\"method\":\"modules.update\",\"params\":[{\"id\":\"FSGyLdZ7YWEAo5ve8\",\"modifier\":{\"$set\":{\"dimensions\":{\"x\":41,\"y\":81,\"width\":763,\"height\":385}}}}],\"id\":\"41\"}"]
⬇️ a["{\"msg\":\"updated\",\"methods\":[\"40\"]}"]
⬇️ a["{\"msg\":\"result\",\"id\":\"40\"}"]
⬇️ a["{\"msg\":\"result\",\"id\":\"41\",\"result\":1}"]
⬇️ a["{\"msg\":\"changed\",\"collection\":\"modules\",\"id\":\"FSGyLdZ7YWEAo5ve8\",\"fields\":{\"dimensions\":{\"x\":41,\"y\":81,\"width\":763,\"height\":385},\"updatedAt\":{\"$date\":1581392648179}}}"]
⬇️ a["{\"msg\":\"updated\",\"methods\":[\"41\"]}"]
First we see two messages sent to the server:
- A log method call (id 40),
- Followed by a method call to modify the modules collection (id 41)
What’s interesting is the replies:
- We get an
updated
message for the log call (id 40)
- We get the
result
of the log call?
- We get the
result
of the update call (id 41, result is 1 - the number of changed documents returned from method code)
- We get a
changed
message with the changed fields of a subscribed document
- We get the
updated
message
Interesting that they seem to come in different orders!
- I suspect the log sends
updated
first because I call this.unblock
in the log method code
- Also interesting that the database change comes through before the
updated
message for 41
- I think in the case of multiple document changes as a result of a method, all the DDP events will be sent through before the
updated
message
Looking through the Meteor source, seems to validate these thoughts:
- Lets start with the
method
protocol handler
- There’s an interesting construct called a WriteFence
DDPServer._WriteFence
that keeps track of pending database operations, and the DDPServer
's protocol handler for methods waits until all ‘writes’ are complete before sending updated
. The comments are pretty explicit:
// set up to mark the method as satisfied once all observers
// (and subscriptions) have reacted to any writes that were
// done.
- The mongo driver marks writes on the current fence for insert/update/remove/dropCollection/dropDatabase calls.
- The oplog observer and polling observer both mark writes when a collection has changed and they need to wait to grab the result from the database and send the required DDP message
I couldn’t directly find why unblock
would send updated
immediately, but I assume it clears the current write, so more operations can continue out-of-order from the same session.
tl;dr: Updated lets the client know that all operations related to a (not-unblocked) method have completed and the next method can be executed
Thanks for the inspiration for a deep-dive