Files
runner_v/src
2025-08-26 17:16:01 +02:00
..
2025-08-26 17:16:01 +02:00
2025-08-26 14:47:52 +02:00
2025-08-26 14:47:52 +02:00
2025-08-26 17:16:01 +02:00
2025-08-26 14:47:52 +02:00
2025-08-26 14:47:52 +02:00
2025-08-26 17:16:01 +02:00

V Lang Actor Interface

This module provides a V lang port of the Rust actor trait interface for the Hero Baobab system. It enables implementing actors in V lang with a simple interface: implement process_job and use spawn to run the actor.

Architecture

The V lang actor interface mirrors the Rust implementation with these key components:

┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐
│   SyncActor    │    │  AsyncActor    │    │ MyCustomActor  │
│                 │    │                 │    │                 │
│ process_job()   │    │ process_job()   │    │ process_job()   │
│ (sequential)    │    │ (concurrent)    │    │ (custom logic)  │
└─────────────────┘    └─────────────────┘    └─────────────────┘
          │                       │                       │
          └───────────────┬───────────────────────────────┘
                          │
                  ┌───────▼───────┐
                  │ Actor Interface│
                  │               │
                  │ spawn_actor() │
                  │ process_job() │
                  │ config        │
                  └───────────────┘

Core Components

Actor Interface

All actors must implement the Actor interface:

pub interface Actor {
    // Process a single job - must be implemented by concrete actors
    process_job(job Job, mut redis_conn redisclient.Redis) !
    
    // Get the actor type identifier
    actor_type() string
    
    // Get the actor ID
    actor_id() string
    
    // Get the Redis URL
    redis_url() string
    
    // Get the database path
    db_path() string
    
    // Check if tasks should be preserved
    preserve_tasks() bool
}

ActorConfig

Configuration structure for all actors:

pub struct ActorConfig {
pub:
    actor_id        string
    db_path         string
    redis_url       string
    preserve_tasks  bool
    default_timeout ?time.Duration // Optional timeout for async actors
}

Job Structure

Jobs processed by actors:

pub struct Job {
pub mut:
    id         string
    caller_id  string
    context_id string
    script     string
    status     JobStatus
    result     string
    error      string
    created_at time.Time
    started_at time.Time
    finished_at time.Time
}

Built-in Actor Implementations

SyncActor

Processes jobs sequentially, one at a time:

import freeflowuniverse.herolib.lib.baobab.actor

// Create configuration
config := actor.new_actor_config(
    'sync_actor_1',
    '/path/to/database',
    'redis://localhost:6379',
    false
)

// Create and spawn sync actor
sync_actor := actor.new_sync_actor(config)!
mut shutdown_chan := chan bool{cap: 1}
go actor.spawn_actor(sync_actor, mut shutdown_chan)

// Later, shutdown the actor
shutdown_chan <- true

AsyncActor

Processes jobs concurrently with timeout support:

import time
import freeflowuniverse.herolib.lib.baobab.actor

// Create configuration with timeout
mut config := actor.new_actor_config(
    'async_actor_1',
    '/path/to/database', 
    'redis://localhost:6379',
    false
)
config = config.with_default_timeout(time.Duration(300 * time.second))

// Create and spawn async actor
async_actor := actor.new_async_actor(config)!
mut shutdown_chan := chan bool{cap: 1}
go actor.spawn_actor(async_actor, mut shutdown_chan)

// Later, shutdown the actor
shutdown_chan <- true

Creating Custom Actors

To implement a custom actor, simply implement the Actor interface:

import freeflowuniverse.herolib.lib.baobab.actor

struct MyCustomActor {
pub:
    config actor.ActorConfig
mut:
    engine rhai.Engine
    // Add your custom fields here
}

pub fn new_custom_actor(config actor.ActorConfig) !MyCustomActor {
    mut engine := rhai.new_engine()!
    
    return MyCustomActor{
        config: config
        engine: engine
    }
}

// Implement the Actor interface
pub fn (mut actor MyCustomActor) process_job(job actor.Job, mut redis_conn redisclient.Redis) ! {
    // Your custom job processing logic here
    
    // Update job status to started
    actor.update_job_status(mut redis_conn, job.id, .started)!
    
    // Execute the script (or your custom logic)
    result := actor.execute_job_with_engine(mut actor.engine, job, actor.config.db_path)!
    
    // Update job status to finished and set result
    actor.update_job_status(mut redis_conn, job.id, .finished)!
    actor.set_job_result(mut redis_conn, job.id, result)!
    
    // Clean up if needed
    actor.cleanup_job(mut redis_conn, job.id, job.context_id, actor.config.preserve_tasks)!
}

// Implement required interface methods
pub fn (actor MyCustomActor) actor_type() string { return 'MyCustomActor' }
pub fn (actor MyCustomActor) actor_id() string { return actor.config.actor_id }
pub fn (actor MyCustomActor) redis_url() string { return actor.config.redis_url }
pub fn (actor MyCustomActor) db_path() string { return actor.config.db_path }
pub fn (actor MyCustomActor) preserve_tasks() bool { return actor.config.preserve_tasks }

// Usage
config := actor.new_actor_config('my_actor', '/db/path', 'redis://localhost:6379', false)
custom_actor := new_custom_actor(config)!
mut shutdown_chan := chan bool{cap: 1}
go actor.spawn_actor(custom_actor, mut shutdown_chan)

Key Features

Unified Interface

  • Same spawn_actor() function works with all actor implementations
  • Consistent configuration and lifecycle management
  • Clean separation between actor logic and infrastructure

Redis Integration

  • Automatic Redis connection management
  • Job polling from actor-specific queues (hero:job:actor_queue:{actor_id})
  • Job status tracking and result storage
  • Optional job cleanup based on preserve_tasks setting

Script Execution

  • Rhai script engine integration
  • Automatic job context setup (DB_PATH, CALLER_ID, CONTEXT_ID)
  • Error handling and status updates

Graceful Shutdown

  • Channel-based shutdown signaling
  • Proper cleanup of resources
  • Support for cancelling running jobs (AsyncActor)

Job Processing Flow

  1. Job Polling: Actor polls Redis queue using BLPOP
  2. Job Loading: Load job details from Redis hash
  3. Status Update: Mark job as "started"
  4. Script Execution: Execute Rhai script with job context
  5. Result Storage: Store result or error in Redis
  6. Status Update: Mark job as "finished" or "error"
  7. Cleanup: Optionally remove job from Redis

Error Handling

All operations include comprehensive error handling:

  • Redis connection failures
  • Job loading errors
  • Script execution errors
  • Timeout handling (AsyncActor)
  • Graceful degradation and logging

Migration from Rust

This V lang implementation provides the same functionality as the Rust version:

  • Actor trait abstraction
  • Sync and async actor implementations
  • Unified spawn interface
  • Redis job queue integration
  • Rhai script execution
  • Job lifecycle management
  • Error handling and logging
  • Graceful shutdown

Dependencies

The actor interface depends on these herolib modules:

  • freeflowuniverse.herolib.clients.redisclient - Redis operations
  • freeflowuniverse.herolib.data.rhai - Script execution
  • freeflowuniverse.herolib.core.base - Base utilities

Usage Summary

To implement an actor in V lang:

  1. Create ActorConfig: Configure actor ID, database path, Redis URL, etc.
  2. Implement Actor Interface: Create a struct that implements the Actor interface
  3. Implement process_job(): Add your job processing logic
  4. Use spawn(): Call spawn_actor() to start the actor loop

That's it! The interface handles all the Redis polling, job management, and infrastructure concerns automatically.