114 lines
3.8 KiB
Rust
114 lines
3.8 KiB
Rust
//! Lazy initialization pattern using announcement
|
|
//!
|
|
//! This example demonstrates using announcement to signal when
|
|
//! a resource is ready, allowing dependent tasks to wait.
|
|
//!
|
|
//! Run with: cargo run --example lazy_init
|
|
|
|
use announcement::Announcement;
|
|
use std::sync::Arc;
|
|
use std::time::Duration;
|
|
use tokio::time::sleep;
|
|
|
|
#[derive(Debug)]
|
|
struct DatabaseConnection {
|
|
connection_string: String,
|
|
pool_size: usize,
|
|
}
|
|
|
|
impl DatabaseConnection {
|
|
async fn connect(connection_string: String) -> Self {
|
|
println!("Database: Connecting to {}...", connection_string);
|
|
|
|
// Simulate connection establishment
|
|
sleep(Duration::from_millis(800)).await;
|
|
|
|
println!("Database: Connection established");
|
|
Self {
|
|
connection_string,
|
|
pool_size: 10,
|
|
}
|
|
}
|
|
|
|
fn execute_query(&self, query: &str) -> String {
|
|
format!(
|
|
"Query '{}' executed on {} [pool: {}]",
|
|
query, self.connection_string, self.pool_size
|
|
)
|
|
}
|
|
}
|
|
|
|
#[tokio::main]
|
|
async fn main() {
|
|
// Create announcement for database connection
|
|
let (db_announcer, db_announcement) = Announcement::new();
|
|
|
|
// Multiple services that need the database
|
|
let api_service = tokio::spawn({
|
|
let db_listener = db_announcement.listener();
|
|
async move {
|
|
println!("API Service: Starting up...");
|
|
println!("API Service: Waiting for database connection...");
|
|
|
|
// Wait for database to be ready
|
|
let db: Arc<DatabaseConnection> = db_listener.listen().await.unwrap();
|
|
|
|
println!("API Service: Database ready, processing requests");
|
|
let result = db.execute_query("SELECT * FROM users");
|
|
println!("API Service: {}", result);
|
|
}
|
|
});
|
|
|
|
let background_worker = tokio::spawn({
|
|
let db_listener = db_announcement.listener();
|
|
async move {
|
|
println!("Background Worker: Starting up...");
|
|
println!("Background Worker: Waiting for database connection...");
|
|
|
|
let db: Arc<DatabaseConnection> = db_listener.listen().await.unwrap();
|
|
|
|
println!("Background Worker: Database ready, starting jobs");
|
|
let result = db.execute_query("UPDATE jobs SET status = 'running'");
|
|
println!("Background Worker: {}", result);
|
|
}
|
|
});
|
|
|
|
let metrics_collector = tokio::spawn({
|
|
let db_listener = db_announcement.listener();
|
|
async move {
|
|
println!("Metrics Collector: Starting up...");
|
|
|
|
// Try polling first (non-blocking check)
|
|
if db_listener.try_listen().is_none() {
|
|
println!("Metrics Collector: Database not ready yet, waiting...");
|
|
let db: Arc<DatabaseConnection> = db_listener.listen().await.unwrap();
|
|
println!("Metrics Collector: Database ready");
|
|
let result = db.execute_query("INSERT INTO metrics VALUES (...)");
|
|
println!("Metrics Collector: {}", result);
|
|
}
|
|
}
|
|
});
|
|
|
|
// Simulate initialization work before database connection
|
|
println!("Main: Initializing application...");
|
|
sleep(Duration::from_millis(200)).await;
|
|
|
|
// Now establish the database connection
|
|
println!("\nMain: Starting database initialization...\n");
|
|
let db = DatabaseConnection::connect("postgresql://localhost:5432/myapp".to_string()).await;
|
|
|
|
// Wrap in Arc for efficient sharing
|
|
let db = Arc::new(db);
|
|
|
|
// Announce that database is ready
|
|
// All waiting services will be notified simultaneously
|
|
println!("\nMain: Broadcasting database connection...\n");
|
|
db_announcer.announce(db.clone()).unwrap();
|
|
|
|
// Wait for all services to complete
|
|
let _: (Result<(), _>, Result<(), _>, Result<(), _>) =
|
|
tokio::join!(api_service, background_worker, metrics_collector);
|
|
|
|
println!("\nMain: All services initialized successfully");
|
|
}
|