8.0 KiB
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
- Job Polling: Actor polls Redis queue using BLPOP
- Job Loading: Load job details from Redis hash
- Status Update: Mark job as "started"
- Script Execution: Execute Rhai script with job context
- Result Storage: Store result or error in Redis
- Status Update: Mark job as "finished" or "error"
- 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 operationsfreeflowuniverse.herolib.data.rhai
- Script executionfreeflowuniverse.herolib.core.base
- Base utilities
Usage Summary
To implement an actor in V lang:
- Create ActorConfig: Configure actor ID, database path, Redis URL, etc.
- Implement Actor Interface: Create a struct that implements the
Actor
interface - Implement process_job(): Add your job processing logic
- 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.