2009-08-12 3 views
8

Dans le monde C#/.Net, il existe des ORM tels que NHibernate ou ActiveRecord qui incluent la mise en cache transparente: les mises à jour de base de données sont répliquées en cache, les objets sont récupérés directement dans le cache (memcached).Est-ce que DBIx :: Class a une mise en cache transparente?

Il ne semble pas que la mise en cache transparente soit disponible en Perl avec DBIx::Class. Ai-je manqué quelque chose? Cela semble être un besoin commun, je suis surpris de ne rien trouver sur CPAN ou Google.

+0

Vous obtenez beaucoup de résultats pour http://www.google.com/search?q=DBIx%3A%3AClass+caching –

Répondre

6

Semi-transparent il y a DBIx :: Class :: Cursor :: Cached (à partir de mst, comme DBIC). Vous devez cependant fournir un objet Cache à vos connexions ou vos objets de schéma. Semble très sans papiers malheureusement. Le Cookbook a un exemple d'utilisation de Tie :: Cache sur DBIC, et il y a aussi les fonctions (get | set | clear) _cache sur DBIx :: Class :: ResultSet, mais elles ne sont probablement pas exactement ce que vous voulez. avoir besoin.

+1

D'après ce que je compris, c'est la mise en cache pour une utilisation très limitée Cas. Si vous effectuez une recherche et parcourez plusieurs fois les éléments de votre recherche, vous pouvez mettre en cache le jeu de résultats. Mais vous ne pouvez pas mettre en cache des objets individuels. Je l'ai testé, dans mon cas cela ne m'aide pas du tout (j'ai en fait des performances plus mauvaises car je cache le résultat de la recherche, mais je l'utilise une seule fois) – Julien

+1

En fait, ça pourrait être une solution. Le problème est avec les relations: le resultset principal est mis en cache, mais pas les relations. – Julien

5

Voici une manière simple d'ajouter la mise en cache avec CHI. Je n'ai pas réellement essayé ceci, ainsi il peut y avoir des pièges que je n'ai pas considérés, particulièrement en ce qui concerne la sérialisation des jeux de résultats de DBIC.

package My::Table; 
use strict; 
use warnings; 

use base 'DBIx::Class'; 

use Storable 'freeze'; 
use CHI; 

$Storable::canonical = 1; 

__PACKAGE__->load_components(qw/Core/); 
__PACKAGE__->table('mytable'); 

# .... 

my $CACHE = CHI->new(driver => 'Memory'); 

sub search { 
    my $self = shift; 

    my $key = freeze(\@_);  # make cache key from params 
    if (my $rs = $CACHE->get($key)) { 
     return $rs; 
    } 

    # Note: there are issues with context propagation here 
    my $rs = $self->next::method(@_); 
    $CACHE->set($key => $rs); 
    return $rs; 
} 

sub update { 
    my $self = shift; 

    my @keys = $self->find_all_cache_items_affected_by_this_update(@_); 
    $CACHE->remove($_) for @keys; 

    $self->next::method(@_); 
} 

C'est un peu maladroit, mais je pense que c'est un bon point de départ. Si vous faites ce genre de chose dans une classe de base pour toutes vos classes de tables DBIx :: Class, vous devriez pouvoir construire facilement la mise en cache transparente.

+0

Merci, il semble que le chemin à parcourir. J'utilise Catalyst avec Catalyst :: Model :: DBIC :: Schema et je ne trouve pas le bon endroit pour remplacer la méthode de recherche par exemple: J'ai essayé dans DBIx :: lass, et dans DBIx :: Class :: Schema , mais en utilisant $ c-> model ('' ') -> search n'utilise pas la nouvelle méthode de recherche – Julien

1

J'ai rencontré le même besoin avec mon modèle basé sur DBIx :: Class, et après avoir passé en revue les réponses ici, je ne vois vraiment rien qui soit la solution que je recherche. Après avoir lutté avec ce problème, je commence à penser que ma couche de gestion devrait gérer la mise en cache, de sorte que je traite DBIx :: Class comme une couche de persistance qui n'implémente pas la logique métier.

Par exemple, mon code actuel avec le cache idéal serait quelque chose comme ceci:

my $network = SL::Model::App->resultset('Network')->search({ ip => '127.0.0.1' }); 

Et l'objet réseau $ est servi à partir du cache de memcached de $ que j'ai configuré lors DBIx :: initialisation du schéma de classe

Le nouveau code serait:

my $network = SL::Network->find_by_ip_or_create({ ip => '127.0.0.1' }); 

Pendant ce temps, dans un module à proximité:

package SL::Network; 
... 
use SL::Model::App; 
use SL::Cache; 

our $cache = SL::Cache->new; 

sub find_by_ip_or_create { 
    my ($class, $args) = @_; 

    my $network; 
    unless ($network = $cache->get('network|' . $args->{ip}) { 
     $network = SL::Model::App->resultset('Network')->find_or_create({ wan_ip => $args->{ip}}); 
     $cache->set('network|' . $args->{ip} => DBIx::Class::Schema->freeze($network)); 
    } 
    return $network; 

} 

Vous avez l'idée.

0

Je voudrais ajouter que, au lieu d'ajouter une méthode « recherche » dans My::Table,

on peut aussi améliorer la -> méthode de recherche fourni par DBIx::Class::ResultSet, comme ceci:

package Schema::ResultSet::My::Table; 
use base 'DBIx::Class::ResultSet'; 

sub search { 
    my ($self, $args) = (shift, shift); 

    # do what you want here with the args passed to ->search 
    return $self->next::method($args, @_); 
} 

également , vous pouvez très probablement sous-classer ResultSet afin que vous puissiez fournir cette recherche altérée (mise en cache) à tous les ResultSets, gardant ainsi le code de cache dans un endroit pour toutes les tables, ce qui serait beaucoup moins compliqué.

Cela je n'ai pas encore testé, cependant.Pour que l'exemple ci-dessus fonctionne, placez-le dans un fichier portant le nom de votre classe de schéma, dans le répertoire "../Schema/ResultSet/", et assurez-vous que votre fichier Schema.pm contient "load_namespaces();" qui va automatiquement charger toutes vos classes surchargées mettre là (je pense que mon installation de Catalyst a fait cela automatiquement, mais je ne me souviens pas).

DBIx::Class::ResultSet

Questions connexes