#34805
Javier Ader
Participante

(Haaaa se me borro el post que estaba escribiendo…)
Gracias fcristina; en cuanto tenga un poco de tiempo pongo la cache de MFields asociadas a una determianda pestaña (el tema es que aca no es tan simple y tuve que hacer un par de modificaiones; agregar un par de campos en MField); con esta cache y un par de otras mejoras logro crear un MWIndowVO usando solo 2 accesos a la base de datos (que tambien se podrían evitar con otras caches!); paso de cerca de 6 segundos para la ventana a EC a algo asi como 0.5 segundos (ok, despues de esto, hay otros accesos a la base de datos, por lo cual la carga no es tan rapida como desearia).

De cualquier manera pienso que hay que modificar o extender la funcionalidad de CCache, por que todas estas mejoras son buenas para el usuario final, pero para el desarrollador pueden joder (ya que la cache evita ver rapidamente los cambios de diccionario…). El proceso “Vaciar cache” debería andar; el tema es que tal vez sea bueno mejorar un poco esto para que una cache pueda declarar multiples tablas de pendientes (no solo una) y que el proceso (probablemente un Form mas que un proceso) permita visualizar datos de estas caches y opcionalmente vaciar solo algunas.
Otro tema, que dificulta este tipo de caches es que el codigo a medida que va construyendo estos objetos hace uso del contexto para ciertas cosas; esto esta bien, pero el codigo debería modificarse para que en general
1) busque en una cache determinada
2) si no esta en cache, genere el objeto, pero SIN tener en cuenta el contexto
3) guarde en cache el objeto si se ejecuto el punto
2 (esto es, la cache refleja algo inferido solo del diccionario de datos independientemente del los valores en el contexto
4) haga todas las modificacioens en los objetos que provengan del contexto

Si no se hace en estos pasos, uno no puede cachear el objeto, ya que este tiene datos inferidos del contexto actual (el cual puede cambiar la proxima vez que se acceda a la cache). Esto es, la cache es una forma de cachear partes de la base de datos; no tiene que tener ninguna información que pueda, directa o indirectamente provenir del contexto. Muchas partes del codigo va “mechando” un poco de aca otro poco de alla…

OTRO TEMA (relacionado):

Ok, el tema es que MLookupFactory con o sin cache sigue accediendo una vez por cada MLookupInfo (240 veces en la ventana de EC, de manera serial y solo para generar los MLookupInfo); esto se da cuando se llama a MRol.addAccessSql; el cual parte de la información la cachea (la que proviene de AD_Record_Access), pero otra, es delegada a MPrivateAccess.getLockedRecord(AD_Record_ID, AD_Table_ID).
La idea de getLockedRecord retorne algo como “(1,23,33)” [estos seria los ids lockeados por otros usuarios para una determinada tabla), para que pueda usarse en un select de la forma “NOT in (1,23,33)”.
Para mi esto o no debería usarse para los Lookups (en general los Lookups se usan para editar datos de otras tablas relacionados, no para editar los datos del lookup en si) o usar una forma de cache temporal (la cache es temporal, pero es flusheada de manera “deteminisitica”; esto es, se hace explicitamente desde ciertos puntos del codigo) para almanecar entradas de esta forma

Code:
AD_Table_ID Where
——————–
23322 –> “(123,22,232)”
222 –> “12”
1002 –> “” (esto significaría que no hay restricción)

(offtopic: etiqueta code dejo de andar?)
Esta chache esta asociada a un deteminada AD_User_ID; el tema es que se precalacula al momento de cargar una ventana, haciendo UN SOLO acceso al base de datos; se “flushea” al finalizar la carga.

Ahora, MRol (o MPrivateAccess; habria que ver donde queda mejor), al buscar el where para una determinada tabla lo que hace es mirar en esta cache “temporal”; si encuentra una entrada todo bien, si no, accede a la base de datos (salvo en el enfoque “trae todo” que planteo mas abajo; ahi, si hay una cache para el usuario, y esta cache no contiene entradas, se asume que no hay registros lookeados).

Veo dos enfoques para precalcular esta cache; uno mucho mas simple, el otro un poco más complejo pero puede ser un poco mas eficiente si AD_Private_Access esta muy poblada (no creo que sea el caso general….)

El primero, mucho mas simple, basicamente ejecuta la siguiente sentencia sql
“SELECT AD_Table_ID, RecordId FROM AD_Private_Access Where Ad_User_ID <> “id del para el que se construye la cache” “
Posiblemente, si se quiere, ordenado por AD_Table_ID.

Con estos datos va construyendo un hashmap de Int a List, (desde AD_Table_ID a listas de RecordID); despues de tranforma esta mapa en uno de la forma que puse antes (de int a String con el where ya generado). Si esta cache no tiene una entrada para una determianda tabla, es porque no hay lookeos de registros en la misma.

La otra, es que al principio de la carga de una ventana determinada, se “precalculen” la lista de los ids de una y cada una de las tablas que van a ser refenciadas (lo que pasa es que esto no es tan simple) desde la ventana y el select inicial seria algo como “SELECT AD_Table_ID,RecordID From AD_Private_Access where Ad_User_ID<>“id del usuaria” AND AD_Table_ID IN “lista de tablas que van a ser usadas en la carga de la ventana” “. La cache generada de esta manera, TIENE que generar entradas vacias para los ids de aquellas tablas que no tiene restriccion de acceso, pero que estaban en la lista IN (esto es diferenciar el caso “la tabla no tiene restricciones” de “la cache no tuvo en cuenta esta tabla al cargarse”). Lo que tiene de bueno esto este enfoque es no va a generar entradas en la cache para tablas que no tienen nada que ver con la ventana en cuestion (digamos, si uno carga la ventana Bancos, no me va a generar una entrada para Productos si es que algun otro usuario lookeo un producto…). Acá, si uno no encuentra nada en la cache temporal, debería ir a buscar a la base de datos (esto por que por un lado, el calculo de “tablas” dependientes puede haber sido incompleto quedando algunas tablas sin tener en cuenta y la otra porque potencialmente se pueden estar cargando otras ventanas o desde algun otro lugar solicitando información para otra tabla que no tiene ninguna relacion con la ventana para la que se genero la cache)

Me imagino que el primer enfoque es el mas simple y posiblemente el mas eficiente en aquellos escenarios en que la restricción de acceso privado se use poco o nada, en los escenarios en que el lockeo privado se de mucho (y por lo tanto la tabla AD_Access_Private puede ser bastante grande), es posible que el segundo enfoque sea mejor.

Ninguna de estas dos ideas las implemente, porque por ahora corte por lo sano y getLookedRecord retonar siempre null en mi codigo (e.d, efectivamete deshabilite la funcionalidad de acceso privado…). De cualquier manera, lo veo bastante simple de implementar; después si me hago un tiempito, implemento el enfoque simple.