Implementing APC / Memcache

Hi,

I will be developing an application for internal use and will be adding the APC library.

Normally what I would do is create a DB class that holds all my queries:


class DB {
  public function getUserById($id) {
    //SQL query returns all the columns needed
    //ie: email, first name, last name...
  }

  public function getUserNewsFeed($userId) {...}

  public function login($email, $password) {
    //SQL query attempts to find user
    //if found return user id, first name, last name...
  }
}

and a User class:


class User extends AppModel {
  // AppModel has a property that holds the db connection

  private $id;
  private $fname;
  private $lname;
  private $email;

  public function getNewsFeed() {
     $stmt = $this->db->getNewsFeed();

     if ($stmt instanceof PDOStatement) {
       //etc...
     }
  }
}

When adding APC I would think it would go like this:


class APC {
  private $db;

  public function __construct() {
    $this->db = new PDO(); // only used when the object is not cached
  }

  public function getUserById($id) {
    $users = apc_fetch('users'); //returns array
    
    if (isset($users[$id])) {
      return $users[$id]; //returns a User object
    } else {
      //run an sql query: $this->db->getUserById($id)
      //store the result in apc_store and return it
    }
  }
}

Instead of instantiating the DB class, I would instantiate the APC class in the AppModel and only query the database when needed. So the User class would look like this:


class User extends AppModel {
  // AppModel has a property that holds the apc / persistence layer

  private $id;
  private $fname;
  private $lname;
  private $email;

  public function getNewsFeed() {
     $stmt = $this->apc->getNewsFeed(); //changed to apc but should represent a persistence layer

     if ($stmt instanceof PDOStatement) {
       //etc...
     }
  }
}

I’m wondering if this is the correct way of doing this and if there is a name for this pattern.

I think you would want your DB and APC classes to implement a common interface and then have your User class implement against this.

The implementation of APC, where it delegates to DB on a cache-miss, is usually called a wrapper.

One other thing to think off:

  • Sometimes selecting data by the primary key from the DB is faster than selecting it from APC/Memcached.

What I would do:


class APC {
  public static function get..
  public static function set..
  public static function delete..
}

class User extends dto {
  public function get..
  public function set..
  public function delete..
  public function getNewsFeeds..
}

# Your wrapper
class CachedUser extends User {
  public function getNewsFeeds () {
    # Get from cache
    $feeds = APC::get('news_feeds_for_user_id_' . $this->id);
    if ($feeds) { 
       return $feeds;
    }
    # Get from db or wherever the user gets it from
    $feeds = parent::getNewsFeeds();
    # Cache for next get
    APC::set('news_feeds_for_user_id_' . $this->id, $feeds);
    return $feeds;

  }
}

The reasons I would do it like this:

  • Your User class doesn’t have to know that it’s cached, so it’s logic can remain simple.
  • You can chose what to cache (example: your website might always want the feeds cached, but some back-end batch script that deals with old archives might not, since it if it does, it will either be to slow or fill up your cache and slow down the rest of your site)

Hope that helps.

Ah yes, I forgot to implement an interface. Good catch.