-
Notifications
You must be signed in to change notification settings - Fork 1
Open
Description
Overview
The Go implementation has a Component interface that combines Startable and ReadyDoneAware traits. This provides a complete lifecycle management interface for all components in the system.
Background
Reference implementation: skipgraph-go/modules/component.go
A Component is any module that:
- Can be started (Startable)
- Has ready and done states (ReadyDoneAware)
Requirements
1. Define Component Trait
use async_trait::async_trait;
/// A component that can be started and has lifecycle awareness
#[async_trait]
pub trait Component: Startable + ReadyDoneAware + Send + Sync {
// No additional methods needed - this is a marker trait
// that combines Startable and ReadyDoneAware
}
// Blanket implementation for any type that implements both traits
impl<T> Component for T
where
T: Startable + ReadyDoneAware + Send + Sync
{
}2. Provide Base Component Implementation
use std::sync::Arc;
use tokio::sync::watch;
/// Base implementation that components can wrap or extend
pub struct BaseComponent {
start_once: StartOnce,
lifecycle: LifecycleState,
}
impl BaseComponent {
pub fn new() -> Self {
Self {
start_once: StartOnce::new(),
lifecycle: LifecycleState::new(),
}
}
/// Helper to check and mark as started
pub fn ensure_start_once(&self) -> Result<(), &'static str> {
self.start_once.ensure_once()
}
/// Signal that component is ready
pub fn signal_ready(&self) {
self.lifecycle.signal_ready()
}
/// Signal that component is done
pub fn signal_done(&self) {
self.lifecycle.signal_done()
}
}
impl ReadyDoneAware for BaseComponent {
fn ready(&self) -> watch::Receiver<bool> {
self.lifecycle.ready()
}
fn done(&self) -> watch::Receiver<bool> {
self.lifecycle.done()
}
}3. Example Component Implementation
pub struct NetworkComponent {
base: BaseComponent,
port: u16,
// other fields
}
impl NetworkComponent {
pub fn new(port: u16) -> Self {
Self {
base: BaseComponent::new(),
port,
}
}
}
#[async_trait]
impl Startable for NetworkComponent {
async fn start(&self, ctx: Arc<dyn ThrowableContext>) {
if let Err(e) = self.base.ensure_start_once() {
panic!("{}", e);
}
let base = self.base.clone();
let port = self.port;
tokio::spawn(async move {
// Start network listener
match start_listener(port).await {
Ok(listener) => {
base.signal_ready();
// Run until cancelled
let mut cancelled = ctx.cancelled();
tokio::select! {
_ = serve_connections(listener) => {},
_ = cancelled.changed() => {},
}
base.signal_done();
}
Err(e) => {
ctx.throw_irrecoverable(Box::new(e));
}
}
});
}
}
impl ReadyDoneAware for NetworkComponent {
fn ready(&self) -> watch::Receiver<bool> {
self.base.ready()
}
fn done(&self) -> watch::Receiver<bool> {
self.base.done()
}
}
// NetworkComponent automatically implements Component
// due to the blanket implementation4. Usage Pattern
async fn start_application() {
let ctx = Arc::new(Context::new());
let network = Arc::new(NetworkComponent::new(8080));
let storage = Arc::new(StorageComponent::new());
// Start components
network.start(ctx.clone()).await;
storage.start(ctx.clone()).await;
// Wait for components to be ready
wait_for_ready(&network).await;
wait_for_ready(&storage).await;
println!("Application ready");
}
async fn wait_for_ready(component: &dyn Component) {
let mut ready = component.ready();
while !*ready.borrow() {
ready.changed().await.ok();
}
}Benefits
- Unified interface for all components
- Consistent lifecycle management
- Easy to compose components
- Type-safe component handling
Testing
- Test blanket implementation
- Test with various component types
- Test lifecycle transitions
- Test error scenarios
Dependencies
- async-trait
- tokio
- Depends on: Implement ThrowableContext for Error Propagation #52 (ThrowableContext), Implement ReadyDoneAware Trait for Lifecycle State Management #53 (ReadyDoneAware), Implement Startable Trait for Component Initialization #54 (Startable)
Priority
High - This is the core trait for the component system
Related Issues
- Depends on: Implement ThrowableContext for Error Propagation #52, Implement ReadyDoneAware Trait for Lifecycle State Management #53, Implement Startable Trait for Component Initialization #54
- Blocks: ComponentManager implementation
Metadata
Metadata
Assignees
Labels
No labels