mirror of
https://github.com/neondatabase/neon.git
synced 2026-01-16 18:02:56 +00:00
## Problem Follows on from #5299 - We didn't have a generic way to protect a tenant undergoing changes: `Tenant` had states, but for our arbitrary transitions between secondary/attached, we need a general way to say "reserve this tenant ID, and don't allow any other ops on it, but don't try and report it as being in any particular state". - The TenantsMap structure was behind an async RwLock, but it was never correct to hold it across await points: that would block any other changes for all tenants. ## Summary of changes - Add the `TenantSlot::InProgress` value. This means: - Incoming administrative operations on the tenant should retry later - Anything trying to read the live state of the tenant (e.g. a page service reader) should retry later or block. - Store TenantsMap in `std::sync::RwLock` - Provide an extended `get_active_tenant_with_timeout` for page_service to use, which will wait on InProgress slots as well as non-active tenants. Closes: https://github.com/neondatabase/neon/issues/5378 --------- Co-authored-by: Christian Schwarz <christian@neon.tech>
38 lines
1.0 KiB
Rust
38 lines
1.0 KiB
Rust
use std::time::Duration;
|
|
|
|
use tokio_util::sync::CancellationToken;
|
|
|
|
pub enum TimeoutCancellableError {
|
|
Timeout,
|
|
Cancelled,
|
|
}
|
|
|
|
/// Wrap [`tokio::time::timeout`] with a CancellationToken.
|
|
///
|
|
/// This wrapper is appropriate for any long running operation in a task
|
|
/// that ought to respect a CancellationToken (which means most tasks).
|
|
///
|
|
/// The only time you should use a bare tokio::timeout is when the future `F`
|
|
/// itself respects a CancellationToken: otherwise, always use this wrapper
|
|
/// with your CancellationToken to ensure that your task does not hold up
|
|
/// graceful shutdown.
|
|
pub async fn timeout_cancellable<F>(
|
|
duration: Duration,
|
|
cancel: &CancellationToken,
|
|
future: F,
|
|
) -> Result<F::Output, TimeoutCancellableError>
|
|
where
|
|
F: std::future::Future,
|
|
{
|
|
tokio::select!(
|
|
r = tokio::time::timeout(duration, future) => {
|
|
r.map_err(|_| TimeoutCancellableError::Timeout)
|
|
|
|
},
|
|
_ = cancel.cancelled() => {
|
|
Err(TimeoutCancellableError::Cancelled)
|
|
|
|
}
|
|
)
|
|
}
|