Funções definidas pelo usuário (UDFs) são um recurso muito útil suportado no SQL++. O Couchbase 7.6 apresenta aprimoramentos que permitem maior capacidade de depuração e visibilidade da execução de UDFs.
Este blog explorará dois novos recursos do Couchbase 7.6 no mundo das UDFs.
-
- Criação de perfil para instruções SQL++ executadas em UDFs JavaScript
- EXPLAIN FUNCTION para acessar planos de consulta de instruções SQL++ em UDFs
Os exemplos nesta postagem requerem o conjunto de dados de amostra de viagens a ser instalado.
Criação de perfil do SQL++ executado em UDFs JavaScript
A criação de perfil de consulta é um recurso de depuração que o SQL++ oferece.
Quando a criação de perfil está ativada para a execução de uma instrução, o resultado da solicitação inclui uma árvore de execução detalhada com o tempo e as métricas de cada etapa da execução da instrução. Além de as informações de criação de perfil serem retornadas nos resultados da instrução, elas também podem ser acessadas para a solicitação na função system:active_requests e system:completed_requests espaços de chaves do sistema.
Aqui está uma postagem que aprofunda-se na criação de perfis de solicitação.
No Couchbase 7.0, a criação de perfil foi incluída para subconsultas, incluindo a criação de perfil de subconsultas de UDFs em linha.
No entanto, com os novos recursos do Couchbase 7.6, a criação de perfil foi estendida para instruções SQL++ dentro de UDFs de JavaScript.
Nas versões anteriores, para criar perfis de instruções em um UDF JavaScript, o usuário precisava abrir a definição da função, executar individualmente cada instrução no UDF e coletar seus perfis. Essa etapa adicional não será mais necessária na versão 7.6.0!
Agora, quando a criação de perfil estiver ativada, se a instrução contiver a execução do UDF JavaScript, os perfis de todas as instruções SQL++ executadas no UDF também serão coletados. E essas informações de criação de perfil relacionadas ao UDF estarão disponíveis na saída da solicitação, system:active_requests e system:completed_requests espaços de chaves do sistema também.
Exemplo 1:
Criar um UDF JavaScript js1 em uma biblioteca global lib1 por meio do ponto de extremidade REST ou da interface do usuário.
1 2 3 4 5 6 7 8 9 10 11 12 |
função js1() { var consulta = SELECIONAR * DE padrão:`amostra de viagem`.inventário.companhia aérea LIMITE 1; var res = []; para (const fila de consulta) { res.empurrar(fila); } consulta.próximo() retorno res; } |
Crie a função SQL++ correspondente:
1 |
CRIAR FUNÇÃO js1() LÍNGUA JAVASCRIPT AS "js1" AT "lib1"; |
Execute o UDF com a criação de perfil ativada:
1 |
EXECUTAR FUNÇÃO js1(); |
No perfil da resposta retornada, a seção executionTimings A subseção contém um campo ~udfStatements.
~udfStatements é uma matriz de informações de criação de perfil que contém uma entrada para cada instrução SQL++ dentro do UDF JavaScript.
Cada entrada no ~udfStatements A seção contém:
-
- executionTimings – A árvore de execução da declaração. Ela contém informações de métricas e tempos de cada etapa da execução da instrução.
- declaração – A string de declaração.
- função – O nome da função em que a instrução foi executada. Isso é útil para identificar o UDF que executou o comando quando há execuções de UDFs aninhadas.
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", "assinatura": nulo, "resultados": [ [ { "companhia aérea": { "indicativo": "MILE-AIR", "país": "Estados Unidos", "iata": "Q5", "icao": "MLA", "id": 10, "name" (nome): "40 milhas aéreas", "tipo": "companhia aérea" } } ] ], "status": "sucesso", "métricas": { "elapsedTime" (tempo decorrido): "20.757583ms", "executionTime": "20.636792ms", "resultCount": 1, "resultSize": 310, "serviceLoad": 2 }, "profile" (perfil): { "phaseTimes": { "autorizar": "12.835µs", "fetch" (buscar): "374.667µs", "instanciar": "27,75µs", "parse" (analisar): "251.708µs", "plano": "9.125µs", "primaryScan": "813.249µs", "primaryScan.GSI": "813.249µs", "projeto": "5.541µs", "executar": "27.925833ms", "stream" (fluxo): "26,375µs" }, "phaseCounts": { "fetch" (buscar): 1, "primaryScan": 1, "primaryScan.GSI": 1 }, "phaseOperators": { "autorizar": 2, "fetch" (buscar): 1, "primaryScan": 1, "primaryScan.GSI": 1, "projeto": 1, "stream" (fluxo): 1 }, "cpuTime": "468.626µs", "requestTime": "2023-12-04T20:30:00.369+05:30", "servicingHost": "127.0.0.1:8091", "executionTimings": { "#operator": "Authorize" (Autorizar), "#planPreparedTime": "2023-12-04T20:30:00.369+05:30", "#stats": { "#phaseSwitches": 4, "execTime": "1.918µs", "servTime": "1.125µs" }, "privilégios": { "Lista": [] }, "~child": { "#operator": "Sequência", "#stats": { "#phaseSwitches": 2, "execTime": "2.208µs" }, "~crianças": [ { "#operator": "ExecuteFunction" (Executar função), "#stats": { "#itemsOut": 1, "#phaseSwitches": 4, "execTime": "22,375µs", "kernTime": "20.271708ms" }, "identidade": { "name" (nome): "js1", "namespace": "default", "tipo": "global" } }, { "#operator": "Fluxo", "#stats": { "#itemsIn": 1, "#itemsOut": 1, "#phaseSwitches": 2, "execTime": "26,375µs" }, "serializável": verdadeiro } ] }, "~udfStatements": [ { "executionTimings": { "#operator": "Authorize" (Autorizar), "#stats": { "#phaseSwitches": 4, "execTime": "2.626µs", "servTime": "7.166µs" }, "privilégios": { "Lista": [ { "Priv": 7, "Adereços": 0, "Alvo": "default:travel-sample.inventory.airline" } ] }, "~child": { "#operator": "Sequência", "#stats": { "#phaseSwitches": 2, "execTime": "4.375µs" }, "~crianças": [ { "#operator": "PrimaryScan3", "#stats": { "#itemsIn": 1, "#itemsOut": 1, "#phaseSwitches": 7, "execTime": "22.082µs", "kernTime": "1.584µs", "servTime": "791.167µs" }, "bucket" (balde): "amostra de viagem", "índice": "def_inventory_airline_primary", "index_projection": { "primary_key": verdadeiro }, "espaço-chave": "companhia aérea", "limite": "1", "namespace": "default", "optimizer_estimates": { "cardinalidade": 187, "custo": 45.28617059639748, "fr_cost": 12.1780009122802, "tamanho": 12 }, "escopo": "inventário", "usando": "gsi" }, { "#operator": "Buscar", "#stats": { "#itemsIn": 1, "#itemsOut": 1, "#phaseSwitches": 10, "execTime": "18.376µs", "kernTime": "797.542µs", "servTime": "356.291µs" }, "bucket" (balde): "amostra de viagem", "espaço-chave": "companhia aérea", "namespace": "default", "optimizer_estimates": { "cardinalidade": 187, "custo": 192.01699202888378, "fr_cost": 24.89848658838975, "tamanho": 204 }, "escopo": "inventário" }, { "#operator": "InitialProject" (Projeto inicial), "#stats": { "#itemsIn": 1, "#itemsOut": 1, "#phaseSwitches": 7, "execTime": "5.541µs", "kernTime": "1.1795ms" }, "discard_original": verdadeiro, "optimizer_estimates": { "cardinalidade": 187, "custo": 194.6878862611588, "fr_cost": 24.912769445246838, "tamanho": 204 }, "preserve_order": verdadeiro, "result_terms": [ { "expr": "self", "estrela": verdadeiro } ] }, { "#operator": "Limite", "#stats": { "#itemsIn": 1, "#itemsOut": 1, "#phaseSwitches": 4, "execTime": "6,25µs", "kernTime": "333ns" }, "expr": "1", "optimizer_estimates": { "cardinalidade": 1, "custo": 24.927052302103924, "fr_cost": 24.927052302103924, "tamanho": 204 } }, { "#operator": "Receber", "#stats": { "#phaseSwitches": 3, "execTime": "10.324833ms", "kernTime": "792ns", "estado": "em execução" } } ] } }, "declaração": "SELECT * FROM default:`travel-sample`.inventory.airline LIMIT 1;", "function" (função): "default:js1" } ], "~versões": [ "7.6.0-N1QL", "7.6.0-1847-enterprise" ] } } } |
Planos de consulta com EXPLAIN FUNCTION
O SQL++ oferece outro recurso maravilhoso para acessar o plano de uma instrução com a instrução EXPLAIN. Porém, o comando EXPLAIN não se estende a planos de comandos dentro de UDFs - nem UDFs Inline nem JavaScript.
Nas versões anteriores, para analisar os planos de consulta do SQL++ em uma UDF, era necessário que o usuário abrisse a definição da função e executasse individualmente um EXPLAIN em todas as instruções da UDF.
Essas etapas extras são minimizadas no Couchbase 7.6 com a introdução de uma nova declaração-EXPLICAR A FUNÇÃO. Essa instrução faz exatamente o que o EXPLAIN faz, mas para instruções SQL++ em um UDF.
Vamos explorar como usar a instrução EXPLAIN FUNCTION!
Sintaxe
1 |
explain_function ::= 'EXPLAIN' 'FUNCTION' função |
Aqui, função refere-se ao nome da função.
Para obter informações mais detalhadas sobre a sintaxe, consulte a documentação.
Pré-requisitos
Para executar o EXPLAIN FUNCTION em um UDF, o usuário deve ter Permissões RBAC para executar a função.
O usuário também deve ter as permissões RBAC necessárias para executar as instruções SQL++ no corpo da função UDF.
Aqui estão os funções compatíveis com o Couchbase.
UDF em linha
EXPLAIN FUNCTION em uma UDF em linha retornará os planos de consulta de todas as subconsultas dentro de sua definição.
Exemplo 2 - EXPLAIN FUNCTION em uma função inline
Crie uma UDF em linha e execute EXPLAIN FUNCTION nela:
1 2 3 |
CRIAR FUNÇÃO inline1() { ( SELECT * DE padrão:`viagens-amostra`.inventário.aeroporto ONDE cidade = "Zachar Bay" ) }; |
1 |
EXPLICAR FUNÇÃO inline1(); |
Os resultados da declaração acima conterão:
-
- função - O nome da função em que EXPLAIN FUNCTION foi executado.
- planos - Uma matriz de informações do plano que contém uma entrada para cada subconsulta dentro do UDF Inline.
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" (função): "default:inline1", "planos": [ { "cardinalidade": 1.1176470588235294, "custo": 25.117642854609013, "plano": { "#operator": "Sequência", "~crianças": [ { "#operator": "IndexScan3", "bucket" (balde): "amostra de viagem", "índice": "def_inventory_airport_city", "index_id": "2605c88c115dd3a2", "index_projection": { "primary_key": verdadeiro }, "espaço-chave": "aeroporto", "namespace": "default", "optimizer_estimates": { "cardinalidade": 1.1176470588235294, "custo": 12.200561852726496, "fr_cost": 12.179450078755286, "tamanho": 12 }, "escopo": "inventário", "vãos": [ { "exato": verdadeiro, "intervalo": [ { "alto": "\\"Zachar Baía\\"", "inclusão": 3, "index_key": "`city`", "baixo": "\\"Zachar Baía\\"" } ] } ], "usando": "gsi" }, { "#operator": "Buscar", "bucket" (balde): "amostra de viagem", "espaço-chave": "aeroporto", "namespace": "default", "optimizer_estimates": { "cardinalidade": 1.1176470588235294, "custo": 25.082370508382763, "fr_cost": 24.96843677065826, "tamanho": 249 }, "escopo": "inventário" }, { "#operator": "Paralelo", "~child": { "#operator": "Sequência", "~crianças": [ { "#operator": "Filtro", "condição": "((`airport`.`city`) = \\"Zachar Baía\\")", "optimizer_estimates": { "cardinalidade": 1.1176470588235294, "custo": 25.100006681495888, "fr_cost": 24.98421650449632, "tamanho": 249 } }, { "#operator": "InitialProject" (Projeto inicial), "discard_original": verdadeiro, "optimizer_estimates": { "cardinalidade": 1.1176470588235294, "custo": 25.117642854609013, "fr_cost": 24.99999623833438, "tamanho": 249 }, "result_terms": [ { "expr": "self", "estrela": verdadeiro } ] } ] } } ] }, "declaração": "select self.* from `default`:`travel-sample`.`inventory`.`airport` where ((`airport`.`city`) = \\"Zachar Baía\\")" } ] } |
JavaScript UDF
As instruções SQL++ dentro dos UDFs JavaScript podem ser de dois tipos e o EXPLAIN FUNCTION funciona de forma diferente com base na maneira como a instrução SQL++ é chamada.
Aqui está a referência da documentação para Chamada de SQL++ em funções JavaScript.
SQL++ incorporado
-
- O SQL++ incorporado é "incorporado" no corpo da função e sua detecção é tratada pelo transpilador JavaScript.
- A EXPLAIN FUNCTION pode retornar planos de consulta para instruções SQL++ incorporadas.
SQL++ executado pela chamada da função N1QL()
-
- O SQL++ também pode ser executado passando uma instrução na forma de uma cadeia de caracteres como argumento para a função N1QL().
- Ao analisar a função para possíveis instruções SQL++ para executar o EXPLAIN, é difícil obter a string dinâmica no argumento da função. Isso só pode ser resolvido de forma confiável em tempo de execução.
- Com esse raciocínio, o EXPLAIN FUNCTION não retorna os planos de consulta para instruções SQL++ executadas por meio de chamadas N1QL(). Em vez disso, retorna os números de linha em que as chamadas de função N1QL() foram feitas. Esse número de linha é calculado a partir do início da definição da função.
- O usuário pode então mapear os números de linha na definição real da função e investigar mais a fundo.
Exemplo 3 - EXPLAIN FUNCTION em uma função JavaScript externa
Criar um UDF JavaScript js2 em uma biblioteca global lib1 por meio do ponto de extremidade REST ou da interface do usuário:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
função js2() { // SQL++ executado por uma chamada de função N1QL() var consulta1 = N1QL("UPDATE default:`travel-sample` SET test = 1 LIMIT 1"); // SQL++ incorporado var consulta2 = SELECIONAR * DE padrão:`amostra de viagem` LIMITE 1; var res = []; para (const fila de consulta2) { res.empurrar(fila); } consulta2.próximo() retorno res; } |
Crie a função SQL++ correspondente:
1 |
CRIAR FUNÇÃO js2() LÍNGUA JAVASCRIPT AS "js2" AT "lib1"; |
Execute EXPLAIN FUNCTION na função SQL++:
1 |
EXPLICAR FUNÇÃO js2; |
Os resultados da declaração acima conterão:
-
- função - O nome da função em que EXPLAIN FUNCTION foi executado.
- números_linha - Uma matriz de números de linha calculada a partir do início da definição da função JavaScript em que há chamadas de função N1QL().
- planos - Uma matriz de informações do plano que contém uma entrada para cada incorporado instrução SQL++ dentro do 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" (função): "default:js2", "line_numbers": [ 4 ], "planos": [ { "cardinalidade": 1, "custo": 25.51560885530435, "plano": { "#operator": "Authorize" (Autorizar), "privilégios": { "Lista": [ { "Alvo": "default:travel-sample" (padrão: amostra de viagem), "Priv": 7, "Adereços": 0 } ] }, "~child": { "#operator": "Sequência", "~crianças": [ { "#operator": "Sequência", "~crianças": [ { "#operator": "Sequência", "~crianças": [ { "#operator": "PrimaryScan3", "índice": "def_primary", "index_projection": { "primary_key": verdadeiro }, "espaço-chave": "amostra de viagem", "limite": "1", "namespace": "default", "optimizer_estimates": { "cardinalidade": 31591, "custo": 5402.279801258844, "fr_cost": 12.170627071041082, "tamanho": 11 }, "usando": "gsi" }, { "#operator": "Buscar", "espaço-chave": "amostra de viagem", "namespace": "default", "optimizer_estimates": { "cardinalidade": 31591, "custo": 46269.39474997121, "fr_cost": 25.46387878667884, "tamanho": 669 } }, { "#operator": "Paralelo", "~child": { "#operator": "Sequência", "~crianças": [ { "#operator": "InitialProject" (Projeto inicial), "discard_original": verdadeiro, "optimizer_estimates": { "cardinalidade": 31591, "custo": 47086.49704894546, "fr_cost": 25.489743820991595, "tamanho": 669 }, "preserve_order": verdadeiro, "result_terms": [ { "expr": "self", "estrela": verdadeiro } ] } ] } } ] }, { "#operator": "Limite", "expr": "1", "optimizer_estimates": { "cardinalidade": 1, "custo": 25.51560885530435, "fr_cost": 25.51560885530435, "tamanho": 669 } } ] }, { "#operator": "Fluxo", "optimizer_estimates": { "cardinalidade": 1, "custo": 25.51560885530435, "fr_cost": 25.51560885530435, "tamanho": 669 }, "serializável": verdadeiro } ] } }, "declaração": "SELECT * FROM default:`travel-sample` LIMIT 1 ;" } ] } |
Restrições
Se a função N1QL() tiver sido usada como alias em uma definição de função JavaScript, o EXPLAIN FUNCTION não poderá retornar os números de linha em que essa função com alias foi chamada. Por exemplo:
1 2 3 4 |
função js3() { var pseudônimo = N1QL; var q = pseudônimo("SELECT 1"); } |
Se o UDF contiver execuções de UDFs aninhados, o EXPLAIN FUNCTION não suportará a geração de planos de consulta de instruções SQL++ dentro desses UDFs aninhados.
Resumo
O Couchbase 7.6 apresenta novos recursos para a depuração de UDFs, que ajudarão os usuários a examinar facilmente a execução de UDFs.
Consulte os links da documentação a seguir para saber mais ou confira as outras inovações do Couchbase 7.6: