Un système de cache simple en base de données

Quand on n’a pas la possibilité d’avoir un serveur memcache à disposition, on peut mettre en place un système de cache en base de données simplement. Voici comment faire.

Il faut commencer par créer la table qui va stocker les données, voici les commandes sql pour mySQL :

CREATE TABLE `cached_items` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `cachekey` varchar(255) DEFAULT NULL,
  `created` datetime DEFAULT NULL,
  `expires` datetime DEFAULT NULL,
  `content` longblob,
  `cachehit` int(11) NOT NULL,
  PRIMARY KEY  (`id`),
  KEY `cacheditems_cachekey_index` (`cachekey`),
  KEY `cacheditems_created_index` (`created`),
  KEY `cacheditems_expires_index` (`expires`)
);

Ensuite, il faut créer un modèle appelé CachedItem dans app/models/cached_item.rb c’est grâce à lui qu’on pourra accéder aux données cachées :

require 'digest/sha1'
 
class CachedItem < ActiveRecord::Base
  def self.check_for(item)
    key = Digest::MD5.hexdigest(Marshal.dump(item))
    logger.info "Cache checking for key #{key}"
    self.find(:first, :conditions => ["cachekey = ? AND expires > NOW()", key])
  end
 
  def self.get_cached(item)
    key = Digest::MD5.hexdigest(Marshal.dump(item))
    logger.info "Cache getting by key #{key}"
    getc = self.find( :first, :conditions => ["cachekey = ?", key] )
    hitcount = getc.cachehit + 1
    self.update(getc.id, {:cachehit => hitcount})
    self.delete_all "expires < NOW()"  # Efface les données trop vieilles
    return Marshal.load(getc.content)
 end
 
  def self.store_data(item, data)
    key = Digest::MD5.hexdigest(Marshal.dump(item))
    logger.info "%%% storing by key #{key}"
    content = Marshal.dump(data)
    logger.level = (4) # Empêche d'afficher les données de Marshal dans le log
    ci = new()
    ci.cachekey = key
    ci.created = Time.now()
    ci.expires = 30.minutes.from_now() # durée de vie de la donnée cachée
    ci.content = content
    ci.cachehit = 0
    ci.save
    return data
  end
end

Voila, le plus dur est fait. Faites attention au type de données que vous stockez, Marshal ne supporte pas tous les types. Vous pouvez sans problème stocker une chaîne, par exemple pour du XML vous pouvez faire :

donnees = REXML::Document.new(mes_donnees_xml).root
CachedItem.store_data(donnees_id, donnees.to_s)

Pour stoker des donnes xml. Pour les relire :

donnees = REXML::Document.new(CachedItem.get_cached(mes_donnees_xml_id)).root

Les données trop vieilles sont automatiquement effacée lors d’une lecture quelconque (voir get_cached) et la durée de vie d’une donnée est définie lors de l’écriture dans store_data.

Merci à joeldg pour son code qui a servi de base à ce billet.

0 commentaire ↓

Il n'y a aucun commentaire

Laisser un commentaire