Archive

Posts Tagged ‘cache’

On PHP and cache slams and solutions

December 16, 2013 Leave a comment

While reading about Doctrine’s cache mechanism (which applies to other stuff than database queries, by the way), my eye was caught by a little message at the end (last section) about cache slams.

I have used cache mechanisms extensively over the last few years, but (maybe luckily) never happened to witness a “cache slam”.
There’s a link to a blog (by an unnamed author) that explains that.

To make it short, you can have race conditions in APC (and probably in other caching mechanisms in PHP) when you assign a specific time for expiry of cache data, and a user gets to that expiry time at the same time (or very very very closely) as other users. This provokes a chain reaction (a little bit like an atomic bomb, but not with the same effect – unless some crazy military scientist binds a high-traffic website to the initiation process of an atomic bomb) which makes your website eat all memory and freeze (or something like that).

In reply to me mentioning it on Twitter, @PierreJoye (from the PHP development team) kindly pointed me to APCu, which is a user-land caching mechanism (or so to speak an APC without the opcode, and simplified).

Apparently, this one doesn’t have the cache slam issue (although I haven’t checked it myself, I have faith in Pierre here) and it’s already in PECL (still in beta though), so if you want to try it out on Debian/Ubuntu, you will probably be able to sort it out with a simple:

sudo apt-get install php5-dev php5-pear make
sudo pecl install APCu

(and then configure your PHP to include it).

Don’t forget that it is a PECL library, meaning it’s most likely you’ll have to recompile your PHP to enable it, but PECL should handle that just fine (in our case it’s a bit more complicated if we want to avoid asking our users – of Chamilo, that is – for more dependencies).

Anyway, just so you know, there are people like that who like to make the world a better place for us, PHP developers, so that we can make the world a better place developing professional-grade, super-efficient free software in PHP! Don’t miss out on contributing to that!

Clean Redmine cache

February 8, 2013 Leave a comment

To refresh Redmine cache, do the following from the Redmine directory (/usr/local/share/redmine, maybe ?):

rake tmp:cache:clear
rake tmp:sessions:clear

http://www.redmine.org/projects/redmine/wiki/RedmineUpgrade#Step-5-Clean-up

Categories: English, Techie Tags: ,

What HTTP headers do browsers send on CTRL+F5?

If you’ve been into website development, and in particular website optimization, you probably have stumbled upon that question at least once: what HTTP headers do the different browsers send when the user presses F5 or (even better) CTRL+F5, to make the web server bypass the cache system?

Well, the user “some” on Stackoverflow answered that in a very complete way here: http://stackoverflow.com/questions/385367/what-requests-do-browsers-f5-and-ctrl-f5-refreshes-generate/385491#385491

Force caching for a specific page in Varnish

Just a self-reminder, in the vcl_recv section (before sending request to the backend), check the url you want to always serve from cache and tell it to return(lookup);

  if (req.url ~ “request_server.php$”) {
return(lookup);
}

Reload Varnish and get ready for a massive fall of your load if this bloody request_server.php was overloading your site uselessly…

Categories: English, Tech Crunch Tags: , ,

Implementar xCache en su aplicación PHP

October 20, 2010 4 comments

No existe mucha documentación sobre el tema, sin embargo xCache es un sistema de caché de código operativo (opcode) que permite también hacer caché de variables. Esta documentación conviene principalmente para servidores Debian o Ubuntu (lo explico con Apache, pero también funciona con Lighttpd).

