initial commit
This commit is contained in:
269
src/README.md
Normal file
269
src/README.md
Normal file
@@ -0,0 +1,269 @@
|
||||
# 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:
|
||||
|
||||
```text
|
||||
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
||||
│ 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:
|
||||
|
||||
```v
|
||||
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:
|
||||
|
||||
```v
|
||||
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:
|
||||
|
||||
```v
|
||||
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:
|
||||
|
||||
```v
|
||||
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:
|
||||
|
||||
```v
|
||||
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:
|
||||
|
||||
```v
|
||||
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.
|
Reference in New Issue
Block a user