Tengo un cron para ejecutar un script que lee archivos cada 5 minutos. El problema que tengo es que tengo un archivo de 10.000 registros y tardar mas de 5 minutos en procesarse con lo que se vuelve a ejecutar el script dejando el fichero a la mitad de la lectura.
¿algún consejo de como solucionar este problema?
Respuestas
Aunque es obvia, la solución más sencilla sería cambiar la frecuencia de la tarea de 5 a 10 minutos o más. La segunda solución que deberías probar es optimizar el código PHP que procesa los registros, ya que tardar más de 5 minutos para "solo" 10.000 registros parece mucho.
La otra solución que puedes probar es la siguiente:
- Antes de empezar el procesamiento, compruebas si existe un archivo llamado
procesando
- Si existe, te sales del script porque eso significa que hay un script anterior que todavía no ha terminado.
- Si no existe, lo creas tu para avisar a los posibles siguientes scripts que vas a empezar a procesar y nadie debe interrumpir tu trabajo.
- Cuando acabes de procesar, eliminas ese archivo llamado
procesando
El código sería muy sencillo gracias a la función touch()
de PHP:
$archivo = __DIR__.'/procesando'; if (file_exists($archivo)) { exit; } touch($archivo); // procesar registros ... unlink($archivo);
Un par de comentarios más:
- Para evitar problemas, además de comprobar si el archivo
procesando
existe, yo comprobaría que no es muy viejo (por ejemplo, que se creó hace menos de 1 hora). Si no, si alguna vez falla el script y el archivoprocesando
no se borra, nunca volverías a ejecutar el proceso. - Si tienes tiempo, la forma más correcta de hacer lo que te indico antes es usar los locks mediante la función flock() de PHP.
@javiereguiluz
el tiempo de carga puede ser porque procesa un archivo xml que cada cabecera representa una entidad, y su contenido son 10k entidades que ademas tienen relación con la principal.
¿Como podría optimizar el código para mejorar los tiempos de lectura?
protected function execute(InputInterface $input, OutputInterface $output) { // Obtener archivos a importar $finder = new Finder(); $finder->files()->name('*.xml')->depth(0)->in('./app/imports'); // Si hay archivos se importan if ($numFiles = $finder->count()) { $files = $finder->files(); ... foreach($files as $file) { // Leer fichero XML $xml = simplexml_load_file($file); // Leer tags foreach($xml->readings->tag as $item) { // procesa las entidades ... } ... $this->em->flush(); // Mover el archivo a procesados $fs = new Filesystem(); $fs->copy($file, './app/imports/processed/' . $file->getFileName()); $fs->remove($file); } } } '''
@ZaoIsmael
Todo el código que muestras parece muy correcto, así que la verdadera carga de trabajo debe estar en el bloque que procesa las entidades. Si usas un profiler como Blackfire podrías ver si hay alguna parte de ese script que consume un tiempo desproporcionado.
Por otra parte, como parece que estás usando Symfony, desde la versión 2.6 dispones del LockHandler que permite hacer lo que te comentaba antes pero de forma más sencilla y mucho más profesional.
@javiereguiluz
Muchas gracias Javier, con LockHandler he solucionado el problema.
Ahora estoy intentando conseguir bajar los tiempos de carga, ya que un archivo de 10k registros tarda una hora en cargarse.
@ZaoIsmael
Disculpa que insista, pero creo que la mejor manera de reducir los tiempos es usar un profiler (Xdebug, XHProf, Blackfire, etc.) para medir la ejecución del comando y ver realmente quién está consumiendo la mayor cantidad de tiempo, memoria, etc.
@javiereguiluz
El problema que tenia era a la hora de hacer flush en doctrine2 y almacenar 10k entidades.
Lo he conseguido solucionar con Bulk Inserts y he dejado el proceso en 2 minutos.
@ZaoIsmael