Loaders de Tablas y de Lookups

Inicio Foros Foro principal Desarrolladores Loaders de Tablas y de Lookups

  • Este debate está vacío.
Viendo 3 entradas - de la 1 a la 3 (de un total de 3)
  • Autor
    Entradas
  • #31377
    Javier Ader
    Participante

    Buenas. Sigo intentando ver formas de mejorar tiempos de carga de las ventanas jajaja.
    Bueno, el tema primero vino por MTable.Loader: la idea del mismo si no me equivoco es cargar asincrónicamente las filas de la tabla. Supongo que con la idea de dar un mejor tiempo de respuesta y explotar la concurrencia. Ahora, me parece que la concurrencia simplemente no se explota y se crean threads de manera innecesaria.
    Pongo un par de consideraciones y dudas que iba escribiendo mientras analizaba el código:
    (clases en cuestión: MTable y MTable.Loader; métodos MTable.open() y MTable.Loader.run(); pero cosas similares se aplica a MLookup y MLookup.MLoader )

    1) Si la cantidad de filas son pocas el costo de crear el thread y ejecutarlo casi seguro que es mayor que no crearlo (caso emblemático: en zooms en donde se va a ver una sola fila!). Crear un thread que a mi parecer es innecesario posiblemente no tenga mucha penalización bajo Linux; pero bajo Windows seguro que es otro tema ….
    2) De todas maneras el thread creado actualmente no tiene ningún beneficio a nivel de acceso a base de datos. El Loader.open() YA trae TODAS las filas desde la base de datos… ( el executeQuery se hace dentro de Loader.open(); hay un stmt.setFechsize que podría darle un poco sentido a esto, pero esta comentado… y probablemete esto último sea los mas sensato; no se si el driver JDBC se banque este tipo de cosas cuando se comparten conexiones). El tema es que este open() no es ni siquiera ejecutado dentro del thread, si no que es invocado desde el thread “gui” que crea Loader (e.d el trabajo realmente pesado, esto es acceder al servidor de base de datos y traer tooodas las filas, no se hace en el thread iniciado por el Loader).
    Esto ademas trae otros problemas….. el driver JDBC de posgres va a cachear el resultado en memoria por sentencia… este memoria solo se va a liberta despues del que Loader invoque a Loader.close()[que invoca a ResultSet.close()]. El diseño actual implica que pueden haber en memoria muchas datos cacheados a nivel de JDBC, que ademas no son consumidos rapidamente (esto incluso se empeora debido a que el Loader se va a “dormir” cada vez que consume 250 filas….)
    4) Las ventanas si no me equivoco ya se abren actualmente en su propio thread; no le veo mucho sentido a crear otro thread por cada tabla que muestre la ventana…

    Bueno, creyendo que esto podia ser un mejora modifique MTable, para que no llame a Loader.start(), si no que directamente llame a Loader.runDirect() [basado en el run() actual; basicametne hace los mismo pero no se va ‘dormir’]
    Los cambios que hice son (si no se entiende bien, subo el MTable.java modificado a algun lado y pongo el link)
    MTable.java

    Code:
    //test NO threads (MIO): agregado
    // setear a true cuando no se quiera
    // crear un thread nuevo para el loader
    private boolean useNoThread = true;
    //fin test

    public boolean open() {

    … sobre el final:
    if( m_rowCount > 0 ) {

    //test NO threads (MIO)
    if (useNoThread)
    {
    // no crea un thread ; se dispara
    //el evento aca para evitar posibles race conditions
    System.out.println(“Corriendo Loader.runDirect() para tabla ” + m_tableName);
    m_loader.runDirect();
    m_loader = null;
    fireDataStatusIEvent( “” );

    } else
    //fin test
    m_loader.start();

    } else {
    m_loader.close();
    }

    m_open = true;

    //

    m_changed = false;
    m_rowChanged = -1;

    return true;
    } // open

    public Object getValueAt( int row,int col ) {


    //test NO threads (MIO): para evitar un access null
    if (m_loader != null)
    //fin test
    while( (row >= m_buffer.size()) && m_loader.isAlive() && (loops < 15) ) { log.fine( "Waiting for loader row=" + row + ", size=" + m_buffer.size()); try { Thread.sleep( 500 ); // 1/2 second } catch( InterruptedException ie ) { } loops++; } ... ... } //DENTRO DE LA CLASE Loader NO MTable; basado en Loader.run() public void runDirect() { log.info( "" ); if( m_rs == null ) { return; } try { while( m_rs.next()) { // Get ALL Data Object[] rowData = readData( m_rs ); // add Data MSort sort = new MSort( m_buffer.size(),null ); // index m_buffer.add( rowData ); m_sort.add( sort ); } } catch( SQLException e ) { log.log( Level.SEVERE,"run",e ); } close(); } //runDirect()

    Bueno, hice un par de test abriendo la ventana de Entidades Comerciales varias veces (tomando los milisegundos que tardaba AWindow.initWindow() como hice en el otro thread) y no me dio muchas mejoras con respecto a unos test llevados a cabo antes (los que están en el otro thread de “accesos a base de datos”); PERO TAMPOCO me genero peores tiempos que antes (esto probaría, que lo que se el acceso real y completo a las filas de las tablas de la ventada se da en Loader.open() y no en Loader.run()). Bueno, hay que tener en cuenta que no lo probe base “real”, si no sobre una base que que genera la instalción: lo ideal para la prueba tiene que ser el acceso a una tabla que tengas mas de 250 items; como lo debe ser por ej Artículos en casi cualquier instalación real; ahi el Loader, no solo raletanría las cosas por que un thread en si un recurso costoso a nivel de SO, sino porque además se iría a dormir bastantes veces antes de terminar de consumir un ResultSet que YA esta en memoria!!!)
    Así que probé con la ventana mensajes (System-> Mensajes):

    System->Mensajes (1243 filas; sin tener en cuenta la primer carga de la ventana;intentando darle el Ok lo mas rápido posible apretando el Enter al dialogo de búsqueda)
    [OBS: como el ‘sentido común’ me lo hacia suponer, MTable.open() es llamado al abrirse la ventana solo para la tabla asociada a la pestaña “principal”; si uno entra a navegar entre las pestañas aparecen los mensajes de debug asociados al runDirect(); pero solo cuando se cambia de pestaña]

    Loader.runDirect()
    1)
    Corriendo Loader.runDirect() para tabla AD_Message
    Tiempo de carga de AWindow.initWindow (mls): 3547
    2)
    Corriendo Loader.runDirect() para tabla AD_Message
    Tiempo de carga de AWindow.initWindow (mls): 3500
    3)
    Corriendo Loader.runDirect() para tabla AD_Message
    Tiempo de carga de AWindow.initWindow (mls): 3500
    4)
    Corriendo Loader.runDirect() para tabla AD_Message
    Tiempo de carga de AWindow.initWindow (mls): 3422
    5)
    Corriendo Loader.runDirect() para tabla AD_Message
    Tiempo de carga de AWindow.initWindow (mls): 3594
    6)
    Corriendo Loader.runDirect() para tabla AD_Message
    Tiempo de carga de AWindow.initWindow (mls): 3703

    promedio 3544 mls

    Loader.Run() //original, seteando MTable.useNOThread a false

    1)
    Tiempo de carga de AWindow.initWindow (mls): 4766
    2)
    Tiempo de carga de AWindow.initWindow (mls): 4313
    3)
    Tiempo de carga de AWindow.initWindow (mls): 4313
    4)
    Tiempo de carga de AWindow.initWindow (mls): 4235
    5)
    Tiempo de carga de AWindow.initWindow (mls): 4172
    6)
    Tiempo de carga de AWindow.initWindow (mls): 4140

    promedio 4323

    Diferenncia: 779 mls
    Speepup: 773/4323 = 17%

    Conclusiones:
    – cuando se carga una ventana cuya primer tab tiene menos de 250 filas, los dos casos no parecen tener muchs diferencias(más allá de que el Loader.run() original consume mas recurso; esto no se nota mucho ya que al momento de carga solo es creado un Loader para la pestaña principal). Pero cuando tiene mas de 250 las diferencias empiezan a ser notables (en el caso de Mensajes el thread se debe ir a dormir 4 veces)
    – esto no solo afecta a la primer tab; si no también a la carga de tabs “hijos”; pero de nuevo, esto se debe hacer mas visible cuando el tab “hijo” tiene muchas filas. Un escenario real para este caso puede ser pestaña Precios de Productos de la ventana Listas de Precios, ya que en una lista de precios pueden existir potencialmente muuuuchos productos; digamos más de 3000 (idem para las visualizaciones de stock, en donde cada producto ademas puede aparecer mas de una vez….); otro que se puede verificar en la instalación “de prueba” es en System->Elemento; seleccionar AD_Client_ID y luego ir a la pestaña “Usado en Columna” (710 filas)
    – las diferencias, al menos en mi maquina (que ya dije, es bastante vieja), me dieron la sensación de ser incluso percibidas subjetivamente (ya que por ej, no se esta hablando de 11 segundo contra 9; acá pueden aparecer escenarios como 4 segundos contra 3; el “speeup” siempre es mas perceptible sobre tiempo cortos, aun cuando represente el mismo porcentaje)

    PD: ahora, lo que voy a poner en otro thread (este ya quedo muy largo) es el caso de los loaders de Lookups (MLookup.MLoader). Este caso es similar salvo 2 grandes diferencias
    1) Los thread MLookup.MLoader si ejecutan el executeQuery() dentro del método run() [e.d, en un thread independiente creado a partir dle thread “gui”]
    2) Acá SI se crean muuuuuuuuchisimos threads dependiendo de la complejidad de la pestaña (caso extremo: poner un breackpoint en MLookup.MLoader.run() y abrir la ventana Entidades Comerciales…. 147 threads se crean casi de manera instantánea!!!! mi Windows XP no se banca esas cosas che…]
    3) la sentencia sql que se ejecuta NO esta muy optimizada (un MLookup tiene como maximo 1000 elementos; PERO el select que se ejecuta contra la base de datos NO usa la clausula LIMIT; asi que el server responde con todos las filas; lo cual no es muy bonito si por ej el MLookup esta asociada a un id de productos… esto se soluciona de manera simple, como voy a explicar mas adelante; básicamente usando un select anidado dentro de otro; el externo poniendo el limite; el interno idéntico al que se usa actualmente)
    4) no estoy seguro, pero me da la sensación que todos estos threads (uno por cada campo de tipo “lookup”: Table,TableDir, List, Search que pertenezca a una pestaña de la ventana; en el caso de Entidades Comerciales al parecer hay 147 de este tipo) NO se disparan en tiempo de carga de tab/tabla (como lo hace MTable.Loader); si no en tiempo de carga de la ventana (y tener 147 threads compitiendo todos contra el thread “gui”, termina siendo incluso peor que hacerlo todo en uno solo….).

    #34398

    Javier, como andas? Con Lucas (lMattar) queremos armar un componente y publicarlo al proyecto, para imputar centro de costos de la misma manera que esta estandarizada en otros soft de gestión. Te parece que lo encaremos juntos de a poco? Yo no programo mucho pero Lucas la semana que viene arranca con los cursos de programación, y tenemos ganas de encarar éste componente.

    No es mucho lo que hay que hacer:

    Básicamente, un botón en cada factura (también podría estar el botón en todos algunas ventanas que manejen Cargos) que presente un árbol (de centro de costos) y una grilla (de imputaciones porcentuales). Es decir, al completar una factura se la asigna a un centro de costos que se selecciona del árbol, y se le imputa un porcentaje, por ejemplo la factura de Compra de Material va en un 30% al centro de costos Sucursal Sur (A.1) y otro 30% al centro de costos Sucursal Norte (A.2) y el resto al centro de Costos Casa Central (D.1). De manera que la imputación para la factura quedaría expresada de la siguiente manera:

    FACTURA 00001
    Centro de Costo A.1 = 30%
    Centro de Costo A.2 = 30%
    Centro de Costo D.1 = 40%

    Esto siempre me lo pidieron en las demos que he hecho del Soft. Es muy importante cuando la empresa empieza a calibrar los gastos. En las facturas de venta se puede hacer lo mismo, pero imputando la ganancia a los mismos centros de costos, y al cierre del año podes ver cuanto gastaste y cuanto ganaste por cada centro de costo.

    Actualmente el sistema te permite imputar líneas de facturas proyectos y subproyectos, eso esta bueno pero no resuelve el problema de las imputaciones porcentuales del documento completo, ni la apertura en forma de árbol multinivel que pueden tener los centros de costos.

    Si te prendes, seguro que lo liquidamos mas rápido y contribuimos a la comunidad con un componente interesante. Podríamos abrir un hilo y nos vas diciendo por donde arrancar. = )

    #34399
    Javier Ader
    Participante

    Que tal Federico. A nivel de programación, diseño de tablas, metadatos, etc no tengo ningún problema en intentar darles una mano (este en mi fuerte…. soy principalmente un programador). Ahora, a nivel de análisis del problema en sí (e.d los requerimientos de usuario finales) tal vez se me complique un poco sin un poco más de detalles (creo que te entendí más o menos a lo que apuntas; pero el concepto de “centros de costos” me lo acabas de hacer conocer!), así que el tema de crear un thread mas que nada para explayarte (y que te consulte mis dudas …) un poco más me parece perfecto (de paso, tal vez de esta manera, con aportes de otros usuarios, se pueda hacer una mejora más general y/o flexible y/o configurable a nivel de requerimientos finales)
    Ya te agrego como contacto del MSN; cualquier cosa, contactame por ahí y claro, por este foro.
    Saludos

Viendo 3 entradas - de la 1 a la 3 (de un total de 3)
  • Debes estar registrado para responder a este debate.