Más allá del TTL: almacenamiento definido por el usuario

El complemento de caché de consultas admite el uso de gestores de almacenamiento definidos por el usuario. Éstos pueden usar algoritmos de invalidacion complejos de forma arbitraria y admitir medios de almacenamiento arbitrarios.

Todos los gestores de almacenamiento definidos por el usuario deben proporcionar una cierta interfaz. Las funciones del gestor de almacenamiento definido por el usuario serán llamadas por el núcleo del complemento de caché. La interfaz necesaria consiste en siete funciones públicas. El gestor de almacenamiento definido por el usuario, tanto procedimental como el orientado a objetos, debe implementar el mismo conjunto de funciones.

Ejemplo #1 Utilizar un gestor de almacenamiento definido por el usuario

<?php
/* Habilitar el almacenamiento en la caché predeterminado para todas las sentencas */
ini_set("mysqlnd_qc.cache_by_default"1);

/* Funciones del gestor de almacenamiento procedimental definido por el usuario */

$__cache = array();

function 
get_hash($host_info$port$user$db$query) {
    global 
$__cache;
    
printf("\t%s(%d)\n"__FUNCTION__func_num_args());

    return 
md5(sprintf("%s%s%s%s%s"$host_info$port$user$db$query));
}

function 
find_query_in_cache($key) {
    global 
$__cache;
    
printf("\t%s(%d)\n"__FUNCTION__func_num_args());

    if (isset(
$__cache[$key])) {
        
$tmp $__cache[$key];
        if (
$tmp["valid_until"] < time()) {
            unset(
$__cache[$key]);
            
$ret NULL;
        } else {
            
$ret $__cache[$key]["data"];
        }
    } else {
        
$ret NULL;
    }

    return 
$ret;
}

function 
return_to_cache($key) {
    
/*
     Invocada en una búsqueda coincidente en la caché después de haber procesado el
     almacenamiento de los datos, podría usarse como cuenta de referencia
    */
    
printf("\t%s(%d)\n"__FUNCTION__func_num_args());
}

function 
add_query_to_cache_if_not_exists($key$data$ttl$run_time$store_time$row_count) {
    global 
$__cache;
    
printf("\t%s(%d)\n"__FUNCTION__func_num_args());

    
$__cache[$key] = array(
        
"data"               => $data,
        
"row_count"          => $row_count,
        
"valid_until"        => time() + $ttl,
        
"hits"               => 0,
        
"run_time"           => $run_time,
        
"store_time"         => $store_time,
        
"cached_run_times"   => array(),
        
"cached_store_times" => array(),
    );

    return 
TRUE;
}

function 
query_is_select($query) {
    
printf("\t%s('%s'): "__FUNCTION__$query);

    
$ret FALSE;
    if (
stristr($query"SELECT") !== FALSE) {
        
/* almacenar en caché durante 5 segundos */
        
$ret 5;
    }

    
printf("%s\n", (FALSE === $ret) ? "FALSE" $ret);
    return 
$ret;
}

function 
update_query_run_time_stats($key$run_time$store_time) {
    global 
$__cache;
    
printf("\t%s(%d)\n"__FUNCTION__func_num_args());

    if (isset(
$__cache[$key])) {
        
$__cache[$key]['hits']++;
        
$__cache[$key]["cached_run_times"][] = $run_time;
        
$__cache[$key]["cached_store_times"][] = $store_time;
    }
}

function 
get_stats($key NULL) {
    global 
$__cache;
    
printf("\t%s(%d)\n"__FUNCTION__func_num_args());

    if (
$key && isset($__cache[$key])) {
        
$stats $__cache[$key];
    } else {
        
$stats = array();
        foreach (
$__cache as $key => $details) {
            
$stats[$key] = array(
               
'hits'              => $details['hits'],
               
'bytes'             => strlen($details['data']),
               
'uncached_run_time' => $details['run_time'],
               
'cached_run_time'   => (count($details['cached_run_times']))
                                      ? 
array_sum($details['cached_run_times']) / count($details['cached_run_times'])
                                      : 
0,
            );
        }
    }

    return 
$stats;
}

