Skip to content

Commit

Permalink
support for redirection of read only queries to a round robbin of rea…
Browse files Browse the repository at this point in the history
…d db handles
  • Loading branch information
weyrick committed Nov 16, 2011
1 parent a6bb5c3 commit 0b9c27f
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 12 deletions.
2 changes: 2 additions & 0 deletions config/globalConfig.xsm
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,15 @@
<VAR name="defaultLocale" value = "en_US" desc = "default locale for site" />
<VAR name="useClientLocale" value = "true" desc = "when true, SiteManager will default to the locale requested by the clients browser, if available" />
</SECTION>
<!--
<SECTION name="db" id="default">
<VAR name="active" value = "false" desc = "whether this connection is active or not" />
<VAR name="defaultConnection" value = "true" desc = "set to true to have this connection be the default connection. will be $dbH" />
<VAR name="dbType" value = "mysql" desc = "the database type. must be valid PEAR database type." />
<VAR name="persistent" value = "true" desc = "bool to determine whether this is a persistent connection or not" />
<VAR name="required" value = "true" desc = "if this is true, a fatal error will be thrown if the connection fails. if false, it will ignore a bad connection." />
</SECTION>
-->
<SECTION name="memcached" id="default">
<VAR name="host" value = "localhost" desc = "memcached host" />
<VAR name="port" value = "11211" desc = "memcached port" />
Expand Down
39 changes: 32 additions & 7 deletions lib/smDbWrapper.inc
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,25 @@ class SM_dbWrapper
/**
* simple counter
*/
public $queryCount = 0;
static public $queryCount = 0;
static public $readCount = 0;

/**
* read only database connection: only allow SELECT
*/
public $readOnly = false;

/**
* a list of db handles we redirect SELECT queries to. it will rotate through
* the list in round robin fashion
*/
public $roList = NULL;

/**
* last index we sent a query to in roList
*/
public $roI = 0;

function __construct($id, $dsn, $user = NULL, $pass = NULL, $options = NULL) {
$this->SM_id = $id;
$this->config['dsn'] = $dsn;
Expand Down Expand Up @@ -177,12 +189,25 @@ class SM_dbWrapper
// so that siteManager knows the last ID in use
$GLOBALS['SM_LAST_DB_ID'] = $this->SM_id;

$this->queryCount++;

// XXX we're strict here and require the first word to be SELECT.
// this is to avoid multiple queries in one string
if ($this->readOnly && strtoupper(substr($query,0,6)) != 'SELECT') {
SM_fatalErrorPage('ro db violation: '.substr($query,0,3));
self::$queryCount++;

// we're strict here and require the first word to be SELECT (no leading spaces)
// this avoids failing ro check on multiple queries in one string
if ($this->readOnly || $this->roList) {
$isSelect = (($query[0] == 'S' || $query[0] == 's') && ($query[1] == 'E' || $query[1] == 'e'));
if ($isSelect)
self::$readCount++;
// read only check
if ($this->readOnly && !$isSelect) {
SM_fatalErrorPage('ro db violation: '.substr($query,0,2));
}
// round robin read only redirects
elseif ($this->roList && $isSelect) {
$result = $this->roList[$this->roI]->query($query);
if (sizeof($this->roList) > 1)
$this->roI = ($this->roI + 1) % sizeof($this->roList);
return $result;
}
}

// unless we're benchmarking, just record the last query (for errors) and go to parent
Expand Down
9 changes: 5 additions & 4 deletions lib/smObject.inc
Original file line number Diff line number Diff line change
Expand Up @@ -594,10 +594,10 @@ class SM_dbHandles implements ArrayAccess {
// db settings to connect lazily (i.e., on first use)
public $lazyConnect = array();

public function offsetGet($i) {
public function offsetGet($i,$fatal=true) {
// if the handle they are looking for isn't defined,
// see if it's a lazyConnect and if so, make the connection
global $SM_siteManager;
global $SM_siteManager;
if (empty($this->data[$i])) {
if (isset($this->lazyConnect[$i])) {
// do lazy connect
Expand All @@ -606,14 +606,15 @@ class SM_dbHandles implements ArrayAccess {
}
else {
// no lazy connect, it really doesn't exist
$SM_siteManager->fatalErrorPage('database connection undefined: '.$id);
if ($fatal)
$SM_siteManager->fatalErrorPage('database connection undefined: '.$id);
}
}
return $this->data[$i];
}

public function offsetExists ($i) {
$d = $this->offsetGet($i);
$d = $this->offsetGet($i,false);
return !empty($d);
}

Expand Down
17 changes: 16 additions & 1 deletion lib/smRoot.inc
Original file line number Diff line number Diff line change
Expand Up @@ -663,8 +663,23 @@ class SM_siteManagerRoot extends SM_object {
$dbSettings['userName'],
$dbSettings['passWord'],
array(PDO::ATTR_PERSISTENT => $dbSettings['persistent']));
$this->debugLog("added database connection [$id]");
if ($dbSettings['readOnly'])
$this->dbHL[$id]->readOnly = true;
if ($dbSettings['readRedirect']) {
if ($this->dbHL[$id]->readOnly)
$this->fatalErrorPage('cannot be readOnly and readRedirect');
// this db handle will redirect SELECTs to one or more other handles (which are presumably readOnly)
// these must have already been defined
$rList = explode(',',$dbSettings['readRedirect']);
foreach ($rList as $roid) {
if (!isset($this->dbHL[$roid]))
$this->fatalErrorPage('undefined readRedirect id: '.$roid);
if (!$this->dbHL[$roid]->readOnly)
$this->fatalErrorPage('readRedirect connection is not read only: '.$roid);
$this->dbHL[$id]->roList[] =& $this->dbHL[$roid];
}
}
} catch (PDOException $e) {
if ($dieOnError) {
$this->debugLog($e->getMessage()."<br />\n");
Expand Down Expand Up @@ -905,7 +920,7 @@ class SM_siteManagerRoot extends SM_object {
if ($this->siteConfig->getVar('flags','showLoadTime')) {
if ($GLOBALS['SM_develState']) {
$mem = memory_get_peak_usage();
echo "\n<!-- $scriptLoadTime secs / $mem bytes / {$this->dbH->queryCount} queries -->\n";
echo "\n<!-- $scriptLoadTime secs / $mem bytes / ".SM_dbWrapper::$queryCount." (".SM_dbWrapper::$readCount." read) queries -->\n";
}
else {
echo "\n<!-- $scriptLoadTime s -->\n";
Expand Down

0 comments on commit 0b9c27f

Please sign in to comment.