(This article is also available in English as appeared translated in Salesfoceben on July 2019).
En la última entrada sobre Optimización de Queries en Salesforce, destapaba unos de los problemas más evidentes de cualquier proyecto: la degradación de rendimiento de los elementos que consultan datos en Salesforce.
Es decir, a medida que nuestra ORG crece, las consultas que inicialmente diseñamos (que quizás optimizamos o quizás no) pueden sufrir degradación. Pero no estoy hablando sólo de las Queries de los Developers con SOQL, estoy hablando de List Views, Reports y Dashboards que crean los usuarios.
Además, esta degradación se realiza en silencio, inadvertida, y solo es visible con incidencias de rendimiento en Producción, cuando ya es demasiado tarde, y las acciones correctoras (creación de índices, re-organización de datos, etc.) no serán inmediatas.
Por ello, y porque existen diversos componentes, que puestos a trabajar de forma conjunta, permiten obtener esta información simplemente, he creado una herramienta, que he denominado: Query Performance Detector ( un nombre poco original, pero significativo 🙂 ).
Esta herramienta, permite obtener para ListViews, Reports y Dashboards, cómo el Optimizador de Salesforce está evaluando cada una de ellas:
- su coste
- el número de filas retornado
- el plan de acceso
- con los datos presentes en la ORG
Destaca aquellas que tienen un coste no-selectivo, para que las optimices de forma prioritaria, especialmente si tienen un volumen muy elevado de registros.
No son un conjunto de consultas cualquiera, sino que son las que los usuarios han utilizado, por tanto, tu máximo objetivo de optimización.
Con esta información podemos:
- Detectar aquellas consultas, que no son eficientes, su coste es elevado y seguramente su volumen de registros también, para eficientarlas de inmediato.
- Consultar el histórico del rendimiento y evaluar aquellas que están empeorando para iniciar un proceso de optimización.
- Obtener el listado de Queries SOQL, para saber sobre cuales debemos centrar nuestros esfuerzos de optimización.
Por tanto, nuestros esfuerzos de optimización se verán reflejados de inmediato en la percepción del usuario. A continuación te describo la herramienta en detalle.
Introducción
Como una imagen vale más de mil palabras, esta vez he compuesto un vídeo, para que puedas ver la herramienta en funcionamiento (90» aproximadamente):
Sus características técnicas principales son:
- 100% Apex y VisualForce (aún necesito aprender más Lightning)
- 1 único objeto creado en la ORG para guardar el histórico de resultados
- Todos los procesos de cálculo son Batchable, para que su ejecución no perjudique el trabajo de los usuarios
- Se consultan objetos internos de Salesforce y se invoca a la Tooling API
Es decir, básicamente lo que he realizado ha sido, utilizar componentes/funcionalidades/datos que ya existen dentro de la plataforma de Salesforce y ponerlos a trabajar para solucionar el problema, volviendo a demostrar la gran versatilidad y potencia que tenemos disponible.
Objetos Creados
Esta vez he utilizado únicamente 2 objetos:
- QueryCostHistory__c: que almacena los resultados de los escaneos de todos los objetos, ya sean List Views, Reports, Dashboards o investigación de Logs de usuario en busca de SOQL Queries
- Process_Log__c: que me permite recojer trazas que no quiero que se muestren o que no se muestran en el Log de Salesforce (lo utilizo en otras herramientas que he creado)
Para mi es importante, que este tipo de herramientas, creen el menor número de objetos adicionales.
Clases y VisualForce Pages
En la siguiente captura puedes ver el conjunto de clases y páginas que he creado:
- 4 páginas VisualForce
- 11 clases APEX

Esquema de ejecución
El esquema de ejecución es muy sencillo:
- Desde el que Controller principal QP_MainController, se invocan al resto de Controllers para obtener la información. Cada controller obtiene la lista de los objetos que gestiona (ListViews, Reports, etc., ) dentro del período seleccionado por el usuario y lo deposita como pendientes de análisis en el objeto QueryCostHistory__c
- El Controller QP_ToolingAPIRequester se encarga de invocar a la Tooling API para obtener los Query Plans, recorriendo la tabla en busca de los pendientes. Invoca a la API para obtener con Batches, y registra los resultados en el mismo objeto.
- Los Controllers que realizan las búsquedas de elementos son los denominados QP_GetLast*
- Posteriormente, los Controllers y las páginas VisualForce de detalle e históricos se encargan de mostrar los resultados individuales y acumulados respectivamente al usuario.
- Los Controllers que muestran datos son los denominados QP_Show*
- Finalmente hay 2 objetos DAOs de apoyo al almacenamiento de datos en listas
No es complejo.
Características a destacar
Objetos Internos
Para obtener la información de las ListViews, Reports, Dashboards utilizados en un período se consultan tablas internas, ni se crean nuevos datos, ni se hacen cosas raras.
Todos los procesos implementan la @Batchable
Como comentaba anteriormente, me obsesiona que el trabajo de los usuarios no sea afectado por las herramientas que hago, ni quiero que se llegue nunca a afectar a los límites, por tanto, utilizo la interfaz @Batchable
y la ejecución segmentada mediante chunks, que es un parámetro del método Database.ExecuteBatch
.
De esta manera, por muy grande que sean los volúmenes, las ejecuciones nunca superan los límites y el consumo de recursos de la Flex Queue y de las colas asíncronas es mínimo (además siempre menos prioritario que el consumo síncrono por supuesto).
A continuación te muestro una captura de pantalla del consumo en mi ORG:

Ejecución Paralelizable
Dado que utilizo los recursos asíncronos, esto me permite lanzar las peticiones en paralelo. Es decir, si planificamos (mediante @Schedule) el proceso de forma nocturna, por ejemplo, puedas lanzar los procesos de forma simultánea, consumiendo 1/4 del tiempo que consumirías si lo tuvieras que lanzar uno detrás de otro.
Envío de notificaciones (cada uno …)
No he implementado ni la interfaz @Schedulable
ni un sistema de avisos, para planificar ni generar avisos respectivamente, porque no he querido ampliar el código innecesariamente para que fuera más legible.
Con total seguridad, ya sabes hacerlo, y existe mucha documentación al respecto, pero tan solo implementando la interfaz @Schedulable, en los Controllers, sería suficiente para su cronificación. Para la generación de eventos y avisos, cada maestrillo tiene su librillo y para gustos los colores, con lo que…
Conclusiones
Nuevamente me demuestro, que conocer la plataforma, me proporciona las capacidades de extenderla para conseguir herramientas con funcionalidades que inicialmente no existen, pero que podemos construir.
Esta herramienta es un punto de partida de un problema evidente en todas las ORGS que crecen con datos, o van cambiando de datos, y que sin ninguna duda acaban siendo incidencias en Producción difíciles de diagnosticar, y que si podemos adelantarnos, nos ahorraremos dolores de cabeza.
Repositorio de Código disponible
El código completo de esta herramienta está disponible en este repositorio de Bitbucket, para que lo puedas utilizar como quieras, y seguro mejorar.
Espero que sea de ayuda.
Muy útil y necesaria. Soy testigo de la degradacion en varias orgs. Lo compartire con mis amigos. Gracias.
Me gustaLe gusta a 1 persona
Muchas gracias Elkin, espero que os sea de ayuda.
Me gustaMe gusta
[…] Originally written by Esteve Graells on Forcegraells. […]
Me gustaMe gusta
[…] Originally written by Esteve Graells on Forcegraells. […]
Me gustaMe gusta