Evitar ejecuciones múltiples en cron con PHP

Cuando tienes una aplicación web, los procesos cron son muy útiles para automatizar tareas de todo tipo: mantenimiento, background, … El problema que nos podemos encontrar es cuando el tiempo que tarda en ejecutarse un cron supera el tiempo de espera entre ejecuciones, con lo que nos encontramos con dos cron ejecutándose en el mismo momento, pudiendo repercutir negativamente en la propia tarea del cron.

Para evitar esta situación, el script al que hago referencia nos va a venir muy bien, porque añade bloqueos al cron para evitar que dos procesos se ejecuten. Una solución sencilla es crear un fichero, y si el fichero existe, pues no ejecutar el cron. El problema que tiene esto es que si el cron termina abruptamente, el fichero seguirá existiendo por no haberse borrado y el resto de procesos cron no se ejecutarán.

El autor en este script añade la posibilidad de saber si el cron sigue ejecutándose o se terminó incorrectamente, para lo cual en el fichero de bloqueo guarda el PID del proceso cron que lo crea y comprueba si el PID sigue existiendo. Este script sólo es útil en entornos linux.

How to use locks in PHP cron jobs to avoid cron overlaps

Vía / PHPDeveloper.org

Hacks para CodeIgniter

Ya he mencionado anteriormente que CodeIgniter me parece el mejor framework PHP que hay, por lo que considero que los hacks a los que hace referencia el artículo de Nettuts nos pueden venir muy bien a quienes usamos CI:

  • Autoload de modelos
  • Prevenir colisión de nombres de controladores y modelos
  • Validación de formularios con valores únicos
  • Ejecutar CI desde la línea de comandos
  • Usar CI para múltiples sitios
  • Permitir cualquier tipo de ficheros a la hora de subir ficheros

6 CodeIgniter Hacks for the Masters

Terrastore: base de datos documental

Terrastore es una BD documental distribuida que ofrece escalabilidad sin quitarle consistencia. Entre sus características encontramos:

  • Accesible mediante HTTP
  • Distribuida: permite distribuir los nodos por cualquier servidor de nuestra red
  • Elasticidad: permite quitar y poner nodos sin necesidad de parar el servicio o cambiar la configuración
  • Escalabilidad en la capa de datos: los documentos se dividen y reparten por los nodos con balanceo automático y unión de las partes
  • Escalabilidad computacional: las queries y updates se distribuyen por los nodos reduciendo el tráfico
  • Consistencia: ofreciendo consistencia para los documentos, garantizando que siempre obtendrás el último valor de un documento, con aislamiento de la lectura de posibles commits de modificación

Terrastore

Vía / High Scalability

Laboratorio: Twitter trends

Mi amigo Christian me ha comentado que la entrada anterior sobre Twitter sería más interesante si mostrara los datos sobre un término.

Pues dicho y hecho, tan sólo se necesitan dos scripts, uno para recuperar los datos y otro para mostrarlos. El primero habrá que ponerlo en el cron para que recupere los datos cada cierto tiempo (en mi ejemplo busco “google” cada 2 minutos).

Hay que tener cuidado porque Twitter da un máximo de 2000 actualizaciones nuevas, por lo que tendremos que ajustar los tiempos de consulta en Twitter.

El script que lee los datos es el siguiente:

<? php

function insertar($consulta, $ult) {
  global $db;

  $data = json_decode(file_get_contents('http://search.twitter.com/search?q='.urlencode($consulta).'&refresh=true&since_id='.$ult));
  $n = isset($data->total) && $ult != $data->max_id? $data->total:0;
  $db->queryExec('INSERT INTO estadisticas (fecha, n) values ('.time().', '.$n.')');
  if (!$ult) $db->queryExec("INSERT INTO opciones (clave, valor) values ('ultimo', ".$data->max_id.")");
  else $db->queryExec("UPDATE opciones SET valor = ".$data->max_id." where clave='ultimo' ");
}

