Latency Compensation Algorithm Specifics?

316 views
Skip to first unread message

liron00

unread,
Oct 1, 2012, 8:43:37 PM10/1/12
to meteo...@googlegroups.com, Jonathan Lerner

Meteor team,

What exactly is the designed behavior of Latency Compensation?

For example, say I run db.users.update({favColor: "blue"}, {$set: {favColor: "green"}}) on the client which changes the favColor value blue->green on a matching user document. But on the server, the same document had already been updated by another client to have favColor:"red", so the blue->green $set is a no-op on the server.

What messages (presumably DDP messages?) does the server send the client in order to correct the client's document? How does the server know what work the client mistakenly did? Does the client tell the server what effect the update had client-side? Does the client set a timeout and if the server hasn't confirmed its updates then it rolls them back?

I would love to have an explanation of the Latency Compensation algorithm in some detail because as previously mentioned I have an elaborate Object Mapper and reactive publisher extension to your pub-sub system, and I'm trying to debug it.

Best,
-Liron

David Glasser

unread,
Oct 1, 2012, 10:44:37 PM10/1/12
to meteo...@googlegroups.com, Jonathan Lerner
It's relatively simple: there's no merging or conflict resolution involved.

Before sending any insert, update, or remove method, Meteor saves a
snapshot of the affected collection.

Then, during the execution of the method (in fact, any method), all
updates to all collections on the server are buffered rather than
applied to the local caches. Further client-side operations are
performed on the local caches and send further methods.

Once *all* outstanding methods are finished (and any new subscriptions
have received their initial contents), the following steps happen:
- All collections are disconnected from any reactive contexts that
may be observing them
- All collections with snapshots are restored to that snapshot
- All buffered updates are applied
- The reactive contexts are restored to the collections, which now
observe any changes from this process as a single change



Example 2: The case of a users.update({favColor: "blue"}, {$set:
{favColor: "green"}}), where Liron's favorite color was blue but is
changed concurrently to red.

1) The client saves a snapshot of Meteor.users.
2) The client updates Liron's favorite color from blue to green in the
local cache, and sends an update({favColor: "blue"}, {$set: {favColor:
"green"}}) method call to the server.
2b) The server, for some other reason, changes Liron's favorite color to red.
2c) The server sends the client a "set Liron's favorite color to red"
(which is buffered because of the outstanding method).
3) The server tries to apply the update, but nothing changes because
nobody's favorite color is blue.
4) Nothing is added to the write fence.
5) No further data update messages are sent.
6) Now that all the (nonexistent) conditions of the "write fence" have
been completed, the server tells the client that the method has
completed.
7) The client disconnects the collection from the reactive context.
8) The client restores the snapshot, so Liron's favorite color is blue again.
9) The client applies the buffered "set it to red" message.
10) The client reconnects the collection to the reactive context. The
reactive context observes a change from blue to red, so redraws
anything that was depending on the color.

Does that make sense? (The write fence part is an internal detail of
the server and if it's confusing you can ignore it.)
> --
> You received this message because you are subscribed to the Google Groups
> "meteor-talk" group.
> To view this discussion on the web visit
> https://groups.google.com/d/msg/meteor-talk/-/xshyftyFr70J.
> To post to this group, send email to meteo...@googlegroups.com.
> To unsubscribe from this group, send email to
> meteor-talk...@googlegroups.com.
> For more options, visit this group at
> http://groups.google.com/group/meteor-talk?hl=en.

David Glasser

unread,
Oct 1, 2012, 10:51:26 PM10/1/12
to meteo...@googlegroups.com
Oops, I accidentally overwrote my Example 1 with my Example 2.

Example 1: The normal successful case of a users.update({favColor:
"blue"}, {$set: {favColor: "green"}}), where Liron's favorite color
was blue.

1) The client saves a snapshot of Meteor.users.
2) The client updates Liron's favorite color from blue to green in the
local cache, and sends an update({favColor: "blue"}, {$set: {favColor:
"green"}}) method call to the server.
3) The server receives the update method and starts a "write fence".
The "write fence" is what controls when the "method is done" message
is sent to the client: it's a set of conditions like "method not done
until data written to Mongo" or "method not done until data update
messages have been sent to the client". The first condition on the
"write fence" is "a Mongo update call finishes"
4) The data is updated in Mongo. Before telling the write fence that
the update call finished, it adds another condition to the write
fence: that any relevant data update messages on the collection get
sent to the client.
5) The server sends the client a message saying to update Liron's
color (by _id) to green. The client buffers this message because it is
waiting for the method to finish.
6) Now that all the conditions of the "write fence" have been
completed, the server tells the client that the method has completed.
7) The client disconnects the collection from the reactive context.
8) The client restores the snapshot, so Liron's favorite color is blue again.
9) The client applies the buffered "set it to green" message.
10) The client reconnects the collection to the reactive context.
Although we've just changed the color twice (green -> blue -> green),
the changes happened while reactivity was off, and the total change
from before to after is nothing. So nothing has to be redrawn
client-side.