function 
clear_cache() {
    global 
$__cache;
    
printf("\t%s(%d)\n"__FUNCTION__func_num_args());

    
$__cache = array();
    return 
TRUE;
}

/* Instalar el gestor de almacenamiento procedimental definido por el usuario */
if (!mysqlnd_qc_set_user_handlers("get_hash""find_query_in_cache",
    
"return_to_cache""add_query_to_cache_if_not_exists",
    
"query_is_select""update_query_run_time_stats""get_stats""clear_cache")) {
  
        
printf("Fallo al instalar el gestor de almacenmiento definido por el usuario\n");
}


/* Conectar, crear y rellenar la tabla test */
$mysqli = new mysqli("host""usuario""contraseña""esquema""puerto""socket");
$mysqli->query("DROP TABLE IF EXISTS test");
$mysqli->query("CREATE TABLE test(id INT)");
$mysqli->query("INSERT INTO test(id) VALUES (1), (2)");

printf("\nColocada en caché/sin coincidencia en la caché\n");

$res $mysqli->query("SELECT id FROM test WHERE id = 1");
var_dump($res->fetch_assoc());
$res->free();

/* Borrar el registro para verificar que obtenemos los datos desde la caché */
$mysqli->query("DELETE FROM test WHERE id = 1");

printf("\nCoincidencia con la caché\n");

$res $mysqli->query("SELECT id FROM test WHERE id = 1");
var_dump($res->fetch_assoc());
$res->free();

printf("\nMostrar las estadísticas de la caché\n");
var_dump(mysqlnd_qc_get_cache_info());

printf("\nVolcado de la caché, colocada en la caché/sin coincidencia en la caché");
var_dump(mysqlnd_qc_clear_cache());

$res $mysqli->query("SELECT id FROM test WHERE id = 1");
var_dump($res->fetch_assoc());
$res->free();
?>

El resultado de los ejemplos sería algo similar a:

        query_is_select('DROP TABLE IF EXISTS test'): FALSE
        query_is_select('CREATE TABLE test(id INT)'): FALSE
        query_is_select('INSERT INTO test(id) VALUES (1), (2)'): FALSE

Colocada en caché/sin coincidencia en la caché
        query_is_select('SELECT id FROM test WHERE id = 1'): 5
        get_hash(5)
        find_query_in_cache(1)
        add_query_to_cache_if_not_exists(6)
array(1) {
  ["id"]=>
  string(1) "1"
}
        query_is_select('DELETE FROM test WHERE id = 1'): FALSE

Coincidencia con la caché
        query_is_select('SELECT id FROM test WHERE id = 1'): 5
        get_hash(5)
        find_query_in_cache(1)
        return_to_cache(1)
        update_query_run_time_stats(3)
array(1) {
  ["id"]=>
  string(1) "1"
}

Mostrar las estadísticas de la caché
        get_stats(0)
array(4) {
  ["num_entries"]=>
  int(1)
  ["handler"]=>
  string(4) "user"
  ["handler_version"]=>
  string(5) "1.0.0"
  ["data"]=>
  array(1) {
    ["18683c177dc89bb352b29965d112fdaa"]=>
    array(4) {
      ["hits"]=>
      int(1)
      ["bytes"]=>
      int(71)
      ["uncached_run_time"]=>
      int(398)
      ["cached_run_time"]=>
      int(4)
    }
  }
}

Volcado de la caché, colocada en la caché/sin coincidencia en la caché    clear_cache(0)
bool(true)
        query_is_select('SELECT id FROM test WHERE id = 1'): 5
        get_hash(5)
        find_query_in_cache(1)
        add_query_to_cache_if_not_exists(6)
NULL