Funciones definidas por el usuario (UDF) son una característica muy útil soportada en SQL++. Couchbase 7.6 introduce mejoras que permiten una mayor depuración y visibilidad en la ejecución de UDF.
Este blog explorará dos nuevas características de Couchbase 7.6 en el mundo de las UDFs.
-
- Perfilado de sentencias SQL++ ejecutadas en UDFs JavaScript
- EXPLAIN FUNCTION para acceder a planes de consulta de sentencias SQL++ dentro de UDFs
Los ejemplos de este post requieren el conjunto de datos de muestras de viajes a instalar.
Creación de perfiles de SQL++ ejecutado en UDF de JavaScript
El perfilado de consultas es una característica de depuración que ofrece SQL++.
Cuando se activa la creación de perfiles para la ejecución de una sentencia, el resultado de la petición incluye un árbol de ejecución detallado con el tiempo y las métricas de cada paso de la ejecución de la sentencia. Además de la información de perfiles que se devuelve en los resultados de la sentencia, también se puede acceder a ella para la petición en el archivo sistema:solicitudes_activas y sistema:solicitudes_realizadas espacios de claves del sistema.
He aquí un post que profundiza en el perfilado de las peticiones.
En Couchbase 7.0, se incluyó la creación de perfiles para subconsultas, incluida la creación de perfiles de subconsultas de UDFs en línea.
Sin embargo, con las nuevas características de Couchbase 7.6, la creación de perfiles se amplió a las sentencias SQL++ dentro de UDF de JavaScript.
En versiones anteriores, para perfilar sentencias dentro de una UDF JavaScript, el usuario debía abrir la definición de la función, ejecutar individualmente cada sentencia dentro de la UDF y recopilar sus perfiles. Este paso adicional ya no será necesario en la versión 7.6.0.
Ahora, cuando se habilita la creación de perfiles, si la sentencia contiene la ejecución de una UDF JavaScript, también se recopilarán los perfiles de todas las sentencias SQL++ ejecutadas en la UDF. Y esta información de perfiles relacionada con UDF estará disponible en la salida de la petición, sistema:solicitudes_activas y sistema:solicitudes_realizadas del sistema.
Ejemplo 1:
Crear una UDF JavaScript js1 en una biblioteca mundial lib1 a través del punto final REST o de la interfaz de usuario.
|
1 2 3 4 5 6 7 8 9 10 11 12 |
function js1() { var query = SELECT * FROM default:`travel-sample`.inventory.airline LIMIT 1; var res = []; for (const row of query) { res.push(row); } query.close() return res; } |
Crear la función SQL++ correspondiente:
|
1 |
CREATE FUNCTION js1() LANGUAGE JAVASCRIPT AS "js1" AT "lib1"; |
Ejecuta la UDF con el perfil activado:
|
1 |
EXECUTE FUNCTION js1(); |
En el perfil de la respuesta devuelta, el campo executionTimings contiene un campo ~declaracionesudf.
~declaracionesudf es una matriz de información de perfiles que contiene una entrada por cada sentencia SQL++ dentro de la UDF JavaScript.
Cada entrada dentro del ~declaracionesudf contiene:
-
- executionTimings – El árbol de ejecución de la sentencia. Contiene información sobre métricas y tiempos de cada paso de la ejecución de la sentencia.
- declaración – La cadena de la declaración.
- función – El nombre de la función donde se ejecutó la sentencia. Esto es útil para identificar la UDF que ejecutó la sentencia cuando hay ejecuciones UDF anidadas.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 |
{ "requestID": "2c5576b5-f01d-445f-a35b-2213c606f394", "signature": null, "results": [ [ { "airline": { "callsign": "MILE-AIR", "country": "United States", "iata": "Q5", "icao": "MLA", "id": 10, "name": "40-Mile Air", "type": "airline" } } ] ], "status": "success", "metrics": { "elapsedTime": "20.757583ms", "executionTime": "20.636792ms", "resultCount": 1, "resultSize": 310, "serviceLoad": 2 }, "profile": { "phaseTimes": { "authorize": "12.835µs", "fetch": "374.667µs", "instantiate": "27.75µs", "parse": "251.708µs", "plan": "9.125µs", "primaryScan": "813.249µs", "primaryScan.GSI": "813.249µs", "project": "5.541µs", "run": "27.925833ms", "stream": "26.375µs" }, "phaseCounts": { "fetch": 1, "primaryScan": 1, "primaryScan.GSI": 1 }, "phaseOperators": { "authorize": 2, "fetch": 1, "primaryScan": 1, "primaryScan.GSI": 1, "project": 1, "stream": 1 }, "cpuTime": "468.626µs", "requestTime": "2023-12-04T20:30:00.369+05:30", "servicingHost": "127.0.0.1:8091", "executionTimings": { "#operator": "Authorize", "#planPreparedTime": "2023-12-04T20:30:00.369+05:30", "#stats": { "#phaseSwitches": 4, "execTime": "1.918µs", "servTime": "1.125µs" }, "privileges": { "List": [] }, "~child": { "#operator": "Sequence", "#stats": { "#phaseSwitches": 2, "execTime": "2.208µs" }, "~children": [ { "#operator": "ExecuteFunction", "#stats": { "#itemsOut": 1, "#phaseSwitches": 4, "execTime": "22.375µs", "kernTime": "20.271708ms" }, "identity": { "name": "js1", "namespace": "default", "type": "global" } }, { "#operator": "Stream", "#stats": { "#itemsIn": 1, "#itemsOut": 1, "#phaseSwitches": 2, "execTime": "26.375µs" }, "serializable": true } ] }, "~udfStatements": [ { "executionTimings": { "#operator": "Authorize", "#stats": { "#phaseSwitches": 4, "execTime": "2.626µs", "servTime": "7.166µs" }, "privileges": { "List": [ { "Priv": 7, "Props": 0, "Target": "default:travel-sample.inventory.airline" } ] }, "~child": { "#operator": "Sequence", "#stats": { "#phaseSwitches": 2, "execTime": "4.375µs" }, "~children": [ { "#operator": "PrimaryScan3", "#stats": { "#itemsIn": 1, "#itemsOut": 1, "#phaseSwitches": 7, "execTime": "22.082µs", "kernTime": "1.584µs", "servTime": "791.167µs" }, "bucket": "travel-sample", "index": "def_inventory_airline_primary", "index_projection": { "primary_key": true }, "keyspace": "airline", "limit": "1", "namespace": "default", "optimizer_estimates": { "cardinality": 187, "cost": 45.28617059639748, "fr_cost": 12.1780009122802, "size": 12 }, "scope": "inventory", "using": "gsi" }, { "#operator": "Fetch", "#stats": { "#itemsIn": 1, "#itemsOut": 1, "#phaseSwitches": 10, "execTime": "18.376µs", "kernTime": "797.542µs", "servTime": "356.291µs" }, "bucket": "travel-sample", "keyspace": "airline", "namespace": "default", "optimizer_estimates": { "cardinality": 187, "cost": 192.01699202888378, "fr_cost": 24.89848658838975, "size": 204 }, "scope": "inventory" }, { "#operator": "InitialProject", "#stats": { "#itemsIn": 1, "#itemsOut": 1, "#phaseSwitches": 7, "execTime": "5.541µs", "kernTime": "1.1795ms" }, "discard_original": true, "optimizer_estimates": { "cardinality": 187, "cost": 194.6878862611588, "fr_cost": 24.912769445246838, "size": 204 }, "preserve_order": true, "result_terms": [ { "expr": "self", "star": true } ] }, { "#operator": "Limit", "#stats": { "#itemsIn": 1, "#itemsOut": 1, "#phaseSwitches": 4, "execTime": "6.25µs", "kernTime": "333ns" }, "expr": "1", "optimizer_estimates": { "cardinality": 1, "cost": 24.927052302103924, "fr_cost": 24.927052302103924, "size": 204 } }, { "#operator": "Receive", "#stats": { "#phaseSwitches": 3, "execTime": "10.324833ms", "kernTime": "792ns", "state": "running" } } ] } }, "statement": "SELECT * FROM default:`travel-sample`.inventory.airline LIMIT 1;", "function": "default:js1" } ], "~versions": [ "7.6.0-N1QL", "7.6.0-1847-enterprise" ] } } } |
Planes de consulta con EXPLAIN FUNCTION
SQL++ ofrece otra maravillosa capacidad para acceder al plan de una sentencia con la sentencia EXPLAIN. Pero la sentencia EXPLAIN no se extiende a los planes de sentencias dentro de UDFs-ni Inline ni JavaScript UDFs.
En versiones anteriores, para analizar los planes de consulta de SQL++ dentro de una UDF era necesario que el usuario abriera la definición de la función y ejecutara individualmente un EXPLAIN en todas las sentencias de la UDF.
Estos pasos adicionales se reducen al mínimo en Couchbase 7.6 con la introducción de una nueva declaración.EXPLICAR LA FUNCIÓN. Esta sentencia hace exactamente lo mismo que EXPLAIN, pero para sentencias SQL++ dentro de una UDF.
Exploremos cómo utilizar la sentencia EXPLAIN FUNCTION.
Sintaxis
|
1 |
explain_function ::= 'EXPLAIN' 'FUNCTION' function |
Toma, función se refiere al nombre de la función.
Para obtener información más detallada sobre la sintaxis, consulte la documentación.
Requisitos previos
Para ejecutar EXPLAIN FUNCTION en una UDF, el usuario debe tener suficiente Permisos RBAC a ejecutar la función.
El usuario también debe tener los permisos RBAC necesarios para ejecutar las sentencias SQL++ dentro del cuerpo de la función UDF.
Aquí están los roles soportados en Couchbase.
UDF en línea
EXPLAIN FUNCTION en una UDF en línea devolverá los planes de consulta de todas las subconsultas dentro de su definición.
Ejemplo 2 - EXPLAIN FUNCTION en una función en línea
Cree una UDF en línea y ejecute EXPLAIN FUNCTION en ella:
|
1 2 3 |
CREATE FUNCTION inline1() { ( SELECT * FROM default:`travel-sample`.inventory.airport WHERE city = "Zachar Bay" ) }; |
|
1 |
EXPLAIN FUNCTION inline1(); |
Los resultados de la declaración anterior contendrán:
-
- función - El nombre de la función sobre la que se ha ejecutado EXPLAIN FUNCTION.
- planos - Una matriz de información del plan que contiene una entrada para cada subconsulta dentro de la UDF en línea.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
{ "function": "default:inline1", "plans": [ { "cardinality": 1.1176470588235294, "cost": 25.117642854609013, "plan": { "#operator": "Sequence", "~children": [ { "#operator": "IndexScan3", "bucket": "travel-sample", "index": "def_inventory_airport_city", "index_id": "2605c88c115dd3a2", "index_projection": { "primary_key": true }, "keyspace": "airport", "namespace": "default", "optimizer_estimates": { "cardinality": 1.1176470588235294, "cost": 12.200561852726496, "fr_cost": 12.179450078755286, "size": 12 }, "scope": "inventory", "spans": [ { "exact": true, "range": [ { "high": "\\"Zachar Bay\\"", "inclusion": 3, "index_key": "`city`", "low": "\\"Zachar Bay\\"" } ] } ], "using": "gsi" }, { "#operator": "Fetch", "bucket": "travel-sample", "keyspace": "airport", "namespace": "default", "optimizer_estimates": { "cardinality": 1.1176470588235294, "cost": 25.082370508382763, "fr_cost": 24.96843677065826, "size": 249 }, "scope": "inventory" }, { "#operator": "Parallel", "~child": { "#operator": "Sequence", "~children": [ { "#operator": "Filter", "condition": "((`airport`.`city`) = \\"Zachar Bay\\")", "optimizer_estimates": { "cardinality": 1.1176470588235294, "cost": 25.100006681495888, "fr_cost": 24.98421650449632, "size": 249 } }, { "#operator": "InitialProject", "discard_original": true, "optimizer_estimates": { "cardinality": 1.1176470588235294, "cost": 25.117642854609013, "fr_cost": 24.99999623833438, "size": 249 }, "result_terms": [ { "expr": "self", "star": true } ] } ] } } ] }, "statement": "select self.* from `default`:`travel-sample`.`inventory`.`airport` where ((`airport`.`city`) = \\"Zachar Bay\\")" } ] } |
JavaScript UDF
Las sentencias SQL++ dentro de las UDFs de JavaScript pueden ser de dos tipos y EXPLAIN FUNCTION funciona de forma diferente basándose en la forma en que se llama a la sentencia SQL++.
Aquí está la referencia documental a Llamada a SQL++ en funciones JavaScript.
SQL++ integrado
-
- SQL++ incrustado está "incrustado" en el cuerpo de la función y su detección es gestionada por el transpilador de JavaScript.
- EXPLAIN FUNCTION puede devolver planes de consulta para sentencias SQL++ incrustadas.
SQL++ ejecutado por la llamada a la función N1QL()
-
- SQL++ también puede ejecutarse pasando una sentencia en forma de cadena como argumento a la función N1QL().
- Cuando se analiza la función para posibles sentencias SQL++ sobre las que ejecutar EXPLAIN, es difícil obtener la cadena dinámica en el argumento de la función. Esto sólo se puede resolver de forma fiable en tiempo de ejecución.
- Con este razonamiento, EXPLAIN FUNCTION no devuelve los planes de consulta para sentencias SQL++ ejecutadas mediante llamadas a N1QL(). Sino que devuelve los números de línea donde se han realizado las llamadas a la función N1QL(). Este número de línea se calcula desde el principio de la definición de la función.
- A continuación, el usuario puede asignar los números de línea en la definición real de la función e investigar más a fondo.
Ejemplo 3 - EXPLAIN FUNCTION en una función JavaScript externa
Crear una UDF JavaScript js2 en una biblioteca mundial lib1 a través del punto final REST o de la interfaz de usuario:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
function js2() { // SQL++ executed by a N1QL() function call var query1 = N1QL("UPDATE default:`travel-sample` SET test = 1 LIMIT 1"); // Embedded SQL++ var query2 = SELECT * FROM default:`travel-sample` LIMIT 1; var res = []; for (const row of query2) { res.push(row); } query2.close() return res; } |
Crear la función SQL++ correspondiente:
|
1 |
CREATE FUNCTION js2() LANGUAGE JAVASCRIPT AS "js2" AT "lib1"; |
Ejecute EXPLAIN FUNCTION en la función SQL++:
|
1 |
EXPLAIN FUNCTION js2; |
Los resultados de la declaración anterior contendrán:
-
- función - El nombre de la función sobre la que se ha ejecutado EXPLAIN FUNCTION.
- números_de_línea - Una matriz de números de línea calculados desde el principio de la definición de la función JavaScript donde hay llamadas a la función N1QL().
- planos - Una matriz de información del plan que contiene una entrada por cada incrustado SQL++ dentro de la UDF JavaScript.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 |
{ "function": "default:js2", "line_numbers": [ 4 ], "plans": [ { "cardinality": 1, "cost": 25.51560885530435, "plan": { "#operator": "Authorize", "privileges": { "List": [ { "Target": "default:travel-sample", "Priv": 7, "Props": 0 } ] }, "~child": { "#operator": "Sequence", "~children": [ { "#operator": "Sequence", "~children": [ { "#operator": "Sequence", "~children": [ { "#operator": "PrimaryScan3", "index": "def_primary", "index_projection": { "primary_key": true }, "keyspace": "travel-sample", "limit": "1", "namespace": "default", "optimizer_estimates": { "cardinality": 31591, "cost": 5402.279801258844, "fr_cost": 12.170627071041082, "size": 11 }, "using": "gsi" }, { "#operator": "Fetch", "keyspace": "travel-sample", "namespace": "default", "optimizer_estimates": { "cardinality": 31591, "cost": 46269.39474997121, "fr_cost": 25.46387878667884, "size": 669 } }, { "#operator": "Parallel", "~child": { "#operator": "Sequence", "~children": [ { "#operator": "InitialProject", "discard_original": true, "optimizer_estimates": { "cardinality": 31591, "cost": 47086.49704894546, "fr_cost": 25.489743820991595, "size": 669 }, "preserve_order": true, "result_terms": [ { "expr": "self", "star": true } ] } ] } } ] }, { "#operator": "Limit", "expr": "1", "optimizer_estimates": { "cardinality": 1, "cost": 25.51560885530435, "fr_cost": 25.51560885530435, "size": 669 } } ] }, { "#operator": "Stream", "optimizer_estimates": { "cardinality": 1, "cost": 25.51560885530435, "fr_cost": 25.51560885530435, "size": 669 }, "serializable": true } ] } }, "statement": "SELECT * FROM default:`travel-sample` LIMIT 1 ;" } ] } |
Restricciones
Si la función N1QL() ha sido aliasada en una definición de función JavaScript, EXPLAIN FUNCTION no podrá devolver los números de línea donde esta función aliasada fue llamada. Por ejemplo:
|
1 2 3 4 |
function js3() { var alias = N1QL; var q = alias("SELECT 1"); } |
Si la UDF contiene ejecuciones UDF anidadas, EXPLAIN FUNCTION no soporta la generación de planes de consulta de sentencias SQL++ dentro de estas UDFs anidadas.
Resumen
Couchbase 7.6 introduce nuevas características para la depuración UDF que ayudarán a los usuarios a echar un vistazo a la ejecución UDF fácilmente.
Consulte los siguientes enlaces de documentación para obtener más información, o eche un vistazo a las demás novedades de Couchbase 7.6: