Funções
Funções são pontos de entrada síncronos e sem estado. Você as chama, elas executam, elas retornam um resultado. Quando uma função executa, ela herda o contexto do chamador — se o chamador cancela, a função cancela também. Isso torna funções ideais para handlers HTTP, endpoints de API, e qualquer operação que deve completar dentro do ciclo de vida de uma requisição.
Chamando Funções
Chame funções sincronamente com funcs.call():
local funcs = require("funcs")
local result, err = funcs.call("app.api:get_user", user_id)
Para execução não-bloqueante, use funcs.async():
local future = funcs.async("app.process:analyze", data)
local ch = future:response()
local result, ok = ch:receive()
Veja o módulo funcs para a API completa.
Propagação de Contexto
Cada chamada cria um frame com seu próprio escopo de contexto. Funções filhas herdam o contexto pai sem passagem explícita:
local ctx = require("ctx")
local trace_id = ctx.get("trace_id")
local user_id = ctx.get("user_id")
Adicione contexto ao chamar:
local exec = funcs.new()
:with_context({trace_id = "abc-123"})
:call("app.api:process", data)
O contexto de segurança se propaga da mesma forma. Funções chamadas veem o ator do chamador e podem verificar permissões. Veja o módulo security para APIs de controle de acesso.
Definição no Registro
No nível do registro, uma entrada de função se parece com isso:
- name: get_user
kind: function.lua
source: file://handlers/user.lua
method: get
pool:
type: lazy
max_size: 16
Funções podem ser invocadas por outros componentes do runtime — handlers HTTP, consumidores de fila, jobs agendados — e estão sujeitas a verificações de permissão baseadas no contexto de segurança do chamador.
Pools
Funções executam em pools que gerenciam a execução. O tipo de pool determina o comportamento de escalabilidade.
Inline executa na goroutine do chamador. Sem concorrência, zero overhead de alocação. Usado para contextos embutidos.
Static mantém um número fixo de workers. Requisições enfileiram quando todos os workers estão ocupados. Uso de recursos previsível.
pool:
type: static
workers: 8
buffer: 512
Lazy começa vazio e cria workers sob demanda. Workers ociosos são destruídos após um timeout. Eficiente para tráfego variável.
pool:
type: lazy
max_size: 32
Adaptive escala automaticamente baseado no throughput. O controlador mede o desempenho e ajusta a contagem de workers para otimizar para a carga atual.
pool:
type: adaptive
max_size: 256
Interceptadores
Chamadas de função passam por uma cadeia de interceptadores. Interceptadores tratam preocupações transversais sem tocar na lógica de negócio.
- name: my_function
kind: function.lua
source: file://handler.lua
method: main
meta:
options:
retry:
max_attempts: 3
initial_delay: 100
backoff_factor: 2.0
Interceptadores embutidos incluem retry com backoff exponencial. Você pode adicionar interceptadores personalizados para logging, métricas, tracing, autorização, circuit breaking, ou transformação de requisição.
A cadeia executa antes e depois de cada chamada. Cada interceptador pode modificar a requisição, curto-circuitar a execução, ou encapsular a resposta.
Contratos
Funções podem expor seus schemas de entrada/saída como contratos. Contratos definem assinaturas de método que permitem validação em tempo de execução e geração de documentação.
local contract = require("contract")
local email = contract.get("app.email:sender")
email:send({to = "user@example.com", subject = "Hello"})
Essa abstração permite trocar implementações sem mudar o código chamador — útil para testes, implantações multi-tenant, ou migrações graduais.
Funções vs Processos
Funções herdam contexto do chamador e se vinculam ao ciclo de vida do chamador. Quando o chamador cancela, funções cancelam. Isso permite execução na borda — executando diretamente em handlers HTTP e consumidores de fila.
Processos executam independentemente com contexto de host. Eles sobrevivem ao seu criador e se comunicam através de mensagens. Use processos para trabalho em segundo plano; use funções para operações no escopo da requisição.