Wednesday, September 26, 2012

Client side includes

Here's a future tech to keep an eye on: the "seamless" attribute for iframes. (WhatWG spec) It basically acts as a client-side include for HTML code (and so, really, isn't much like a frame at all). So, just as we've always done server-side, we can now ("now," meaning "lord knows when"... browser support ugh) break a page into fragments and serve each portion from a different URI, which are recombined by the browser. This gives us great control over caching and distribution. We could, for example, use a frame for the navigation that is served from a CDN and refreshed only every 24 hours, and one for an article content that caches for one hour (in case of updates), and one for social media stuff that is never cached. Unlike with regular iframes, all of this can happen within one DOM space, sharing styles, scripts, etc.

Of course, much of the same can be achieved with javascript templating and AJAX fetches. But that's not always appropriate, and it always adds an additional layer of complexity that might be overkill. This is a conceptually simpler approach, and I like having it as an option. Unfortunately, we don't really have it as an option yet. There's no browser support; it's not even mentioned yet on caniuse. So, future-Dave: let's watch this one as it emerges. It looks like a good trick for scalability on sites where the content on a page has a mix of freshness requirements. (And isn't that pretty much every site?)

Saturday, September 8, 2012

Defaulting on performance

In creating a highly performant Web environment, I've frequently been hampered by the very conservative default limits that exist in many technologies. In fact, I'd say that at least 4 out of every 5 performance bottlenecks I've encountered have come not from the system being actually overwhelmed, but rather from a self-imposed limit. Systems have numerous in-built constraints on concurrency, memory usage, and many other ways to prevent themselves from utilizing all of their available resources and realizing their true potential. Servers, you need a life coach. I'm here for you.

Of course, these limits were placed there by very wise people with very good reasons. For example, on *NIX operating systems, the designers were considering the needs of a multi-user situation: in a university CS lab where dozens of naive and/or mischievous undergrads are sharing clock cycles of a single CPU, it's important that no one user is allowed to chew up all the resources and bring down the system. But a Web server typically runs just one user, and has much more capacity than it is configured to use by default. When you're chasing down an international superspy in your Bugatti Veyron (who hasn't been there?), you've gotta pop out the electronic speed limiter and go for it. Likewise, when your NCIS slash fiction goes viral (who hasn't been there?), you need to goose the config and let those servers fly.

So, here is an incomplete list of some of the configuration-imposed constraints I've encountered over the years that are typically easy to relieve -- if you do it before the crowds arrive:
  • Linux: iptables' ip_conntrack default database size is too small [see racker hacker]
  • MySQL: max_connections default is low [see electric toolbox]
  • Apache: prefork module is single-threaded [see serverfault]
  • MySQL: query cache is disabled by default [it's not always a good idea to turn it on, but when it's good, it can be very good. see mysqlperformanceblog]
  • Tomcat: the JDBC default connection pool size is small [see tomcat 6 docs, but also, tomcat 7's new hotness]
  • Java: default JVM memory settings don't take advantage of available memory, and default garbage collection can create long "stop-the-world" pauses [this is a deep topic, but here's an intro]
  • Linux: default max open files is too low [see stackoverflow]
  • MySQL: back_log default is low [see MySQL documentation]
  • MySQL: innodb_thread_concurrency default is low [see stackexchange -- though be aware, setting it to "0" (infinite) might be too much]
  • Linux: net.core.somaxconn, net.core.netdev_max_backlog are too low [see]