Este foro ya no está activo, así que no puedes publicar nuevas preguntas ni responder a las preguntas existentes.

Problema con script para leer archivos

21 de agosto de 2015

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?

php

Respuestas

#1

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:

  1. Antes de empezar el procesamiento, compruebas si existe un archivo llamado procesando
  2. Si existe, te sales del script porque eso significa que hay un script anterior que todavía no ha terminado.
  3. 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.
  4. 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 archivo procesando 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

21 agosto 2015, 9:53
#2

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

21 agosto 2015, 10:58
#3

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

21 agosto 2015, 11:12
#4

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

21 agosto 2015, 12:25
#5

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

21 agosto 2015, 12:31
#6

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

24 agosto 2015, 13:13