feat: Pass current runtime handle in ExecutionPhase::Running
In order to perform any asynchronous operations as a result of receiving the "server is now running" broadcast message I found myself needing access to the current Tokio handle.
In my case I was trying to initialize a memcached client which is used as a 2nd level storage layer, for example:
pub fn init_server(config: &Config) -> Result<Server> {
// Start pingora server with a much reduced grace period....
let mut pingora_config = ServerConf::default();
pingora_config.work_stealing = true;
pingora_config.grace_period_seconds = Some(15);
pingora_config.graceful_shutdown_timeout_seconds = Some(10);
let mut server = Server::new_with_opt_and_conf(None, pingora_config);
// Set up ExecutionPhase broadcast hook to initialize memcached client as soon as server starts
let mut phase_watch = server.watch_execution_phase();
std::thread::spawn(move || {
while let Ok(phase) = phase_watch.blocking_recv() {
debug!("Received ExecutionPhase: {:?}", phase);
if let ExecutionPhase::Running(handle) = phase {
info!("Server entered Running phase - initializing memcached client");
// Use the provided runtime handle to spawn the initialization task
handle.spawn(async {
match get_memcached_client().await.version().await {
Ok(version) => info!("Memcached client initialized successfully - version: {}", version),
Err(e) => error!("Failed to initialize memcached client: {}", e),
}
});
break;
}
}
});
server.bootstrap();
...
Without this I couldn't find any other nice way of initializing the memcached library nicely before the server starts receiving requests.
Closes: #694
Hi @andrewhavck
I don't know if you have had a chance to review. Since I first created this I added some other phases where the runtime handle might be usefully passed. In addition I added a bit to the documentation on stop/start as well as an example of usage.