Antes de hacer cualquier cosa, sería bueno probar su servidor para asegurarse de como funciona ahora. Una forma simple de hacerlo es probarlo a través de ab, una herramienta de benchmarking de Apache que está incluida en el paquete de Apache. Para usarla, hay unos parámetros posibles. Recomiendo estudiar un poco la documentación, pero lo más simple (imaginando que el servidor para probar está en http://example.com/) es de lanzar:

ab -c50 -n1000 http://example.com/

Esto generará un resultado bastante amplio. Recomiendo copiarlo y guardarlo en un lugar seguro. Servirá al momento de comparar los resultados de las distintas mejoras.

xCache es super fácil de instalar en un servidor Ubuntu o Debian:

sudo apt-get install php5-xcache

sudo /etc/init.d/apache2 restart

Después (o antes) de relanzar el servidor web, se tiene que configurar el xCache. Esta es la parte más obscura. No hay casi nada de documentación (lo que se encuentra en el archivo de configuración mismo ya casi lo suma todo), pero básicamente no hay mucho que saber. Se edita el archivo de configuración con VIM u otro editor:

sudo vim /etc/php5/apache2/conf.d/xcache.ini

Ahí hay dos cosas principales que cambiar (potencialmente).

  1. xcache.size es la cantidad de caché que su sistema permite almacenar (se puede ver con la división por 1024*1024 de: cat /proc/sys/kernel/shmmax). Será algo de 16M, 32M o 64M en muchos casos
  2. xcache.count es la cantidad de procesadores que tiene esta máquina (se puede recuperar con: cat /proc/cpuinfo |grep -c processor)

De ahí es necesario recargar la configuración de Apache

sudo /etc/init.d/apache2 restart

De ahí ya sentirá probablemente una mejora, pero muy ligera.

Para probarlo, de nuevo:

ab -c50 -n1000 http://example.com

Probablemente verá una mejora (subida) en “Requests per second”.

De ahí se puede jugar un poco cambiando los parámetros de xCache como xcache.slots y xcache.size, o xcache.ttl para alcanzar mejores resultados.

Lamentablemente, estas modificaciones simples no permiten mejorar mucho la eficiencia del servidor. Pero sí se puede mejorar mucho todavía, gracias a la gestión de variables de xcache.

Para usarlo, nada más simple:

if (xcache_isset(‘un_nombre_de_variable_para_poner_en_cache’)) {

$variable = xcache_get(‘un_nombre_de_variable_para_poner_en_cache’);

} else {

$variable = query_pesado();

xcache_set(‘un_nombre_de_variable_para_poner_en_cache’,$variable);

}

Dicha gestión funciona para todo el sistema, es decir que la memoria usada por el proceso es compartida con otros procesos… si se tratan de varios usuarios repartidos sobre varios portales, se tiene que ser muy cuidadoso a nivel de la seguridad de la información almacenada, pero también se tiene que diseñar para que no aparezca la información de un portal en el otro.

Para esto, se puede usar, por ejemplo, un prefijo del nombre de variable con el nombre del URL del portal…

Por ejemplo, xcache_set(‘example_com_global_users_online’,$online);

No se recomienda poner ahí ninguna información personal de usuarios. Solamente datos genéricos válidos para todo un portal, y que no necesiten ser refrescados. Por ejemplo, en Chamilo mostramos siempre en la cabecera una cantidad de usuarios en línea. Pues si bien esta cantidad puede variar muy rápidamente, no importa tanto que sea la cantidad exacta, ya que a partir de 100 usuarios empieza a ser difícil saber quienes están o no están conectados.

Por lo tanto, esta información puede ser almacenada en la memoria caché compartida de xCache, y evitamos así un query bastante pesado a la base de datos para cada página refrescada o abierta: una cantidad tremenda cuando 900 usuarios simultáneos se conectan a un portal. Este pequeño cambio dió un resultado tremendo de entre 20 y 75% de respuestas adicionales por segundo en varios servidores, por lo que recomiendo altamente esta pequeña mejora

De la misma forma, información pre-calculada del header o del footer, un marcador de la hora actual, etc, pueden fácilmente ser almacenados para todo un portal por 60 segundos: suficiente para aliviar el servidor de estas pequeñas pedidas que rápidamente suman y lo hacen más lento.

Poniendo algo de 8 variables muy pedidas en caché de esta manera, se obtiene fácilmente 20% de eficiencia adicional, según el Apache Benchmark.

ab -c50 -n1000 http://example.com

Para finalizar, si usted está desarrollando un sistema para grande distribución (por ejemplo una plataforma de software libre), debería pensar bien en los casos de uso posibles, entre otros el alojamiento de varios portales con URLs distintos en un mismo servidor (usar prefijo como arriba) y el uso dentro de muchos scripts y contextos distintos (otro prefijo, puede ser).

Building a cache cleaner from devel for Drupal 5

In a particular context, it happened to me to have to reproduce a cache cleaner feature for Drupal 5 without installing the Devel module.

Although this article doesn’t create anything new, I thought it might be worth explaining in case this would help someone someday.

The idea is to re-use the code from the devel module and put it into another module for re-use without having the weight corresponding to a new module installation.

The code from the devel module (devel.module) looks like this:

/**
* Implementation of hook_menu().
*/
function devel_menu($may_cache) {
$items = array();

if ($may_cache) {
$items[] = array(‘path’ => ‘devel/cache/clear’,
‘title’ => t(‘Empty cache’),
‘callback’ => ‘devel_cache_clear’,
‘access’ => user_access(‘access devel information’),
‘type’ => MENU_CALLBACK,
);

// … the rest of this hook is of no interest to us

The only thing we would need to modify here is the permission, to avoid creating a new permission just for this. For example, we could use the permission “access administration menu” which comes from the admin_menu module (and hack it along to add the feature).

Then the corresponding function to clean the cache looks like this:

/**
* Menu callback; clears all caches, then redirects to the previous page.
*/
function devel_cache_clear() {
// clear preprocessor cache
drupal_clear_css_cache();

// clear core tables
$core = array(‘cache’, ‘cache_filter’, ‘cache_menu’, ‘cache_page’);
$alltables = array_merge($core, module_invoke_all(‘devel_caches’));
foreach ($alltables as $table) {
cache_clear_all(‘*’, $table, TRUE);
}
drupal_set_message(‘Cache cleared.’);
drupal_goto();
}

The good thing about this function is that all the functions used in there are Drupal Core functions, so practically, declaring this menu item in any other module will make the feature available and usable.

So, how do we do it?

First, get the devel_cache_clear() function into the admin_menu/admin_menu.module and rename it to admin_menu_cache_clear(). There might be another function called admin_menu_clear_cache() over there, but the name is subtly different and you will only use it temporarily, so don’t fear.

/**
* Menu callback; clears all caches, then redirects to the previous page.
*/
function admin_menu_cache_clear() {
// clear preprocessor cache
drupal_clear_css_cache();

// clear core tables
$core = array(‘cache’, ‘cache_filter’, ‘cache_menu’, ‘cache_page’);
$alltables = array_merge($core, array());
foreach ($alltables as $table) {
cache_clear_all(‘*’, $table, TRUE);
}
drupal_set_message(‘Cache cleared.’);
drupal_goto();
}

Note that I have removed a module_invoke_all(‘devel_cache’) here (because devel does not exist).

Next, update the menu element to add a link to this function:

admin_menu_add_item($admin_menu, $mid_admin, array(
‘title’ =>    ‘Clear cache’,
‘path’ =>     ‘admin/user/clearcache’,
‘callback’ => ‘admin_menu_cache_clear’,
‘weight’ =>   -89,
‘type’ => MENU_CALLBACK,
));

The weight will allow the menu element to appear next to the leftmost element (very visible). Depending on the form of your menu, this element might rather look like this:

$items[] = array(
‘title’ =>    ‘Clear cache’,
‘path’ =>     ‘admin/user/clearcache’,
‘callback’ => ‘admin_menu_cache_clear’,
‘weight’ =>   -89,
‘type’ => MENU_CALLBACK,
);

Now make sure you don’t leave a syntax error around, save the module and reload your portal. You should now see the element appear in the menu, and clicking it should execute the cache clean command.

Clear cache option appears in admin-menu, without devel

Clear cache option appears in admin-menu, without devel module installed

You will also get onto an empty “users” page, but that’s not really of my concern for this quick hack. Ideally, you would have made a copy of the module before, and you would comment this new code once you don’t need it anymore. Finally, you would add an access control of the kind of “if(user_access(‘administer users’))” or whatever permission you feel OK with assigning this feature to.

Watch out when copy-pasting the code above, as the apostrophes seem to be transformed in non-apostrophes when pasting them, which produces syntax errors!

%d bloggers like this: