Перейти к содержимому

Architecture

This page is a work in progress. Content may be incomplete or change.

Wippy is a layered system built on Go. Components initialize in dependency order, communicate through an event bus, and execute Lua processes via a work-stealing scheduler.

LayerComponents
ApplicationLua processes, functions, workflows
RuntimeLua engine (gopher-lua), 50+ modules
ServicesHTTP, Queue, Storage, Temporal
SystemTopology, Factory, Functions, Contracts
CoreScheduler, Registry, Dispatcher, EventBus, Relay
InfrastructureAppContext, Logger, Transcoder

Each layer depends only on layers below it. The Core layer provides fundamental primitives, while Services build higher-level abstractions on top.

Application startup proceeds through four phases.

Creates core infrastructure before any components load:

ComponentPurpose
AppContextSealed dictionary for component references
EventBusPub/sub for inter-component communication
TranscoderPayload serialization (JSON, YAML, Lua)
LoggerStructured logging with event streaming
RelayMessage routing (Node, Router, Mailbox)

The Loader resolves dependencies via topological sort and loads components level by level. Components at the same level load in parallel.

Базовые компоненты (PIDGen, Dispatcher, Registry, Finder, Supervisor) инициализируются первыми, затем следуют системные компоненты (Topology, Lifecycle, Factory, Functions, Contracts). Конкретные уровни вычисляются во время выполнения из графа зависимостей, поэтому порядок адаптируется при добавлении или удалении компонентов.

Each component attaches itself to context during Load, making services available to dependent components.

After all components load:

  1. Freeze Dispatcher - Locks command handler registry for lock-free lookups
  2. Seal AppContext - No more writes allowed, enables lock-free reads
  3. Start Components - Calls Start() on each component with Starter interface

Registry entries (from YAML files) are loaded and validated:

  1. Entries parsed from project files
  2. Pipeline stages transform entries (override, link, bytecode)
  3. Services marked auto_start: true begin running
  4. Supervisor monitors registered services

Components are Go services that participate in application lifecycle.

PhaseMethodPurpose
LoadLoad(ctx) (ctx, error)Initialize and attach to context
StartStart(ctx) errorBegin active operation
StopStop(ctx) errorGraceful shutdown

Components declare dependencies. The loader builds a directed acyclic graph and executes in topological order. Shutdown occurs in reverse order.

ComponentDependenciesPurpose
PIDGennoneProcess ID generation
DispatcherPIDGenCommand handler dispatch
RegistryDispatcherEntry storage and versioning
FinderRegistryEntry lookup and search
SupervisorRegistryService restart policies
TopologySupervisorProcess parent/child tree
LifecycleTopologyService lifecycle management
FactoryLifecycleProcess spawning
FunctionsFactoryStateless function calls

Asynchronous pub/sub for inter-component communication.

  • Single dispatcher goroutine processes all events
  • Queue-based action delivery prevents blocking publishers
  • Pattern matching supports exact topics and wildcards (*)
  • Context-based lifecycle ties subscriptions to cancellation
sequenceDiagram
participant P as Publisher
participant B as EventBus
participant S as Subscribers
P->>B: Publish(topic, data)
B->>B: Match patterns
B->>S: Queue action
S->>S: Execute callback

Topics are <system>:<kind>. The built-in systems publish:

SystemKindPurpose
registryentry.create, entry.update, entry.delete, entry.accept, entry.rejectEntry mutations
registryregistry.begin, registry.commit, registry.discardTransaction boundaries
processfactory.register, factory.delete, factory.accept, factory.rejectFactory registration for process kinds
supervisorservice.register, service.remove, service.update, service.start, service.stopService lifecycle

Versioned storage for entry definitions.

  • Versioned State - Each mutation creates new version
  • History - SQLite-backed history for audit trail
  • Observation - Watch specific entries for changes
  • Event-driven - Publishes events on mutations
flowchart LR
YAML[YAML Files] --> Parser
Parser --> Stages[Pipeline Stages]
Stages --> Registry
Registry --> Validation
Validation --> Active

Pipeline stages transform entries:

StagePurpose
OverrideApply config overrides
DisableRemove entries by pattern
LinkResolve requirements and dependencies
BytecodeCompile Lua to bytecode
EmbedFSCollect filesystem entries

Message routing between processes across nodes.

flowchart LR
subgraph Router
Local[Local Node] --> Peer[Peer Nodes]
Peer --> Inter[Internode]
end
Local -.- L[Same process]
Peer -.- P[Same cluster]
Inter -.- I[Remote]
  1. Local - Direct delivery within same node
  2. Peer - Forward to peer nodes in cluster
  3. Internode - Route to remote nodes via network

Each node has a mailbox with worker pool:

  • FNV-1a hashing assigns senders to workers
  • Preserves per-sender message ordering
  • Workers process messages concurrently
  • Back-pressure when queue fills

Sealed dictionary for component references.

PropertyBehavior
Before sealОднопоточная запись во время загрузки
After sealLock-free reads, panics on write
Duplicate keysPanic
Type safetyTyped getter functions

Components attach services during Load phase. After boot completes, AppContext is sealed for optimal read performance.

Graceful shutdown proceeds in reverse dependency order:

  1. SIGINT/SIGTERM triggers shutdown
  2. Supervisor stops managed services
  3. Components with Stopper interface receive Stop()
  4. Infrastructure cleanup

Second signal forces immediate exit.