$consulta = $_GET['q'];
// Limpio para poder usarlo en el nombre para la BD
$_consulta = preg_replace('/[^A-Z0-9]/i', '_', $consulta);

if ($db = new SQLiteDatabase($_consulta.'.db')) {
  $q = @$db->query("SELECT valor FROM opciones Where clave='ultimo'");
  if (!$q) {
    $db->queryExec('CREATE TABLE estadisticas (fecha real, n real, PRIMARY KEY (fecha));');
    $db->queryExec('CREATE TABLE opciones (clave text, valor text, PRIMARY KEY (clave));');
    $q = $db->query("SELECT valor FROM opciones Where clave='ultimo'");
  }
  $r = $q->fetchAll(SQLITE_ASSOC);
  $ult = 0;
  if (!empty($r)) $ult = $r[0]['valor'];
  insertar($consulta, $ult);
}
?>

Y el script que dibuja la gráfica es:

<? php
$desde = strtotime($_GET['desde']);
$hasta = strtotime($_GET['hasta']);

$consulta = $_GET['q'];
// Limpio para poder usarlo en el nombre para la BD
$_consulta = preg_replace('/[^A-Z0-9]/i', '_', $consulta);

if ($db = new SQLiteDatabase($_consulta.'.db')) {
  $q = $db->query("SELECT fecha, n FROM estadisticas Where fecha>".$desde." and fecha<".$hasta);
  $r = $q->fetchAll(SQLITE_ASSOC);
  foreach($r as $item) {
    $x[] = $item['n'];
    $l[] = $item['fecha'];
  }
}

header('Location: http://chart.apis.google.com/chart?chtt=Line+Chart&chts=000000,12&chs=1000x600&chf=bg,s,ffffff|c,s,ffffff&chxt=x,y&chxl=0:|'.implode('|', $l).'|1:|'.implode('|', $x).'&cht=lc&chd=t:75.00,16.66,0.00,8.33,100.00&chdl=Label+1&chco=0000ff&chls=1,1,0');

Actualización: Google Charts no deja meter muchos valores por lo que la gráfica de arriba sólo saca las 20 últimas actualizaciones

Estadísticas de uso de Twitter

El otro día me dio por pensar cuantos updates debería tener Twitter cada segundo viendo el número de actualizaciones que había por lo de #manifiesto. Para intentar calcularlo creé un script que cada 10 minutos escribiera un tweet en una cuenta guarra de Twitter. Guardé los datos en una BD de SQLite para luego trabajar con los datos obtenidos.

twitter_t.png

Pues los datos que obtuve durante 1 semana es que cada 10 minutos hay una media de 230.000 updates, cada segundo una media de 380 updates, cada hora 1.380.000 updates. Incluso se puede ver una caída en el servicio.

Librería OAuth para Twitter

Twitter OAuth PHP Class es una librería que nos vendrá muy bien para poder utilizar el API de Twitter usando OAuth, método de autenticación que no necesita que el usuario registre su usuario/contraseña en otras aplicaciones.

Un ejemplo de uso sería el siguiente:

$to = new TwitterOAuth($consumer_key, $consumer_secret);
$tok = $to->getRequestToken();
$request_link = $to->getAuthorizeURL($token);
$tok = $to->getAccessToken();
$to = new TwitterOAuth($consumer_key, $consumer_secret, $user_access_key, $user_access_secret);
$content = $to->OAuthRequest('https://twitter.com/account/verify_credentials.xml', array(), 'GET');
$content = $to->OAuthRequest('https://twitter.com/statuses/update.xml', array('status' => 'Test OAuth update. #testoauth'), 'POST');

Twitter OAuth PHP Class

Vía / PHPDeveloper.org

W2W: la web vista desde el P2P

Aviso: este post es simplemente una idea que se me ha ocurrido y que quería compartir, posiblemente no digo más que tonterías, pero bueno, quizás mis tonterías puedan tener algo de sentido común.

