Модель процессов
Wippy выполняет код в изолированных процессах — легковесных конечных автоматах, общающихся через передачу сообщений. Такой подход на основе акторной модели устраняет баги разделяемого состояния и делает конкурентное программирование предсказуемым.
Выполнение как конечный автомат
Каждый процесс следует одному паттерну: инициализация, пошаговое выполнение с yield на блокирующих операциях, завершение по окончании. Планировщик мультиплексирует тысячи процессов на пуле воркеров, запуская другие процессы, пока один ждёт I/O.
Процессы поддерживают множественные параллельные yield — можно запустить несколько асинхронных операций и ждать завершения любой или всех. Это обеспечивает эффективный параллельный I/O без порождения дополнительных процессов.
flowchart LR
Ready --> Running
Running --> Blocked
Running --> Idle
Blocked --> Running
Idle --> Running
Running --> Complete
Процессы не ограничены Lua. Среда исполнения поддерживает любую реализацию конечного автомата — Go-процессы и WebAssembly-модули в планах.
Хосты процессов
Wippy запускает несколько хостов процессов в одной среде исполнения, каждый с разными возможностями и границами безопасности. Системные процессы с привилегированными функциями могут работать в одном хосте, изолированном от хостов с пользовательскими сессиями. Хосты могут ограничивать, что процессам разрешено делать — в Erlang для такого уровня изоляции понадобились бы отдельные узлы.
Некоторые хосты специализированы. Хост Terminal, например, запускает один процесс, но даёт ему доступ к I/O-операциям, которые другие хосты запрещают. Это позволяет совмещать разные уровни доверия в одном деплое — системные сервисы с полным доступом рядом с изолированным пользовательским кодом.
Модель безопасности
Каждый процесс выполняется под идентичностью актора и политикой безопасности. Обычно это пользователь, инициировавший вызов, но системные процессы работают под системным актором с другими привилегиями.
Контроль доступа работает на нескольких уровнях. У отдельных процессов свои уровни доступа. Отправка сообщений между хостами может быть запрещена политикой безопасности — изолированному пользовательскому процессу может быть вообще не разрешено отправлять сообщения системным хостам. Политика, привязанная к текущему актору, определяет, какие операции разрешены.
Создание процессов
Создавайте фоновые процессы через process.spawn():
local pid = process.spawn("app.workers:handler", "app:processes", arg1, arg2)
Первый аргумент — запись в реестре, второй — хост процессов, остальные передаются процессу.
Варианты spawn управляют отношениями жизненного цикла:
| Функция | Поведение |
|---|---|
spawn |
Запустить и забыть |
spawn_monitored |
Получать события EXIT при завершении потомка |
spawn_linked |
Двунаправленная связь — падение любого уведомляет другого |
Передача сообщений
Процессы общаются через сообщения, не через разделяемую память:
process.send(target_pid, "topic", payload)
Сообщения от одного отправителя приходят по порядку. Сообщения от разных отправителей могут чередоваться. Доставка выполняется по принципу «отправил и забыл»; используйте паттерн запрос-ответ, когда нужно подтверждение.
session_manager). Глобальный реестр для межнодовой адресации в планах.
Супервизия
Любой процесс может супервизировать другие, мониторя их. Процесс порождает потомков с мониторингом, отслеживает события EXIT и перезапускает их при сбое. Это следует философии Erlang «let it crash»: процессы падают на неожиданных условиях, а мониторящий процесс обрабатывает восстановление.
local worker = process.spawn_monitored("app.workers:handler", "app:processes")
local event = process.events():receive()
if event.kind == process.event.EXIT and event.result.error then
worker = process.spawn_monitored("app.workers:handler", "app:processes")
end
На корневом уровне среда исполнения предоставляет сервисы, которые запускают и супервизируют долгоживущие процессы — аналогично systemd в Linux. Определите запись process.service, чтобы среда исполнения управляла процессом:
- name: worker.service
kind: process.service
process: app.workers:handler
host: app:processes
lifecycle:
auto_start: true
restart:
max_attempts: 5
delay: 1s
Сервис запускается автоматически, перезапускается при падении с backoff и интегрируется с управлением жизненным циклом среды исполнения.
Обновление процессов
Работающие процессы могут обновлять свой код без потери идентичности. Вызовите process.upgrade() для перехода на новое определение с сохранением PID, почтового ящика и отношений супервизии:
process.upgrade("app.workers:v2", current_state)
Первый аргумент — новая запись в реестре (или nil для перезагрузки текущего определения). Дополнительные аргументы передаются новой версии, позволяя переносить состояние через обновление. Процесс немедленно продолжает выполнение с новым кодом.
Это обеспечивает горячую перезагрузку кода при разработке и обновления без простоя в продакшене. Среда исполнения кэширует скомпилированные прототипы, так что обновления не платят стоимость компиляции повторно. Если обновление по какой-то причине не удаётся, процесс падает и применяется обычная семантика супервизии — мониторящий родитель может перезапустить его с предыдущей версией или эскалировать сбой.
Планирование
Планировщик акторов использует захват работы между ядрами CPU. У каждого воркера локальная очередь для локальности кэша, с глобальной очередью для распределения. Процессы делают yield на блокирующих операциях, позволяя тысячам работать конкурентно на нескольких потоках.