(BTW, my example 2 in the last email was technically inaccurate;
things are added to the write fence in that case too. As I said,
internal detail.)

--dave

liron00

unread,
Oct 2, 2012, 11:47:39 PM10/2/12
to meteo...@googlegroups.com
Thanks David, that helps a lot.

So if I have a method that updates multiple collections, and I have a bunch of subscribe closures that have cursor observers which trigger @set and @flush calls, will all those make it into the client's LC buffer before the write fence of a method call is retired, precisely if the calls happen synchronously after an observe callback?

matt debergalis

unread,
Oct 2, 2012, 11:54:06 PM10/2/12
to meteo...@googlegroups.com
Yes. collection.observe() promises to run its callbacks
(synchronously!) before the original DB write returns to its caller --
in this case, your original method.

On Tue, Oct 2, 2012 at 8:47 PM, liron00 <li...@quixey.com> wrote:
> Thanks David, that helps a lot.
>
> So if I have a method that updates multiple collections, and I have a bunch of subscribe closures that have cursor observers which trigger @set and @flush calls, will all those make it into the client's LC buffer before the write fence of a method call is retired, precisely if the calls happen synchronously after an observe callback?
>
> --
> You received this message because you are subscribed to the Google Groups "meteor-talk" group.
> To view this discussion on the web visit https://groups.google.com/d/msg/meteor-talk/-/hNoEuEfMfKAJ.

liron00

unread,
Oct 3, 2012, 3:19:36 PM10/3/12
to meteo...@googlegroups.com
I love this guarantee about synchronous observers, but it doesn't seem to be the case in our testing.

Code:
if Meteor.isServer then Meteor.startup ->
    tests = new Meteor.Collection('tests')
    tests.insert({x: 5})

    handle = tests.find().observe(
        changed: (newDoc, atIndex, oldDoc) ->
            console.log 'Observer callback: ', oldDoc.x, '->', newDoc.x
    )
                                                                                                                                                                                                                                                                                                                                                                          
    console.log('Updating...')
    tests.update({x: 5}, {$inc: {x: 1}})
    console.log('This should print after the observer.')


Log output:
Updating...
This should print after the observer.
Observer callback:  5 -> 6


We're pretty stuck on this problem atm.

matt debergalis

unread,
Oct 3, 2012, 3:28:39 PM10/3/12
to meteo...@googlegroups.com
Ah, yes. I'd forgotten the newer write fence semantics.

See this comment in packages/mongo-livedata/mongo_driver.js:

// After making a write (with insert, update, remove), observers are
// notified asynchronously. If you want to receive a callback once all
// of the observer notifications have landed for your write, do the
// writes inside a write fence (set Meteor._CurrentWriteFence to a new
// _WriteFence, and then set a callback on the write fence.)

Then look at packages/livedata/writefence.js and the method invocation
logic in packages/livedata/livedata_server.js.

The method invocation uses the fence to ensure observers complete
before the method reports satisfaction.
> https://groups.google.com/d/msg/meteor-talk/-/3h7Cx2HnpPkJ.

David Glasser

unread,
Oct 3, 2012, 3:44:26 PM10/3/12
to meteo...@googlegroups.com
Yeah, to clarify: the stuff I described in my message is specific to
*method calls*. There aren't any method calls in the code you posted.
If your code was *client-side*, then update would be a method call and
the ordering I defined would work.

If you want to do code like this server-side and get the same
guarantees outside of a method invocation, you can emulate how
livedata_server uses _WriteFence, but do be aware that the details of
this private undocumented API are more likely to change incompatibly
than public documented APIs.

liron00

unread,
Oct 3, 2012, 4:11:40 PM10/3/12
to meteo...@googlegroups.com
Thanks guys, your answers explained everything I've been confused about so far.

David Baldin

unread,
Feb 13, 2014, 7:46:31 AM2/13/14
to meteo...@googlegroups.com, li...@quixey.com
Is this "write fence" borrowed from mpi-wait / pthreads conditionals? Who thought this out initially? Sounds like its inspired by hpc-computing topics.

Regards,D.

David Baldin

unread,
Feb 13, 2014, 7:51:15 AM2/13/14
to meteo...@googlegroups.com, li...@quixey.com
Btw another question: where in the sources is this communication process implemented currently (specific this fences thing)?
Reply all
Reply to author
Forward
0 new messages