Estaba leyendo un post de High Scalability acerca de cómo construir web altamente escalables. El artículo, entre otras muchas cosas habla sobre la posibilidad de crear una red social que tenga la capacidad de soportar miles de millones de usuarios.

Esta magnitud de usuarios puede parecer futurista, pero los datos que aporta sobre Facebook y el crecimiento en número de conexiones a nivel mundial, hacen que estos datos puedan ser posibles en unos cuantos años:

[…] Para hacer este mágico trabajo, Facebook gasta cientos de millones de dólares en datacenters, tienen más de 30.000 servidores, 300 millones de usuarios activos, 80.000 millones de fotografías, sirven 600.000 fotos por segundo, tienen 28TB de caché, generan 25TB de logs por día y su caché sirve 120 millones de queries cada segundo.

A mi estos números me asustan, no sólo por la cantidad en sí de todo: servidores, fotos, queries, … sino porque si por algún casual alguna vez llego a estar metido en un proyecto de esta envergadura no sé si seré capaz de estar a la altura.

Pero si Facebook da miedo, la gente de Google son de otro planeta:

El sistema que Google está construyendo se llama Spanner, La meta de Spanner es soportar 10 millones de servidores, 1013 directorios, 1018 bytes de almacenamiento, extenderlo entre cientos y miles de localizaciones al rededor del mundo para aguantar 109 clientes.

El problema de las redes sociales es que los datos son muy activos: dependen de los usuarios y sus “amigos”, y al estar los usuarios siempre activos, los datos son escasamente cacheables y su recuperación altamente problematica. Si a esto le sumamos la cantidad de usuarios que puede llegar a tener una red social exitosa, hace que me plantee lo siguiente: ¿vamos por el buen camino?

Últimamente se habla mucho de la nube o el cloud computing, pero el planteamiento que a veces se plantea para mí no es el más correcto: desperdigar mi aplicación por Internet (mi aplicación en mi servidor, la BD en Google y las imagenes en Amazon). Yo no estoy en contra de la nube, ni mucho menos, pero quizás en vez del planteamiento guay2.0 que a veces se le da, se podría replantear de distinta forma: el P2P. ¿Por qué es necesario que toda mi información, amigos, fotos y vicios estén en Facebook si no tengo necesidad de ello? En Facebook hay 300 millones de usuarios activos y yo tengo 100 amigos (bueno, yo no tengo ninguno porque no uso Facebook, pero esta es otra historia), eso es el 3×10-5% de la capacidad de Facebook. ¿De verdad necesito Facebook?.

¿Qué idea absurda planteo? La comunicación e intercambio de información entre webs y no entre usuarios. Es decir, yo tengo mis datos en un servicio, y este servicio comparte los datos con un protocolo parecido al P2P (el W2W) con otros servicios (ya sea el mismo u otros distintos). El servicio puede ser Facebook, Twitter, mi propio servidor o blog, una aplicación que tengo en el ordenador conectado todo el día, mi móvil de ultimísima generación, el Kindle, el netbook o una web genérica que se encarga de almacenar mis datos y fotos para que esté siempre online. Cuando yo me conecto a mi servicio y quiero ver lo que han escrito mis amigos o ver sus fotos, mi servicio lee mi lista de amigos (desperdigados en cientos de servicios) y accede a los servicios de cada amigo para recuperar la información. ¿Qué posibilidades ofrece el P2P? que si los datos no los tengo disponibles, se puedan recuperar de otro cliente. En el W2W sería igual, si mis datos no están disponibles porque mi servicio está caído, que se puedan recuperar mis datos de mis amigos, siempre que los permisos lo permitan (ambos son amigos míos o mi perfil es público).

w2w.png

¿Qué se gana con el W2W?

  • No tengo que darme de alta en Facebook porque tenga un amigo que está registrado y es allí donde comparte sus fotos, mi servicio se conectará a Facebook para recuperar sus fotos.
  • No hay necesidad de grandes aplicaciones con necesidad de soluciones drásticas de escalabilidad porque la información se reparte en muchos nodos/servicios.
  • Puedo seguir a mis amigos de Twitter desde otro servicio de microbloggin que me guste más
  • No hay necesidad de registrarse en cientos de aplicaciones para poder estar conectados con todo el mundo
  • Sería algo abierto y distribuido

