Savory
The Scalable Prudence/MongoDB
Web Development Framework

It is now 11:16:14.919

Savory's Sencha Integration Library

We love, love, love Ext JS, which is by far the best client-side JavaScript framework, and its mobile cousin Sencha Touch, and wanted Savory to do them justice. We're happy with the result! This is a big library, with lots of examples and explanations, so get yourself a cup of coffee, tea or yerbamatte, and get comfy.

The main sections:

  • Grids: Easily Ext JS's most impressive widget
  • Trees: Easily Ext JS's most impressive widget, too
  • Charts: Look, ma, no Flash! (HTML5 FTW)
  • Forms: AJAX forms with client-side and server-side validation
  • Ext Direct: Ext JS's elegant client-side RPC
  • Sencha Touch: Easily create data-driven mobile apps

Refer to the Savory.Sencha API documentation for more details.

General Comments

Extended JSON

Off the bat, let's mention we added support for MongoDB's extended JSON notation to Ext JS, specifically for $date and $long. This allows you to easily send JSON coming from MongoDB straight to the client and not worry about having to massage the data. Moreover, you can make use of extended JSON without MongoDB, making it easy to pass dates directly to the client.

Client-Side Caching with Prudence

By default, Ext JS and almost all other client-side JavaScript frameworks and widgets enable an annoying form of client cache overriding in their communication with the server. You'll see in all our examples here that we're explicitly turning off Ext JS's cache overriding. Why? Because in Savory this is unnecessary: Prudence lets us do the right thing and actually use client-side caching to our benefit.

What's with the cache bashing, anyway? The story is that most server-side containers don't offer good support for conditional HTTP, which means that clients (the web browsers in which the client-side frameworks run) treat all data coming from the same URL as cacheable. If you attempt to hit the same URL twice, the browser will try to optimize and use its already cached data. In many cases, this is not what you want, because the data from the server may be different upon subsequent requests.

Ext JS and other client-side frameworks get around this problem by changing the URL for each request. A random or timestamp-based query parameter is added to the URL, so the web browser must consider it different from previous requests, and will not use its cache. The new request, in fact, will have no cache to fall back to. At the other end, the server ignores this extra query parameter and just generates and returns its data as usual.

This is a terrible solution! Think about it: every single request will be cached, but that cached data will never be used again, because the exact URL will not be used again. It's a waste of resources on the client. And why not make use of the cache when appropriate? If the data on the server indeed did not change since we last hit it, we can save lots of resources and bandwidth by not generating data and instructing the client to use its cached version. Multiply that by thousands of clients and thousands of requests, and you've increased your ability to scale up.

(It's not just the client that suffers: proxies, gateways and caches along the way, whether at the hosting backbone infrastructure, or at various points along the internet/intranet on the way to the client, will also not be able to optimize these requests, and may even waste resources on an optimization that is devilishly bypassed.)

Savory does the right thing: data from the server is returned with a timestamp, which is then compared to the client's cached version. Moreover, Savory uses Prudence's handleGetInfo() to fetch only the timestamp before fetching all the data, so that HTTP can be truly conditional. Thus, only if the client has an outdated version is handleGet called and the data generated. Lots of MongoDB requests can be entirely avoided!

Even if the timestamp changes for every single request, you still have an important advantage: because the URL is constant, the new version coming from the server will replace the cached version the client maintains for that URL. So, while you're not saving resources on the server in such super-dynamic situations, you are still saving resources on the client.

Let's do HTTP right, OK?