• Meteor

Using the Write Fence

From the class:  RPC with Meteor Methods

In the last video, we looked at how a method on the server can make some writes to the Mongo database. So a write could be an insert, or an update, or a remove. And the way that it tells the client that all of these writes are complete, and the client should consider that it has all of the most up-to-date data, is it sends a message called the Updated message.

In this video, we're going to look at a data structure called the write fence, which allows the server to do this. The write fence is located in the Livedata package in the write fence file. A write fence just allows us to collect some writes, and when all of the writes have completed, to fire a callback. And in that callback we can do whatever we want. But Meteor Methods uses the callback to send this updated message.

I think the best way to get a feel for the write fence is just to play around with it. So let's jump over to some application code.

OK. This is the same code that we were using last time. On the left, up top, I've got the client code. And on the bottom, in the left side, I've got the server code. And that's where I've defined this Meteor method. So there's no client stub for this Meteor method. And so far it just returns a result which is the string My Document ID.

And on the client, we have a callback for on result received, and another callback for when we have the result and the updated message. And we print the corresponding messages in the debug console on the right.

I've popped open the network console so we can take a look at the DDP messages also. In this example, just like last time, once we make the method call here, we get the updated message and the result message almost at the same time. And that's because we're not actually making any writes on the server using the write fence to block sending this updated message.

So to start us off here, let's get a reference to the write fence, and start playing with it inside of our method. To get access to the fence, or the write fence, we're going to create a variable called Fence. And we can get access to the write fence by using a variable that's off of DDP server called CurrentWriteFence. And this is a dynamic environment variable. And you can learn more about that in a feed video about dynamic variables. But basically, this is going to allow us to get the current value of the write fence for this method.

Now once we have the fence, we can call a method on the fence called Begin Write. And this tells the fence that we're going to start a write process, and that write might finish at some later time. And so don't call the callback quite yet, because we have an outstanding write.

And what this method will return is an object that we can later call Committed On. And that tells the write fence that the write has completed. And we can call that committed method at any later time that we want.

So to start us off, let's not call the committed method at all. Instead, let's just block the write fence by calling the Begin Write method. When I press Save in the write, you'll see that we get a result. But our final callback is never called. And that's because we never got an updated message from the server, because there's still an outstanding write that we created by calling the Begin Write method on the write fence.

And in case you're skeptical, we can click on WebSockets again and check on frames. And you'll see that there's no updated message that has been sent.

So here's the method call here. And then we get a result. But there's no updated message anywhere else up here. And once again, that's because we have this outstanding write on the write fence.

Now to fix this, we can call the Committed method on the returned write object. And this will tell the write fence, OK, we've made the write. It's committed. So you can go ahead and call the callback for the write fence, which will send to this updated message. So let me press Save here. And this should fix the problem. And we should get now our final callback getting called.

And if I click on Web sockets and take a look at the frames again, this time around we make the method call, and we get back an updated message and a result. And then the callback on the client gets called, because we have both a result and the updated data.

Now if we look at the result and updated messages in DDP, we get those at about the same time. So if you can look at the seconds here, you'll see that they get sent at about the same time.

But that's not always the case. We might make a write to Mongo that takes a couple of seconds, or even a couple of milliseconds. And in that case the commit happens at some later time. And we may have made multiple writes to Mongo, in which case there has to be multiple writes that get committed.

So let's simulate that by introducing some time delay. I'll call set timeout of JavaScript. We don't do anything fancy here with Meteor. And I'll set a timeout for five seconds later. And inside of that timeout is where I'll put the committed-- I'll make the committed method call.

So what we're doing here is just simulating the idea that the write could happen at a later time. And when we get that result back from Mongo, is when we'll call this committed method, telling the write fence that we're done.

I'll press Save. And notice we don't get the updated message down in the bottom right away. So we get a result almost immediately. And then five seconds later we print out that we have the result and the updated data.

In the DDP messages, notice that we send the method call at about 5:32, 53 And we get a result, almost right away. So at 5:32, 53. And then five seconds go by. And then we finally get this updated message a little bit later.

Now let's add another write to the write fence to simulate making multiple updates or inserts or removes to the Mongo database. So I'll call this First Write.

And then I'll create a second variable called Second Write. And we'll make another write. And just to start us off here, I won't actually commit the second write. So that will simulate the second write never actually completing. And once again, over in the right, our final callback never gets called, because we never get the updated message.

So here's our result. But we get no updated message. Just a method and a result. So what we need to do is to come over and to commit this second write.

And let's simulate that write taking just a little bit longer than the first one. So I'll set another time out. And we'll say this happens at about seven seconds. So about two seconds after the first one.

And I'll press Save. And we get our result right away. And five seconds go by, and we still don't get an updated message. But then seven seconds go by, and we get our final, updated message.

So here's our method call at 5:35:17. And we get the result almost right away. And then the updated message we get about seven seconds later.

And the reason we get that updated message seven seconds later is because that's how long it took for both writes-- the first write and the second write-- to complete. So the first write finishes in about five seconds. But then we have to wait another two seconds for the second write to finish. And once both have been committed, the write fence says OK, I'm done now with all the writes, and so I can call my callback.

Let's jump over and actually look at the Livedata code for Meteor Methods that sends the updated message. OK. I've taken us over to the Meteor source code, and I'm looking at the Livedata package in the Livedata server file.

And on the right, we're looking at the method function. And what this does is handles the DDP method message off of the wire. And so this is the function that will get called on the server to handle our method message.

One of the things you'll notice is it creates a new write fence, and then calls this function on all committed. And so when we've called committed on all the writes on the fence, that's when this function will get called.

And what it does is it calls this retire method on the fence, which prevents further writes, and then sends this DDP message called Updated, with the methods that it applies to. And so this is the source code that should show you why this updated message gets sent when we've written or committed all of the writes on the write fence.

You can learn more and study more about the write fence by going to the Livedata package and taking a look at the source file. But hopefully this video gave you more intuition about how the server is able to tell the client when all of the asynchronous writes to Mongo have been completed. And the way that it does that is by using this data structure called a write fence, which just collects a group of writes and then allows us to call a callback when all of those writes have been marked as committed.