• Publicidad

SQL colgado en cgi

¿Ya sabes lo que es una referencia? Has progresado, el nível básico es cosa del pasado y ahora estás listo para el siguiente nivel.

SQL colgado en cgi

Notapor mgonzalez » 2010-11-09 08:58 @415

Hola.

Este problema lo he planteado antes. Se trata de un aplicación CGI que ejecuta recurrentemente una consulta SQL, dicha consulta está en un método de una clase. Leyendo por ahí vi un caso en que se estaba tratando de insertar muchos registros en una BBDD, pero que solo ingresaba una pequeña parte: http://www.forosdelweb.com/f12/insertando-multiples-registros-con-mysql-512695/. Aquí se le recomienda usar la función finish(), que traté de usar pero no logro superar el problema.

Aquí va el código del método del que hablo.

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. sub preparar
  2. {
  3.      my $self = shift;
  4.      my $tipo = shift;
  5.      my $visualizacion = shift;
  6.  
  7. #    $tipo = ($tipo) ?$tipo : 1;
  8.     $tipo ||= 1;
  9.  
  10.     print start_table({-width=>'100%'}) if (! $visualizacion);
  11.          
  12.     my $personal = $dbh->selectall_arrayref("Select a.id From Personas2 a Where exists (select 1 from atributos b where a.id = b.cod_atributo and b.ind_proc_docto = $self->{ind_proc_doc} And b.id_tipo_atributo = $tipo and b.id_dueno = $self->{dueno})", { Slice => {} } ) || die $self->error($self->{dbh}->errstr);
  13.         # Personas2 es una vista de una tabla en otra BBDD, pero en el mismo servidor.
  14.  
  15.     my $pers_resp=0;
  16.     my @array;
  17.         foreach my $pers (@$personal){
  18.         my $emp = new SBMP::Empleado($pers->{id});
  19.         if ($visualizacion){
  20.             push @array,&fixcase($emp->nombre);
  21.         } else {
  22.             print Tr(
  23.                         td({-class=>"bordercell", -width=>'100%'},&fixcase($emp->nombre))
  24.                     )."\n";
  25.             $pers_resp++;
  26.         }
  27.     }
  28.      if ($visualizacion){
  29.         return join " ; ", @array;
  30.     } else {
  31.         print end_table();
  32.         print hidden('tot_pers',$pers_resp);
  33.     }
  34.                 #aqui agregue esta sentencia, pero sin resultados
  35.         $personal->finish;
  36.  
  37. }
Coloreado en 0.003 segundos, usando GeSHi 1.0.8.4


Adicionalmente hay que agregar que se trata de SqlServer 7.0... también por ahí he investigado que esta BBDD va acumulando las sesiones y en este caso, como se trata de un método que es llamado persistentemente, debería ser el problema. Sin embargo haciendo pruebas, no tengo claro si el problema pasa por SqlServer o por Perl.

Muchas Gracias
mgonzalez
Perlero nuevo
Perlero nuevo
 
Mensajes: 28
Registrado: 2010-10-13 08:54 @412

Publicidad

Re: SQL colgado en cgi

Notapor explorer » 2010-11-09 12:33 @565

¿Has puesto

$|=1;

al principio del CGI?

Hablas de un cuelgue, pero no se ve nada raro en el código... ¿No será que lo que pasa, realmente, es un tiempo agotado (timeout)? ¿Sale algo en los registros (log) de actividad del servidor web?

Debes identificar qué parte es la que provoca el agotamiento del tiempo disponible. Yo veo dos: el tiempo que tarda en resolverse la consulta, y el tiempo de creación de los objetos SBMP::Empleado. Digo que sospecho que son esos, porque todo lo demás veo que no ocupa tiempo.
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14480
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Re: SQL colgado en cgi

Notapor mgonzalez » 2010-11-09 12:59 @582

Muchísimas Gracias por tu respuesta.

Cuéntame para qué es $|=1;

Sí, definitivamente es timeout, lo malo de todo esto es que no me permiten mover nada en la configuración de servidor, por ejemplo el tiempo de espera del IIS.

Al hacer pruebas y comentar este trozo de código el asunto anda bien. Así es que ahí empezó la investigación en lo que ocurre dentro del método. En este sentido, lo primero que hice fue optimizar al máximo la consulta.