Quizás a muchos les chirríe esta idea, sobre todo ahora que parece que el futuro está en tenerlo todo en la web, pero viendo la situación actual, los cientos de millones de dólares gastados en datacenters, los problemas de rendimiento de Twitter, los cambios de políticas de privacidad de Facebook, y otras historias, creo que mi idea, a parte de absurda, puede tener algo de futuro.

Aunque el término W2W es algo que me acabo de inventar, no sé si a alguien se le ocurrió lo mismo antes que a mi, no pretendo ser innovador ni original, así que si a alguien conoce algún post que diga lo mismo, por favor que me lo envíe.

Por cierto, interesante web que plantea algo similar para crawlear webs: 80legs.

MySQL 5.5 Milestone 2

MySQL ha sacado la primera versión Milestone (o RC) de la 5.5, la cual trae algunas novedades interesantes:

  • Replicación semisíncrona: la cual se asegura de que al menos un esclavo confirme la transacción, habrá que ver que tal es esto para el rendimiento.
  • Nueva sentencia LOAD XML
  • Particionamiento por rangos y listas, que por cierto, yo desconocía que la versión Community de MySQL admitía particionamiento.
  • Versión mejorada del plugin de InnoDB

What is new in MySQL 5.5

Cosas que he aprendido sobre desarrollo web

Tanto en mi primer blog como en Sentido Web, el motivo fundamental ha sido compartir conocimientos con los demás. No hablo de enseñar, sino de aprender y compartir. Pero de donde más se aprende es de la experiencia que se adquiere trabajando y dándote de bruces con situaciones o problemas. Y basándome en mi experiencia, que ni es la mejor ni la más extensa, me gustaría compartir qué cosas he ido aprendiendo sobre desarrollo web.

Vales lo que sabes

Para dedicarse al desarrollo web hay que tocar todos los palos, no vale con saber sólo HTML y CSS o sólo Javascript (o JQuery) o sólo PHP. Está claro que no todo el mundo sabe diseñar, pero si debería saber maquetar. Y una maquetación no es nada sin la parte cliente, porque siempre es necesario cierta interacción. Y ahora no hay ni una sola aplicación web que no requiera una aplicación en el servidor, ya sea en PHP o Java o la que más te guste.

Es absolutamente necesario aprender día a día, porque lo que hoy es actualidad, dentro de unos meses estará deprecated. Suscríbete a unos cuantos blogs y aprende algo nuevo cada día.

No es jQuery todo lo que reluce

jQuery está genial, pero nos olvidamos de algo muy importante: el Javascript. Hay algunos que sólo se preocupan de aprender jQuery, cuando lo realmente importante es aprender Javascript. Si sabes manejarte en Javascript, te da igual usar jQuery, Mootools o simplemente hacer tus propias librerías. Además, no se puede aprender realmente jQuery si no conoces la base.

Lógicamente, esto es extensible a cualquier framework Javascript, los cuales nos facilitan mucho la vida, pero también su mal uso hace que caigamos en vicios a la hora de programación, por ejemplo:

$('#id').fn();
$('#id').bar();
// en vez de
$obj = $('#id');
$obj.fn();
$obj.bar();

No sólo de SQL vive la BD

Viva el noSQL. Se nos enseña todo sobre el SQL y las bases de datos relacionales, pero no siempre es necesario o recomendable usar las bases de datos relacionales. Cada día se habla más del noSQL, y no en plan moda con el cloud computing, sino con datos y casos prácticos.

El noSQL es bastante útil para temas de rendimiento, aunque existen otras alternativas como Sphinx o HBase que también nos pueden venir muy bien.

Cachea, cachea

