Em blogs anteriores, abordamos a execução do N1QL (SQL++) a partir do JavaScript funções, processamento de documentos por meio de iteradores, manipulação de dados, tratamento de errose usando declarações preparadas. Temos alguns tópicos mais avançados para abordar antes de passarmos a manipular as bibliotecas JavaScript, como chamar outras funções, usar transações e RBAC.
Chamada de funções
As funções JavaScript podem chamar outras funções JavaScript diretamente.
Qualquer instrução N1QL executada na função chamada será executada da mesma forma e com exatamente as mesmas limitações, como se tivesse sido executada na função chamada. Os resultados podem subir e descer livremente a pilha e qualquer exceção lançada na função chamada descerá a pilha até a solicitação N1QL de chamada (a menos que seja capturada e tratada em algum lugar).
Essa é definitivamente uma maneira mais prática de chamar funções do que executar instruções N1QL que chamam UDFs N1QL. Ao chamar funções diretamente em vez de passar por EXECUTAR FUNÇÃO você ignora completamente a camada N1QL e economiza todos os recursos necessários para alternar de JavaScript para N1QL e de volta para JavaScript. Isso reduz o custo das trocas de contexto, a necessidade de mover todos os dados por meio de iteradores e não diretamente, e a necessidade de deixar os iteradores abertos durante a chamada.
Execução de N1QL que chama funções
Há ocasiões em que você executará comandos N1QL que chamam os próprios UDFs N1QL e esses UDFs podem muito bem ser funções JavaScript.
Isso é absolutamente esperado e funciona exatamente da mesma forma que tudo o que descrevemos até agora.
A única limitação da qual você deve estar ciente é que, embora qualquer instrução N1QL que o JavaScript possa executar, será executada no mesmo servidor que estiver executando a solicitação principal. Cada chamada de função JavaScript usará um novo worker JavaScript, o que significa que quanto mais chamadas de UDF N1QL aninhadas você fizer, menos workers estarão disponíveis, e a função eventualmente falhará dessa forma:
|
1 2 3 |
function doRecursion() { var q = execute function doRecursion(); } |
|
1 |
CREATE FUNCTION doRecursion() LANGUAGE JAVASCRIPT AS "doRecursion" AT "udfblog" |
O que resulta em:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
[ { "code": 10109, "msg": "Error executing function 'doRecursion' (udfblog:doRecursion)", "reason": { "details": { "Code": " var q = N1QL('execute function doRecursion();', {}, true);", "Exception": { "caller": "javascript:133", "code": 10112, "key": "function.nested.error", "message": "Error executing function 'doRecursion': 25 nested javascript calls" }, "Location": "functions/udfblog.js:11", "Stack": " at doRecursion (functions/udfblog.js:11:13)" }, "type": "Exceptions from JS code" } } ] |
O limite de aninhamento não é fixo e depende do número de trabalhadores JavaScript disponíveis no momento em que cada chamada aninhada é executada. Em termos gerais, quanto mais atividade JavaScript estiver presente no nó, menor será esse limite.
No exemplo acima, o nó tinha 48 trabalhadores e falhou na 25ª chamada.
RBAC
Para executar o N1QL como parte das funções JavaScript, o usuário que executa a função precisa ter os privilégios adequados para realizar as ações (inserir, selecionar...) especificados na função sobre os objetos referenciados pela função.
Como a identidade do usuário que executa a função não é conhecida até o momento da execução, quaisquer erros de privilégio só serão levantados quando a função for executada, supondo que o usuário que executa a função tenha recebido privilégio para executar funções JavaScript em primeiro lugar!
Isso está de acordo com o que outros mecanismos de banco de dados fazem.
Efeitos colaterais
As funções executadas como parte de expressões não podem ter efeitos colaterais, o que significa que nenhuma DML pode ser executada como parte dessa função. Isso está de acordo com o que outros mecanismos de banco de dados fazem e faz sentido. Você não gostaria que uma função SELECIONAR para inserir dados nas suas costas por meio do uso de uma função JavaScript.
A única maneira de fazer com que as funções manipulem o armazenamento é executá-las isoladamente por meio do EXECUTAR FUNÇÃO declaração.
Transações
É perfeitamente legal executar instruções N1QL transacionais dentro de uma função JavaScript, desde que a função esteja sendo executada com a capacidade de ter efeitos colaterais, ou seja, por meio da função EXECUTAR FUNÇÃO declaração. A função pode executar DML em uma transação que tenha sido iniciada antes da execução da função, pode iniciar a transação, pode confirmar ou reverter a transação ou qualquer combinação legal dessas opções.
A única coisa que você precisa ter em mente é que as instruções e os iteradores do N1QL só vivem inteiramente dentro ou fora de uma transação, mas não no meio do caminho. Isso significa que, se você tiver iniciado um comando select antes de uma transação, mas não tiver consumido todos os valores, assim que a transação for iniciada, esse iterador será fechado. O mesmo se aplica às instruções DML que retornam resultados (elas são concluídas antes que a transação possa ser iniciada), e um comportamento semelhante é aplicado quando uma transação é confirmada ou revertida.
Há bons motivos para isso, e o comportamento é geralmente consistente com o que acontece com os mecanismos de banco de dados. A justificativa é que, se permitíssemos que os comandos sobrevivessem aos limites da transação, os comandos select não poderiam acessar as alterações da transação e os comandos DML não poderiam fazer a reversão completa.
Para evitar dúvidas, você não pode iniciar uma transação dentro de um loop que processa um SELECIONAR pois o iterador seria fechado assim que a transação fosse iniciada. Toda a parte externa SELECIONAR deve estar dentro da transação.
Conclusão
Com este blog, abordamos os principais tópicos relacionados à execução do N1QL dentro de funções JavaScript. O próximo blog se concentrará nos novos recursos do gerenciamento de bibliotecas JavaScript.