Posted on December 23, 2010 by

Recess! Lazy Load the Database

Its been awhile since I’ve been able to play with Recess! After being stuck doing application level code for some time, I am happy to be able to offer at least a little back.

I’ve found myself in the situation where I have multiple databases on a couple different hosts. One is my Recess! application’s database, another is a radius database (which is totally impossible to model btw but I still have to query against it), and I have a third database coming into play on another host that I need to integrate functionality from. So I have three databases that are being connected to on each request. This is totally overkill since only certain resources ever need to contact any single database.

So why not see if we can lazy load the database connections? This way code that deals with user login history won’t have to wait for a connection to my database, or this third database. The others get their own space as well.

First, I wanted to see how long a standard request took. I used PRODUCTION mode, and watched chrome’s resource time count, and I was looking at almost 2 seconds for a request to come back. This made sense because the databases weren’t all on the same host, so I had a lot of network activity behind the scenes. But now that we have a baseline to beat, so lets go.

The offending code is here:
bootstrap.php
if(!empty(RecessConf::$namedDatabases)) {
foreach(RecessConf::$namedDatabases as $name => $sourceInfo) {
Databases::addSource($name, new ModelDataSource($sourceInfo));
}
}

This loop will create a new ModelDataSource object, which extends PdoDataSource, which establishes the database connection in the constructor. Since this is in bootstrap, it is run every time someone tries to access your service.

The addSource function will stick the established object into an array of sources. So I think the best thing to do is:

  1. Modify bootstrap.php to only pass the $sourceInfo array to addSource, and not instantiate the object
  2. Modify addSource() to put the $sourceInfo array into an inactive object list
  3. Modify the getSource() function to establish the connection if it hasn’t already been established

Lets look at my modified Databases.class.php:
static function getSource($name) {
if(isset(self::$sources[$name]))
return self::$sources[$name];
else
if(isset(self::$inactive_sources[$name])) {
self::addActiveSource($name, new ModelDataSource(self::$inactive_sources[$name]));
return self::$sources[$name];
} else return null;
}

static function addActiveSource($name, PdoDataSource $source) {
self::$sources[$name] = $source;
}

/**
* Add a named datasource.
*
* @param string $name
* @param Array $source
*/
static function addSource($name, $source) {
self::$inactive_sources[$name] = $source;
}

And the new bootstrap.php source
if(!empty(RecessConf::$namedDatabases)) {
foreach(RecessConf::$namedDatabases as $name => $sourceInfo) {
Databases::addSource($name, $sourceInfo);
}
}

The Results:
I tried it out using some logging just to make sure the code acted as I intended. I’m now only connecting to databases that are specified as sources by the object models, and chrome is telling me my request time is down to around 550ms. So I say mission accomplished at an average reduction of almost 1.5 seconds per request.

This won’t play nice with Recess Tools. When you create a new object, you will only be able to add to the default database. If you add some code to check for $useTurboSpeed that will be an easy fix though. I just haven’t used the tools in quite awhile now.

Let me know what you think. Are there any problems I’m not seeing with this?