Modelo de procesos
Wippy ejecuta código en procesos aislados: máquinas de estado ligeras que se comunican mediante mensajes en lugar de memoria compartida. Este modelo de actores proporciona a cada proceso su propio estado y ciclo de vida.
Esta página explica el modelo de ciclo de vida y aislamiento. Usa la referencia de gestión de procesos para las API de creación, mensajería, monitorización, registro y upgrade. Consulta Process Host y servicios para los campos de servicios gestionados por el runtime.
Ejecución de la máquina de estados
Cada proceso se inicializa, avanza por su ejecución, cede en operaciones bloqueantes y se cierra al completarse. El scheduler multiplexa procesos en un pool de workers y ejecuta otro trabajo mientras un proceso espera I/O.
Los procesos admiten varias suspensiones concurrentes, por lo que el código puede iniciar varias operaciones asíncronas y esperar a una o a todas sin crear procesos adicionales.
flowchart LR
Ready --> Running
Running --> Blocked
Running --> Idle
Blocked --> Running
Idle --> Running
Running --> Complete
Los procesos no se limitan a Lua. El runtime también admite módulos WebAssembly mediante el tipo process.wasm, y su arquitectura de procesos puede incorporar otras implementaciones de máquinas de estado.
Process Hosts
Wippy puede ejecutar varios process hosts en un runtime, cada uno con sus propias capacidades y límites de seguridad. Los procesos privilegiados del sistema pueden ejecutarse en un host separado de los hosts que ejecutan sesiones de usuario.
Algunos hosts son especializados. Por ejemplo, el host Terminal usa un worker del scheduler y proporciona contexto de I/O de terminal a los procesos aceptados; no impone un límite de un solo proceso durante su vida. Los hosts separados permiten que un deployment ejecute procesos con distintos niveles de confianza.
Modelo de seguridad
Cada proceso se ejecuta bajo una identidad de actor y una security policy. Normalmente es el usuario que inició la llamada, mientras los procesos del sistema usan un actor del sistema con privilegios diferentes.
El control de acceso se aplica en varios niveles. La security policy puede restringir operaciones individuales del proceso y el envío de mensajes entre hosts. La policy asociada al actor actual determina qué operaciones están permitidas.
Para las implicaciones de seguridad del aislamiento de procesos, consulte el Modelo de Seguridad.
Creando Procesos
Creación de procesos
Crea procesos en segundo plano con process.spawn():
local pid, err = process.spawn("app.workers:handler", "app:processes", arg1, arg2)
if err then return nil, err end
return pid
El primer argumento es la entrada del registro, el segundo el process host y los restantes se pasan al proceso.
Las variantes de spawn controlan las relaciones del ciclo de vida:
| Función | Comportamiento |
|---|---|
spawn |
Inicia un proceso independiente |
spawn_monitored |
Recibe eventos EXIT cuando termina el child |
spawn_linked |
Un exit anormal se propaga en ambas direcciones; con trap_links: true, el peer recibe LINK_DOWN en vez de fallar |
Paso de mensajes
Los procesos se comunican mediante mensajes, no mediante memoria compartida:
local ok, err = process.send(target_pid, "topic", payload)
if err then return nil, err end
return ok
Los mensajes de un mismo remitente llegan en orden. Los de remitentes distintos pueden intercalarse. La entrega se realiza sin esperar respuesta; usa patrones de solicitud-respuesta si necesitas confirmación.
Supervisión
Cualquier proceso puede supervisar otros procesos monitorizándolos. Un supervisor inicia children monitorizados, observa eventos EXIT y decide si reiniciarlos tras un fallo.
local worker, spawn_err = process.spawn_monitored("app.workers:handler", "app:processes")
if spawn_err then return nil, spawn_err end
local event, open = process.events():receive()
if not open then return nil, errors.new("process event channel closed") end
if event.kind == process.event.EXIT and event.result.error then
local replacement, restart_err = process.spawn_monitored("app.workers:handler", "app:processes")
if restart_err then return nil, restart_err end
worker = replacement
end
A nivel del runtime, los servicios pueden iniciar y supervisar procesos de larga duración. Define una entrada process.service para que el runtime gestione un proceso:
- name: worker.service
kind: process.service
process: app.workers:handler
host: app:processes
lifecycle:
auto_start: true
restart:
max_attempts: 5
initial_delay: 1s
max_delay: 30s
backoff_factor: 2.0
El servicio se inicia automáticamente y se integra en la gestión del ciclo de vida del runtime. En el runtime fijado, el primer inicio fallido cuenta para max_attempts, por lo que 5 permite como máximo cuatro inicios posteriores. Cada reintento espera initial_delay con jitter; el delay no aumenta entre intentos.
Upgrade de procesos
Los procesos en ejecución pueden actualizar su código sin perder identidad. Llama a process.upgrade() para cambiar a una definición nueva conservando PID, mailbox y relaciones de supervisión:
process.upgrade("app.workers:v2", current_state)
El primer argumento es la nueva entrada del registro (o nil para recargar la definición actual). Los argumentos adicionales pasan a la versión nueva, lo que permite conservar estado durante el upgrade. El proceso reanuda de inmediato la ejecución con el código nuevo.
El runtime guarda en cache los prototipos compilados para evitar compilaciones repetidas. Si un upgrade falla, el proceso crashea y se aplica la supervisión normal; un parent que lo monitoriza puede reiniciarlo o escalar el fallo.
Scheduling
El actor scheduler usa work-stealing entre cores de CPU. Cada worker tiene una queue local para locality de cache y una queue global para distribuir trabajo. Los procesos ceden en operaciones bloqueantes para que otros procesos se ejecuten en el pool.