Safari has heisenbug with Meteor.call (!?)

So, in a nutshell, I’ve been spending the last week trying to fix a weird problem with safari, using a meteor site I’m building, and am beginning to think I am losing my mind.

Basically, I want to have Meteor.call()s rapidly fire in order to sync state in an action based game. My code, stripped of fluff, is the following:

if (Meteor.isClient) {
  
  // this == window / global
  this.Physics = {}
  Physics.counter = 0;
  Physics.updating = false;
  Physics.onUpdate = function(err, res){
    if(err) { alert(err); }
    else if (res.data == "test") {
      document.getElementById("counter").innerHTML = ++this.counter;
      this.updating = false;
    } else {
      document.getElementById("counter").innerHTML = "WTF?";
    }
  }
  Physics.onUpdate = Physics.onUpdate.bind(Physics);

  Physics.clientUpdate = function(){
    if(!this.updating){
      this.updating = true;
      Meteor.call("update", {data: "test"}, Physics.onUpdate);
    }
  }

  Template.test.onRendered(function(){
    AnimationLoop();
  })


  AnimationLoop = function(){
    Physics.clientUpdate();
    requestAnimationFrame(AnimationLoop);
  }

}

if (Meteor.isServer) {
  
  Meteor.methods({
    update: function(data) { return data; }
  })

}

With an accompanying html file that consists of:

<head>
  <title>call_test</title>
</head>

<body>
  {{> test}}
</body>

<template name="test">
  <p>You've hit the server <span id="counter">0</span> times.</p>
</template>

The expected behaviour is that on each animation frame, if there is not already an update in transit, fire a Meteor.call().

Now, as for the problem. On all browsers I’ve tested, this works absolutely fine, except for Safari… On Safari, it is ok for about ~150 iterations, but then just flat stops working. trying to manually kickstart it with retries does not work, and neither does adding fancy decorations like setTimeout()s.

The problem also exists on mobile safari (iOS) which is one of the primary deployment targets (using cordova) so this is a brutal breaker bug.

And here is the real kicker, if you open the debug menu and show the error console… the problem goes away and it works as expected. (also, when phone is hooked up to debug, the problem also goes away!)

I’m at my wits end and really could use a little bit of help figuring out what’s going on, and desperately am in need for some kind of workaround to get rapid fire Meteor.call()s working again.

Ideas? Suggestions? Solutions!? Thanks so much in advance for any help.

For the time being I have the test code above running at
http://abc.lambpop.com

It’s not what Meteor.call is made for. This amount of requests might be better done with some other stream technology like https://atmospherejs.com/yuukan/streamy

Also I am not sure if the concept is good, why would you need to sync with the database so many times?

@lucfranken Thank you. I will look into streamy and see if it helps.

I should mention that I am not touching the database for game state information, and that it is handled all in memory on the server. On other browsers I can handle hundreds of clients with 10 updates per second with negligible bandwidth utilization, so the fact remains that there is a bug with with either safari or meteor causing sockjs or Meteor.call() to stop sending requests under rapid fire conditions.

Ah ok, I read method name update which seemed to suggest saving, my bad!

You could use Kadira or https://github.com/thebakeryio/meteor-ddp-monitor to see what is happening. I have the feeling your code will never run really great because it will drain the browser. It keeps it always busy.

What is the use of your code? What needs so many updates over the line?

Since it is supposed to be a fairly intense game, I think it is ok to be fairly intense on the browser, since hopefully most of the work will be spent on animation frames and the state sync will be a secondary thing. (which, nonetheless, needs to work!)

Basically, having the browser / WebView sync with the server whenever it can is essentially the core of what I am trying for. Having a weird bug in safari, where no other browsers exhibit the bug, is the crux of the matter. Doubly so when safari is a prime target for it to be used on.

I don’t think that a keyframe is the same moment to sync with your server. I think you should see keyframes more as a moment to redraw a canvas for example. Synchronization of data is another process which can run separate and async from this.

That will also give you more control over it which allows you to solve the issue. Then you can define by yourself when you want to make a call to the server and you can throttle it.

Sure, however the basic concern is that this works (as implemented) everywhere but safari (iOS and desktop). (except when error console is open)

In general Chrome seems to have much better performance which as a result might not show you the issue.

Some interesting reading:
http://www.html5rocks.com/en/tutorials/speed/rendering/

Also I think you should check your memory consumption. Maybe something is filling up because of the bind call.

And another check is:
else if (res.data == “test”) {
If this is not true, what is the value of res? You only check for the value test but try to show what you do get back. Empty string, error, something different?

have you thought about using a non-persitent collection on the server for this ?

1 Like