Funções

Funções são pontos de entrada de chamada e retorno. Uma função herda o contexto do chamador e é cancelada quando ele é cancelado. Os pools podem reutilizar estados Lua; portanto, globais de módulo e upvalues de closures podem sobreviver em um worker, mas não são compartilhados de maneira consistente entre chamadas. Armazene estado durável ou compartilhado fora da função. Use funções para handlers HTTP, endpoints de API e outras operações que terminem dentro do ciclo de vida de uma requisição.

Chamando funções

Chame funções de forma síncrona com funcs.call():

local funcs = require("funcs")
local result, err = funcs.call("app.api:get_user", user_id)
if err then return nil, err end
return result

Para execução não bloqueante, use funcs.async():

local future, err = funcs.async("app.process:analyze", data)
if err then
    return nil, err
end

local ch = future:response()
local payload, open = ch:receive()
if not open then
    return nil, "future response channel closed"
end

local result, err = payload:data()
if err then
    return nil, err
end

Consulte o módulo funcs para a invocação de funções e as opções do executor.

Propagação de contexto

Cada chamada cria um frame com seu próprio escopo de contexto. Funções filhas herdam o contexto do 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 funcs = require("funcs")

local exec, err = funcs.new():with_context({trace_id = "abc-123"})
if err then return nil, err end

local result, err = exec:call("app.api:process", data)
if err then return nil, err end
return result

O contexto de segurança se propaga da mesma forma. As funções chamadas veem o ator do chamador e podem verificar permissões. Consulte o módulo security para as APIs de controle de acesso.

Definição no registro

No nível do registro, uma entrada de função tem este formato:

- 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 filas e 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 do pool determina o comportamento de escala.

Inline executa na goroutine do chamador, sem um pool de workers. É usado em contextos embutidos.

Static mantém um número fixo de workers. As requisições entram na fila quando todos estão ocupados, mantendo fixa a concorrência de workers.

pool:
  type: static
  size: 8
  buffer: 512

Lazy inicia sem workers e os cria sob demanda. Workers ociosos são removidos após um timeout.

pool:
  type: lazy
  max_size: 32

Adaptive ajusta a quantidade de workers com base no throughput medido e na carga atual.

pool:
  type: adaptive
  max_size: 256
Se você não especificar um tipo de pool, o runtime seleciona um baseado na sua configuração. Defina `workers` para static, `max_size` para lazy, ou defina `type` explicitamente para controle total. Se nenhum dos dois for definido, o pool é lazy com no máximo 16 workers.

Interceptadores

Chamadas de função passam por uma cadeia de interceptadores. Interceptadores podem tratar preocupações transversais separadamente da implementação da função.

- 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

Os interceptadores incorporados incluem retry com backoff exponencial. Integrações do runtime escritas em Go podem registrar outros interceptadores para logging, métricas, tracing, autorização, circuit breaking ou transformação de requisições; entradas de aplicação Lua podem configurar apenas os interceptadores instalados pelo runtime.

A cadeia executa antes e depois de cada chamada. Cada interceptador pode modificar a requisição, interromper a execução antecipadamente ou envolver a resposta.

Contratos

Funções podem expor seus schemas de entrada e saída como contratos. Contratos definem assinaturas de métodos que permitem validação no runtime e geração de documentação.

local contract = require("contract")
local sender = contract.get("app.email:sender")
local email = sender:open("app.email:sender_impl")
email:send({to = "user@example.com", subject = "Hello"})

Contratos permitem que os chamadores usem uma interface e escolham sua implementação separadamente. Isso favorece testes, implantações multi-tenant e migrações graduais.

Funções versus processos

Funções herdam o contexto e o ciclo de vida do chamador. Quando ele é cancelado, suas chamadas de função também são canceladas. Isso é adequado à execução em handlers HTTP e consumidores de filas.

Processos executam independentemente com o contexto do host. Eles sobrevivem ao criador e se comunicam por mensagens. Use processos para trabalho em segundo plano; use funções para operações no escopo da requisição.