PiensoPienso: Porque esta lento este programa?

Tengo una tabla Documentos de la forma


DocumentoId* N(10) Autonumber
ClienteId C(10)  //Codigo de cliente
DocumentoNombre C(256)
DocumentoFecha Date //Fecha del documento
DocumentoKBytes N(10) //Tamaño en KBytes
DocumentoBlob Blob //Contenido del documento

Hay un webpanel (GeneXus Xev1) que muestra todos los documentos de los clientes

Documentos del Cliente:  MOOGLE    //Filtro por ese cliente
Nombre Fecha
Como dominar el mundo 2000/01/15
Como hacer el primer billon de dólares 2005/03/09
Haciendo un click en el nombre del documento, se puede bajar/abrir el documento en la maquina del cliente.
En el evento Load de la grilla se tiene

for each
where ClienteId=&ClienteId
&DocumentoNombre=DocumentoNombre
&DocumentoNombre.Link=PathToUrl(DocumentoBlob)
&DocumentoFecha=DocumentoFecha
endfor

La funcion PathToUrl() devuelve el una URL con el link necesario para bajar el documento.


El cliente se queja que la consulta esta muy lenta y tiene razón.


Preguntas
1) Cual es el motivo para que el webpanel se ponga tan lento, cuando se tienen mas registros?
2) Como podría modificarse el webpanel para tener la misma funcionalidad pero mejor performance?