Puede que el asunto esté en el objeto SBMP::Empleado. Sin embargo, el código que ejecuta es super simple:

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. sub new {
  2.  
  3. my ($class, $id) = @_;
  4. my $self;
  5. #esta parte del codigo la comente, tratando de optimizar.
  6. # if ($id =~ /\d+$/){
  7.     # $self = &select_hash("select tbl_empleado.*, Usuarios_new.Usuario from usuarios_new, tbl_empleado
  8.                 # where id_persona = $id and tbl_empleado.id = usuarios_new.id_persona");
  9. # } else {
  10.     # $self = &select_hash("select tbl_empleado.*, Usuarios_new.Usuario from usuarios_new, tbl_empleado
  11.                 # where usuario = ".$dbh->quote($id) ."and tbl_empleado.id = usuarios_new.id_persona");
  12. # }
  13.  
  14. if ($id =~ /\d+$/){
  15.     $self = &select_hash("select nombres, apaterno, amaterno from usuarios_new2 a, tbl_empleado2 b
  16.                where a.id_persona = $id and b.id = a.id_persona");
  17. } else {
  18.     $self = &select_hash("select tbl_empleado2.*, Usuarios_new2.Usuario from usuarios_new2, tbl_empleado2
  19.                where usuario = ".$dbh->quote($id) ."and tbl_empleado2.id = usuarios_new2.id_persona");
  20.  
  21. }
  22.  
  23. #print Dumper $self;
  24. return $self ? bless $self : undef;
  25.  
  26. }
  27.  
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4


El mensaje que envía es el siguiente:
Sintáxis: [ Descargar ] [ Ocultar ]
Using text Syntax Highlighting
HTTP/1.1 502 Error de la puerta de enlace o gateway Server: Microsoft-IIS/5.0 Date: Tue, 09 Nov 2010 18:10:45 GMT Content-Length: 199 Content-Type: text/html
Caducidad CGI
La aplicación CGI especificada ha excedido el tiempo permitido para procesamiento. El servidor ha eliminado el proceso.
Coloreado en 0.000 segundos, usando GeSHi 1.0.8.4


Gracias nuevamente.
mgonzalez
Perlero nuevo
Perlero nuevo
 
Mensajes: 28
Registrado: 2010-10-13 08:54 @412

Re: SQL colgado en cgi

Notapor explorer » 2010-11-09 16:44 @739

El $| controla el caché de salida. Si lo pones a un valor distinto de cero, desactivas el caché, por lo que todo print() sale inmediatamente (y no cuando se llenen los buffers de salida). De esa manera, si se va generando las filas de la tabla de forma muy lenta, con este método, al menos, vamos enviando información poco a poco al usuario.

Pero aquí, el problema no es mantener la conexión con el usuario, sino que el CGI está demasiado tiempo funcionando.

Como supongo que estás en Windows, uno de los problemas de Perl en Windows es el desastroso sistema de gestión de reserva de memoria. Puede llegar a ser 4 órdenes de magnitud más lento que el mismo programa en LInux (décimas de segundos en Linux, decenas de minutos en Windows).

Además, existe el problema de que si la consulta devuelve mucha información, puede llenar la memoria RAM del servidor, y entonces, comenzar la máquina a usar la swap. Esto lo viví una vez y era desastroso ver que un trabajo que ->la misma<- máquina, en Linux, lo resolvía en menos de 3 segundos, se traducía en casi media hora de espera, en Windows.

La solución fue dividir la consulta SQL en otras pequeñas, y hacer que la operación de join()/filtrado, la hiciese el propio Perl. Primero se comprobó que había memoria suficiente para que Perl recuperase todos los registros interesantes. Y luego, ya con toda la información en memoria, filtrar los que realmente nos interesaban.

Si SQLServer tiene soporte de LIMIT, podrías hacer pruebas para recuperar solo un cierto número de peticiones, y así, medir cuánto tiempo tarda en recuperarlos.

El cuello de botella puede estar en muchos sitperfectrijilloios... el controlador ODBC, el propio motor de base de datos, el número de registros, la complejidad de la consulta (el número de join() afecta bastante), el tamaño de la memoria RAM... No creo que el cuello de botella esté en el IIS, aunque nunca lo he controlado. Y no creo que tampoco sea Perl, porque no he leído nada al respecto.

Luego, en Internet, te encuentras con recomendaciones, como ésta donde recomiendan el buen uso de los índices de las tablas, para aumentar la velocidad.
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14480
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Re: SQL colgado en cgi

Notapor mgonzalez » 2010-11-11 16:12 @716

Me imaginé que el servidor de aplicación tenía algo que ver, pero, ¿por qué pasa con Perl y no con PHP, por ejemplo...? De hecho, hay una aplicación en PHP que vuela en esa máquina... Ahora, esta consulta está optimizada al máximo, y todas las que les antecede.

Bueno, muchísimas gracias por tu respuesta.
¡¡Saludos!!
mgonzalez
Perlero nuevo
Perlero nuevo
 
Mensajes: 28
Registrado: 2010-10-13 08:54 @412

Re: SQL colgado en cgi

Notapor explorer » 2010-11-11 19:13 @842

La parte del programa que has publicado, en el primer mensaje, tiene dos partes claramente separadas: la de la consulta y la de la generación de la presentación a partir de la consulta.

De las dos partes, sería interesante ver en cuál se pierde más tiempo. Lo podrías saber con, simplemente, unos print() que sacarán el tiempo que ha tardado cada parte en realizarse.

Algo así: al principio del programa importamos el módulo Time::HiRes:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
use Time::HiRes 'time';
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

y ahora ya podemos tomar mediciones de tiempo, en segundos, con precisión de milisegundos.

Vamos al código, y una línea antes de la consulta, ponemos
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. my $antes = time();   # toma el tiempo en este momento
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

y en el otro extremo, pintamos el tiempo que ha tardado en resolverse la consulta
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. print "Tiempo de la consulta (s): ", time()-$antes;
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4


Lo mismo podemos hacer para la parte de la representación. Obtendremos, en pantalla, el tiempo que tarda en ejecutarse cada parte (bueno, como esto es un CGI, habría que adaptarlo para que saliera de forma correcta en el HTML de la página generada o en los logs de actividad del servidor web. Para este último caso, usar warn() en lugar de print()).

Mirando con más calma la función, hay alguna cosa que se puede mejorar. Por ejemplo, parece que es muy distinto el código según tengamos un valor de $visualización u otro.

Lo que podemos hacer es sacar las distintas sentencias de bifurcación y crear una sola.

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. sub preparar
  2. {
  3.      my $self           = shift;
  4.      my $tipo           = shift;
  5.      my $visualizacion  = shift;
  6.  
  7.     $tipo ||= 1;
  8.  
  9.     ## Consulta
  10.     my $antes = time();
  11.  
  12.         # Personas2 es una vista de una tabla en otra BBDD,
  13.         # pero en el mismo servidor
  14.         my $personal
  15.             = $dbh->selectall_arrayref("
  16.                Select
  17.                    a.id
  18.                From
  19.                    Personas2 a
  20.                Where
  21.                    exists (
  22.                        select
  23.                            1
  24.                        from
  25.                            atributos b
  26.                        where
  27.                            a.id = b.cod_atributo
  28.                        and
  29.                            b.ind_proc_docto = $self->{ind_proc_doc}
  30.                        and
  31.                            b.id_tipo_atributo = $tipo
  32.                        and
  33.                            b.id_dueno = $self->{dueno}
  34.                    )
  35.                ",
  36.                 { Slice => {} }
  37.             )
  38.             or
  39.                 die $self->error($self->{dbh}->errstr)
  40.             ;
  41.  
  42.     warn "Tiempo de la consulta (s): ", time() - $antes;
  43.  
  44.     ## Formateo
  45.     $antes = time();
  46.  
  47.         # Leemos el personal y arreglamos sus nombres
  48.         my @array;
  49.         foreach my $pers (@$personal) {
  50.             my $emp = new SBMP::Empleado($pers->{id});
  51.             push @array, &fixcase($emp->nombre);
  52.         }
  53.  
  54.     warn "Tiempo de formateo (s): ", time() - $antes;
  55.  
  56.     ## Presentación
  57.     $antes = time();
  58.  
  59.         if ($visualizacion) {
  60.             return
  61.                 join " ; ", @array;
  62.         }
  63.         else {
  64.             print start_table({-width=>'100%'});
  65.  
  66.             foreach my $pers (@array){
  67.                 print
  68.                     Tr(
  69.                         td({-class=>"bordercell", -width=>'100%'}, $pers)
  70.                     )
  71.                     , "\n"
  72.                     ;  
  73.             }
  74.  
  75.             print
  76.                 end_table(),
  77.                 hidden('tot_pers', scalar @array)
  78.                 ;
  79.         }
  80.  
  81.     warn "Tiempo de presentación (s): ", time() - $antes;
  82.  
  83. }
Coloreado en 0.002 segundos, usando GeSHi 1.0.8.4

(Nota: que se pueda escribir, o no, la consulta SQL de esta manera, en varias líneas, depende del motor de base de datos que estemos usando).

Como ves, hemos simplificado mucho el código, dejando solo una sentencia condicional.

Por otra parte, aunque no soy nada experto en SQL, estoy sospechando que la consulta se puede simplificar en:
Sintáxis: [ Descargar ] [ Ocultar ]
Using sql Syntax Highlighting
                SELECT  
                    a.id
                FROM
                    Personas2 a, atributos b
                WHERE
                        a.id = b.cod_atributo
                    AND
                        b.ind_proc_docto = $self->{ind_proc_doc}
                    AND
                        b.id_tipo_atributo = $tipo
                    AND
                        b.id_dueno = $self->{dueno}
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4


Y, finalmente, de la creación de los objetos SBMP::Empleado, me parece que puede simplificarse esas consultas, si obtenemos los nombres de las personas por adelantado.

O, si sabemos que hay muchas repeticiones en la obtención de esos datos, usar el módulo Memoize con esa función.

En fin, que posibilidades hay muchas. Pero si aún así, el responsable es Perl, habrá que pensar que el problema está en el controlador que dialoga con la base de datos.
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14480
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Re: SQL colgado en cgi

Notapor mgonzalez » 2010-11-16 14:22 @640

Amigo, de verdad estoy muy agradecido de tu ayuda. Como se trata de una de las tantas cargas de trabajo que me asignan tuve que dejarla de lado por un tiempo. De todas formas pruebo lo antes posible.

Saludos
mgonzalez
Perlero nuevo
Perlero nuevo
 
Mensajes: 28
Registrado: 2010-10-13 08:54 @412


Volver a Intermedio

¿Quién está conectado?

Usuarios navegando por este Foro: No hay usuarios registrados visitando el Foro y 7 invitados