Wednesday 31 October 2012

Blitz.io Updates Pricing Model

Early this month I was checking out blitz.io and although it looked like a cool tool (and fun), the pricing model stopped me looking at it too closely, for reasons I explained to them:

Hi folks,

Was just checking out blitz.io, which looks really cool (and fun), but I was stopped pretty early on by the pricing. I don't understand why the length of a rush is tied to the number of concurrent users (for pricing purposes). 1-minute tests are basically useless, other than checking if the site's going to just immediately fall over at a given load. I don't even trust 10-minute tests. To adequately determine the capacity of a system, I'd want to run a test for at least half an hour and ideally an hour, as the various components of the system go through their various operations (GC, for instance). I don't always need 50,000 concurrent users, but I do want the ability to choose longer rushes. Do you have a mechanism for that? If not, could I suggest you add one?

Best,
--
T.J. Crowder

Less than half an hour later (nice), Michael Smith from blitz wrote back to tell me new pricing was on the way.

Well, it's here, and it makes so much more sense: You buy credits (they start at $1 USD = 1 credit), and one credit is worth 1 minute of 1,000 concurrent users. No longer is the length of a rush tied to the number of concurrent users, you control those variables completely independently. A 20-minute rush of 1,000 concurrent users costs 20 credits. 20 minutes of 10,000 concurrent users, 200 credits. And instead of being clock-on-wall time for a set period (which apparently it used to be), you're only charged for the time the rush is actually running; if something goes wrong you can stop it early to avoid spending credits unnecessarily.

As part of this, they did discontinue their old free level in favor of topping up all accounts to 10 credits each month (even accounts that haven't bought credits). There's some discussion about what that means for their continuous integration clients, which is probably worth a read.

But fundamentally, well done blitz for listening to your customers (clearly it wasn't my note that got them working on a new model, that was just happy timing for me). Now I can take a closer look.

Wednesday 24 October 2012

Asynchronicity

I see questions like this one a fair bit: The author has written this code (and is apparently using jQuery):

function Obj() {
    this.id = 0;
    this.name = '';
}

Obj.prototype.setName = function(name) {
    this.name = name;
};

function init() {
    var object1;

    object1 = new Obj();
    object1.setName("Chris");
    alert(object1.name); // alerts 'Chris'

    $.post('my_json_list.php', function(data) {
        object1.setName(data.name);
        alert(object1.name); // alerts 'John'
    });

    // After completing the data transfer alert the value of object
    alert('After data transfer the value is: '+ object1.name); // alerts 'Chris' (!)
}

The comment at the end embodies the question: The author expects the ajax call to be complete at that point. But it isn't. That code runs after the request has been started, but before it completes. So of course object1.name is still "Chris" — nothing has changed it yet. The author of that code is not remotely alone in this confusion, I've answered at least a dozen questions on StackOverflow (such as this one) where this was the fundamental problem. So:

Shout it from the rooftops: Ajax calls are asynchronous. :-) (By default.)

That means the code that starts the call runs, then any code following it, and then the ajax callbacks happen some time later. E.g., here's that init code again with some comments saying when it happens:

function init() {
    // 1: First this code runs...
    var object1;

    object1 = new Obj();
    object1.setName("Chris");
    alert(object1.name); // alerts 'Chris'

    // 2: ...and now this code *starts* the request...
    $.post('my_json_list.php', function(data) {
        // 4: (See #3 below) Then, some time AFTER #3 below, this code runs
        object1.setName(data.name);
        alert(object1.name); // alerts 'John'
    });

    // 3: ...now the request has started but is not yet finished,
    // so `object1.name` remains `"Chris"`
    alert('After data transfer the value is: '+ object1.name); // alerts 'Chris' (!)
}

So what to do about it? Easy, don't use the result of the call at point #3 above, use it at point #4 above.

This simple fact has an important implication: You cannot use the result of an ajax call as the return value of a function. This code is fundamentally flawed:

function getSomething(id) {
    var returnValue;

    $.ajax({
        url:     "/path/to/resource",
        data:    {id: id},
        success: function(result) {
            returnValue = result.data;
        }
    });

    return returnValue; // WRONG
}

That's written so you can call it like this:

something = getSomething("some ID");
if (something) {
    doSomethingWith(something);
}
else {
    doSomethingElse();
}

The problem is, it doesn't matter what id value you feed into doSomething, I can tell you what its return value will be every single time: undefined. The call to doSomething will complete, and return the default value of returnValue (which is undefined), and then some time later the callback will occur and set the value of returnValue (but it's a bit like a tree falling in the forest, as at that point no one uses it).

Can doSomething be fixed? Yes, in two ways: We can force the ajax call to be synchronous (bad idea) or we can embrace the event-driven nature of web applications (good idea).

Let's look at that first option: We can (for now) add the option async: false to the ajax call (that option is deprecated — but functional — in jQuery 1.8 and will go away at some point, though not apparently in 1.9). If we do that, the call to ajax doesn't return until the ajax call completes and its callbacks have finished. This is a bad idea because ajax calls can take significant time (even a tenth of a second is significant to human perception), and JavaScript on browsers is single-threaded. So during the time the ajax call takes, our page becomes non-responsive. (In fact, on several browsers, the entire browser — including unrelated tabs — just seems to lock up.)

Okay, so we don't want that. How do we fix it so doSomething can return what we want it to? By making doSomething return its result using a callback, just like ajax does:

function getSomething(id, callback) {
    $.ajax({
        url:     "/path/to/resource",
        data:    {id: id},
        success: function(result) {
            callback(result.data);
        }
    });
}

...and calling it like this...

getSomething("some ID", function(something) {
    if (something) {
        doSomethingWith(something);
    }
    else {
        doSomethingElse();
    }
});

Voilà, we've embraced the event-driven nature of web applications, kept our pages responsive, and it didn't even have that much impact on the calling code.

Happy (async) coding!

Thursday 4 October 2012

I Don't Want To Create An Account

Note to online vendors:

I don't want to have to create an account to give you business.

Got it? By all means offer me the option of doing so, if that makes you happy, but if I'm buying 10 quid of lightbulbs from your site, or a one-off rail ticket, I really don't want to create a username and password, opt out of your effing mailing list, etc., etc. Imagine if you had to "create an account" with every corner store you bought £10 of stuff from.

And I really don't want to be prevented from making subsequent purchases because I can't remember the stoopid password I gave the last time you made me create an account I didn't want and your "reset your password" feature is slow/broken.

The relevant term here is "barrier to sale." If you're trying to sell things, here's a hint:
Barrier to Sale = Bad Thing(tm).

(Are you listening, National Rail?)

Wednesday 3 October 2012

You can contribute to caniuse.com

I'm sure most of us have referred to (and probably cited) caniuse.com at some point. It's probably the most comprehensive collection of browser feature support around. Want to know the status of CORS support? Here you go.

Well, now if you spot an error or want to add further information, you can do so fairly directly: caniuse.com is now on GitHub.

I should probably note that caniuse.com is monetized (in a very minimal, unobtrusive fashion at present), and perhaps you don't want to contribute to someone else's monetized project on the theory that, well, they're getting paid for this (assuming the ads cover the hosting). Fair enough. For me, barring big changes to the site, I'd be happy to add to the data there if I happen to know something that's not covered. It's a great resource, well-managed.

(Side note: I'm not affiliated with caniuse.com in any way, something that may not be obvious from the above.)