Comentarios

  1. Got an idea:

    El problema es que al recorrer los registros que mostras, el DBMS tiene que bajar TODOS los documentos al filesystem.

    Ahora, el tema es que no vas a querer "ver todos"

    Lo que haría, es tener un webpanel intermedio, que recibe el DocumentoId, y en el start hace:

    for each
    where DocumentoId=&DocumentoId
    link(PathToUrl(DocumentoBlob))
    endfor

    En el panel original, quitaría la linea que pedís el blob, para que no lo baje... y le pondria el link al webpanel intermedio pasandole el DocumentoId

    Eso, debería bajar sensiblemente la I/O al filesystem

    ResponderEliminar
  2. Concuerdo con Gonzalo, pero en lugar de definir un webpanel intermedio, yo pondría ese for each en un evento click sobre la variable

    ResponderEliminar
  3. Pa' mi que esta corriendo el server en un 386 con 16mb de ram... pero eso solo una suposición :D

    ResponderEliminar
  4. Tengo entendido que algunas cosas cambiaron en la Evo 1, por lo que asumiré que el problema no es el For Each, sino la función PathToUrl.

    Yo lo que probaría es tener un link a un procedure main HTTP en donde en la respuesta le hago un addfile y le paso PathToUrl del Document, asegurándome en el procedure http de asignarle correctamente content type, file name y esas cosas en la definición del http (no tengo a mano un ejemplo, pero esto lo hago desde GX 8 en adelante para tener un programa intermedio de bajada de archivos en donde además valido políticas de seguridad para acceso a los archivos y no permitir accesos por url)

    En el caso que no sean los tiempos de la función, lo otro que puede estar afectando es que el especificador GeneXus o el generador vea que en el for each está involucrado el blob y asuma la descarga del mismo en el "Local Storage Path" para el resultado, si cambias la lógica de programación por la que te menciono anteriormente pasándole por parámetro al programa http del id del documento el que tendría acceso al blob es el procedure y no el web panel.

    Si en realidad el problema es éste ultimo, en realidad coloca un evento en la variable y luego en ese evento click realiza el for each al documento, luego haces un link de redirección a lo que te retorna PathToUrl y tienes un comportamiento similar al del procedure (con algunos ínconvenientes, que no vienen al caso de este problema).

    ResponderEliminar
  5. Tengo entendido que algunas cosas cambiaron en la Evo 1, por lo que asumiré que el problema no es el For Each, sino la función PathToUrl.

    Yo lo que probaría es tener un link a un procedure main HTTP en donde en la respuesta le hago un addfile y le paso PathToUrl del Document, asegurándome en el procedure http de asignarle correctamente content type, file name y esas cosas en la definición del http (no tengo a mano un ejemplo, pero esto lo hago desde GX 8 en adelante para tener un programa intermedio de bajada de archivos en donde además valido políticas de seguridad para acceso a los archivos y no permitir accesos por url)

    En el caso que no sean los tiempos de la función, lo otro que puede estar afectando es que el especificador GeneXus o el generador vea que en el for each está involucrado el blob y asuma la descarga del mismo en el "Local Storage Path" para el resultado, si cambias la lógica de programación por la que te menciono anteriormente pasándole por parámetro al programa http del id del documento el que tendría acceso al blob es el procedure y no el web panel.

    Si en realidad el problema es éste ultimo, en realidad coloca un evento en la variable y luego en ese evento click realiza el for each al documento, luego haces un link de redirección a lo que te retorna PathToUrl y tienes un comportamiento similar al del procedure (con algunos ínconvenientes, que no vienen al caso de este problema).

    ResponderEliminar
  6. Algo que estoy asumiendo es que existe un índice por Cliente ;-) ¿existe?

    No se porqué las preguntas me movieron a pensar que el problema está en la pantalla y no en la definición de la tabla. Ahora me entró la duda de si la cantidad de documentos es tan grande si el problema en realidad está en la selección del índice de acceso por cliente. Si no existiera una índice y la cantidad de registros es muy grande es muy probable que se haga un table scan y la demora esté en retornar los registros de ese cliente (se puede sumar también el tema de que luego de eso exista un problema adición con lo que comenté en los anteriores comentarios).

    ResponderEliminar
  7. Gonzalo:
    Detectaste el problema principal. El problema es que se estan bajando a disco todos los documentos del cliente, pero solamente va a querer bajar algunos.

    La solucion que planteas, soluciona el problema principal.

    ResponderEliminar
  8. Federico:
    Tu solucion tiene un objeto menos que la de Gonzalo, por lo que la prefiero.
    Hay que definir el &DocumentoId en la grilla, como campo oculto.
    Se puede definir un evento

    Event DocumentoNombre.Click
    for each
    where DocumentoId=&DocumentoId
    link(PathToUrl(DocumentoBlob))
    endfor
    EndEvent

    Esto va a disminuir mucho las escrituras en disco, pues solo se van a pasar de la base de datos al filesystem, los archivos que el cliente quiere bajar.

    ResponderEliminar
  9. David: Muy buena observacion. En este caso, el indice por Cliente si existe, pero es bueno preguntarlo, pues los problemas de performance pueden tener diversas causas, y la navegacion en al base de datos, es una de las principales.

    Gracias por los comentarios.

    ResponderEliminar
  10. La consulta utiliza el índice? &ClienteId tiene la misma definición que ClienteId? Hay casos en que si no la tienen el DBMS _no_ usa el índice correcto.

    Me parece que si la cantidad de documentos por cliente es baja, la incidencia de la bajada de los Blobs debería ser también baja.

    ResponderEliminar
  11. Gustavo:
    El indice existe y es usado en forma correcta.
    Los Blobs son grandes (mas de 20mb) y el bajar del DBMS al FileSystem, si hace muy pesado la carga del Webpanel. A pesar que el Webpanel tiene paginado mostrando unos 20 registros, se pone muy lento cuando el cliente tiene varios blobs...

    ResponderEliminar
  12. Lo que uso es una
    tabla de "Blobs", por lo que en la tabla de "documentos" solamente grabo el BlobID y descargo el binario cuando el usuario requiera el documento.
    Saludos, Fabio

    ResponderEliminar
  13. Tano:
    La forma en que tu lo programas es correcta.
    Lo que no es intuitivo para todos los programadores es que la funcion PathToUrl() realice la copia desde la base de datos al filesystem, pues se piensa que solamente devuelve la url necesaria para descargar.

    Gracias por el comentario.

    ResponderEliminar

Publicar un comentario

1) Lee el post
2) Poné tu opinión sobre el mismo.
Todos los comentarios serán leidos y la mayoría son publicados.

Entradas más populares de este blog

El Sordo

StackOverflow Documentation

Paleta de colores en GeneXus