fopen file locking en PHP (tipo de situación de lector / escritor)

Tengo un escenario en el que un proceso de PHP está escribiendo un archivo aproximadamente 3 veces por segundo, y luego varios procesos de PHP están leyendo este archivo.

Este archivo es esencialmente un caché. Nuestro sitio web tiene un sondeo muy insistente, para datos que cambian constantemente, y no queremos que cada visitante acceda al DB cada vez que sondean, entonces tenemos un proceso cron que lee el DB 3 veces por segundo, procesa los datos, y lo descarga a un archivo que los clientes de sondeo pueden leer.

El problema que tengo es que, a veces, abrir el archivo para escribirlo lleva mucho tiempo, a veces hasta 2-3 segundos. Asumo que esto sucede porque está bloqueado por lecturas (o por algo), pero no tengo ninguna manera concluyente de probar eso, además, según lo que entiendo de la documentación, PHP no debería estar bloqueando nada . Esto ocurre cada 2-5 minutos, por lo que es bastante común.

En el código, no estoy haciendo ningún tipo de locking, y no me importa si la información de ese archivo se corrompe, si falla una lectura o si los datos cambian en medio de una lectura. Me importa, sin embargo, si escribir en él lleva 2 segundos, esencialmente, porque el proceso que tiene que pasar tres veces por segundo ahora saltó varios latidos.

Estoy escribiendo el archivo con este código:

$handle = fopen(DIR_PUBLIC . 'filename.txt', "w"); fwrite($handle, $data); fclose($handle); 

Y lo estoy leyendo directamente con:

 file_get_contents('filename.txt') 

(no se sirve directamente a los clientes como un archivo estático, recibo una solicitud PHP normal que lee el archivo y hace algunas cosas básicas con él)

El archivo tiene aproximadamente 11kb, por lo que no toma mucho tiempo leer / escribir. Muy por debajo de 1 ms.

Esta es una entrada de registro típica cuando ocurre el problema:

  Open File: 2657.27 ms Write: 0.05984 ms Close: 0.03886 ms 

No estoy seguro si es relevante, pero las lecturas suceden en solicitudes web regulares, a través de apache, pero la escritura es una ejecución de PHP de “línea de comando” hecha por el cron de Linux, no está pasando por Apache.

¿Alguna idea de lo que podría estar causando este gran retraso en la apertura del archivo?
¿Alguna sugerencia sobre dónde podría mirar para ayudarme a identificar la causa real?

Alternativamente, ¿puedes pensar en algo que podría hacer para evitar esto? Por ejemplo, me encantaría poder establecer un tiempo de espera de 50 ms para abrir y, si no se abre el archivo, salta hacia adelante y deja que la próxima ejecución del cron se ocupe de ello.

Una vez más, mi prioridad es mantener al cron latiendo tres veces por segundo, todo lo demás es secundario, por lo que cualquier idea, sugerencia o cualquier cosa es muy bienvenida.

¡Gracias!
Daniel

Puedo pensar en 3 posibles problemas:

  • el archivo se bloquea cuando lo leen / escriben llamadas de sistema php inferiores sin que usted lo sepa. Esto debería bloquear el archivo por 1 / 3s máx. Tienes períodos más largos que eso.
  • la memoria caché fs inicia una fsync () y todo el sistema bloquea las lecturas / escrituras en el disco hasta que se completa. Como solución, puede intentar instalar más RAM o actualizar el núcleo o usar un disco duro más rápido
  • su solución de “almacenamiento en caché” no se distribuye, y golpea la pieza de hardware que funciona peor en todo el sistema muchas veces por segundo … lo que significa que no puede escalar más agregando más máquinas, solo aumentando la velocidad del disco duro. Deberías echarle un vistazo a Memcache o APC, o incluso a la memoria compartida http://www.php.net/manual/en/function.shm-put-var.php

Soluciones en las que puedo pensar:

  • ponga ese archivo en un ramdisk http://www.cyberciti.biz/faq/howto-create-linux-ram-disk-filesystem/ . Esta debería ser la forma más sencilla de evitar golpear el disco con tanta frecuencia, sin otros cambios importantes.
  • usa Memcache Es muy rápido cuando se usa localmente, se escala bien y es usado por jugadores “grandes”. http://www.php.net/manual/en/book.memcache.php
  • usar memoria compartida. Fue diseñado para lo que estás tratando de hacer aquí … tener una “zona de memoria compartida” …
  • cambie el planificador cron para que actualice menos, tal vez implemente algún tipo de sistema de eventos, de modo que solo actualice la memoria caché cuando sea necesario, y no por tiempo
  • haga que el guión de “escritura” escriba en 3 archivos diferentes, y haga que los “lectores” lean uno de los archivos, al azar. Esto puede permitir una “distribución” del loking a través de más archivos y puede reducir las posibilidades de que un determinado archivo esté bloqueado al escribir en él … pero dudo que brinde ningún beneficio notable.

Debería utilizar algo realmente rápido si desea garantizar tiempos de apertura bajos constantes. Tal vez su sistema operativo esté realizando sincronizaciones de disco, confirmaciones de archivos de base de datos u otras cosas que no puede solucionar.

Sugiero usar memcached, redis o incluso mongoDB para tales tareas. Incluso podría escribir su propio daemon de caché, incluso en php (sin embargo, esto es totalmente innecesario y puede ser complicado).

Si está absolutamente seguro de que solo puede resolver esta tarea mediante este caché de archivos y está bajo Linux, intente utilizar un planificador de E / S de disco diferente, como la fecha límite, O (cfq Y disminuya la prioridad del proceso de PHP a -3 / -4).