PiensoPienso: Porque esta lento este programa?
Tengo una tabla Documentos de la forma
Hay un webpanel (GeneXus Xev1) que muestra todos los documentos de los clientes
Documentos del Cliente: MOOGLE //Filtro por ese cliente
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
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?
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 |
Nombre | Fecha |
Como dominar el mundo | 2000/01/15 |
Como hacer el primer billon de dólares | 2005/03/09 |
En el evento Load de la grilla se tiene
for eachwhere ClienteId=&ClienteId&DocumentoNombre=DocumentoNombre&DocumentoNombre.Link=PathToUrl(DocumentoBlob)&DocumentoFecha=DocumentoFechaendfor
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?
Got an idea:
ResponderBorrarEl 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
Concuerdo con Gonzalo, pero en lugar de definir un webpanel intermedio, yo pondría ese for each en un evento click sobre la variable
ResponderBorrarPa' mi que esta corriendo el server en un 386 con 16mb de ram... pero eso solo una suposición :D
ResponderBorrarTengo 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.
ResponderBorrarYo 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).
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.
ResponderBorrarYo 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).
Algo que estoy asumiendo es que existe un índice por Cliente ;-) ¿existe?
ResponderBorrarNo 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).
Gonzalo:
ResponderBorrarDetectaste 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.
Federico:
ResponderBorrarTu 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.
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.
ResponderBorrarGracias por los comentarios.
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.
ResponderBorrarMe 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.
Gustavo:
ResponderBorrarEl 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...
Lo que uso es una
ResponderBorrartabla 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
Tano:
ResponderBorrarLa 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.