Cachear es una parte fundamental de cualquier desarrollo, evita que la BD se cargue de consultas repetitivas, mejora el rendimiento y ahorra recursos. No hablo únicamente de grandes aplicaciones, en cualquier cosa debería plantearse el cachear el contenido. Importante son las llamadas Ajax, los plugins de WordPress que acceden a contenidos externos u otros desarrollos parecidos.

Divide y vencerás

Grandes problemas, pequeñas soluciones… bueno, muchas pequeñas soluciones. Dividir los problemas en pequeñas tareas será la solución más óptima que encontraremos.

Uno de los mayores problemas con los que nos encontramos en nuestras aplicaciones son las consultas lentas a la BD. Cuando nos enseñan bases de datos algo en lo que insisten es en la normalización y en las joins. Las joins pueden ser un problema cuando se trata de tablas muy grandes y tenemos que juntarlas. Por ejemplo, tenemos dos tablas que se unen mediante una primary key y una foreign key, la consulta sería algo así:

/* Da igual usar left joins, sigue siendo lento */
select * from tablaA as A, tablaB as B where A.id = B.idA and A.campo = 'lo que sea'

En los casos en que las tablas son muy grandes, es preferible obtener primero los IDs de una tabla y luego realizar la consulta sobre una de las tablas:

/* Obtengo los IDs */
select id from tablaA where campo = 'lo que sea';
/* Almaceno los IDs obtenidos y ejecuto la query principal */
select * from tablaB where id in (id1, id2, id3, id4, id5, ..., idN)

Y si por algún motivo hay que juntar los datos de tablaA y tablaB, se realiza mediante código.

Un clásico, no reinventes la rueda

Esta premisa es aplicable a muchos aspectos. Hay herramientas que ya hacen lo que queremos nosotros: blogs, redes sociales, agregadores, portales, … a no ser que quieras algo realmente específico y muy personalizable, es recomendable que uses las herramientas que ya existen, las cuales tienen detrás a mucha gente desarrollando, y es bastante posible que lo hagan mejor que nosotros.

Otro tema que incluir es el de los frameworks, algunos están en contra de su uso, pero yo creo que usar un buen framework es bastante útil. Si nos ponemos a pensar, un framework es la base para desarrollar un proyecto, y queramos o no, o usamos una base ya hecha o no la creamos nosotros: necesitaremos la capa de abstracción de la BD, seguridad, logs, cache, MVC… al final nos crearemos nuestro propio framework, el problema está en realizarlo correctamente o en elegir un framework que sea eficiente.

Lo quieras o no, habrá cambios

Desarrolla tu aplicación pensando en el futuro, en las posibles mejoras o características nuevas que tendrá. Sé realista, tampoco te tienes que ir al extremo más lejano, basa tus expectativas en datos objetivos. Siempre habrá que realizar mejoras, nos encontraremos con fallos, porque toda aplicación web tiene sus bugs. Querremos quitar cosas y añadir otras nuevas.

Para no volverte loco con los cambios, o que se vuelva loco el que tenga que encargarse de tu código, es absolutamente necesario usar el patrón MVC, comentar bien el código, tener una herramienta de gestión de errores y tareas, y un SVN o similar.

Conclusiones

No dejes de aprender, ten cuidado de las “modas”, desarrolla eficientemente y huye de comerciales y vende motos.

¡Ah!, se me olvidaba, como diría un amigo: haz lo que yo te diga, no lo que yo haga.

Parseador HTML para PHP

PHP Simple HTML DOM Parser es una librería PHP que permite parsear de forma muy sencilla HTML.

Es necesario PHP5+, admite HTML inválido, permite buscar etiquetas usando selectores como jQuery y extraer el contenido.


// Create DOM from string
$html = str_get_html('<div id="hello">Hello</div><div id="world">World</div>');
$html->find('div', 1)->class = 'bar';
$html->find('div[id=hello]', 0)->innertext = 'foo';
echo $html; // Output: <div id="hello">foo</div><div id="world" class="bar">World</div>

PHP Simple HTML DOM Parser

Vía / DZone