use std::sync::Arc;
use std::thread::sleep;
use std::time::{Duration, Instant};
use parking_lot::RwLock;
use crate::segment::common::operation_error::{OperationError, OperationResult};
use crate::segment::entry::{ReadSegmentEntry, SegmentEntry, StorageSegmentEntry as _};
use crate::segment::segment::Segment;
use crate::shard::proxy_segment::ProxySegment;
const DROP_SPIN_TIMEOUT: Duration = Duration::from_millis(10);
const DROP_DATA_TIMEOUT: Duration = Duration::from_secs(60 * 60);
const DROP_DATA_RETRY_TIMEOUT: Duration = Duration::from_millis(100);
pub enum DropDataOutcome {
StillInUse(LockedSegment, OperationError),
Failed(OperationError),
}
#[derive(Clone, Debug)]
pub enum LockedSegment {
Original(Arc<RwLock<Segment>>),
Proxy(Arc<RwLock<ProxySegment>>),
}
fn try_unwrap_with_timeout<T>(
mut arc: Arc<T>,
spin: Duration,
timeout: Duration,
) -> Result<T, Arc<T>> {
let start = Instant::now();
loop {
arc = match Arc::try_unwrap(arc) {
Ok(unwrapped) => return Ok(unwrapped),
Err(arc) => arc,
};
if start.elapsed() >= timeout {
return Err(arc);
}
sleep(spin);
}
}
impl LockedSegment {
pub fn new<T>(segment: T) -> Self
where
T: Into<LockedSegment>,
{
segment.into()
}
pub fn get(&self) -> &RwLock<dyn SegmentEntry> {
match self {
LockedSegment::Original(segment) => segment.as_ref(),
LockedSegment::Proxy(proxy) => proxy.as_ref(),
}
}
pub fn get_read(&self) -> &RwLock<dyn ReadSegmentEntry> {
match self {
LockedSegment::Original(segment) => segment.as_ref(),
LockedSegment::Proxy(proxy) => proxy.as_ref(),
}
}
pub fn get_read_arc(&self) -> Arc<RwLock<dyn ReadSegmentEntry>> {
match self {
LockedSegment::Original(segment) => segment.clone(),
LockedSegment::Proxy(proxy) => proxy.clone(),
}
}
pub fn is_original(&self) -> bool {
match self {
LockedSegment::Original(_) => true,
LockedSegment::Proxy(_) => false,
}
}
pub fn drop_data(self) -> OperationResult<()> {
self.drop_data_with_timeout(DROP_DATA_TIMEOUT)
.map_err(|outcome| match outcome {
DropDataOutcome::StillInUse(_, err) | DropDataOutcome::Failed(err) => err,
})
}
pub fn try_drop_data(self) -> Result<(), DropDataOutcome> {
self.drop_data_with_timeout(DROP_DATA_RETRY_TIMEOUT)
}
fn drop_data_with_timeout(self, timeout: Duration) -> Result<(), DropDataOutcome> {
match self {
LockedSegment::Original(segment) => {
match try_unwrap_with_timeout(segment, DROP_SPIN_TIMEOUT, timeout) {
Ok(raw_locked_segment) => raw_locked_segment
.into_inner()
.drop_data()
.map_err(DropDataOutcome::Failed),
Err(arc) => {
let err = OperationError::service_error(format!(
"Removing segment which is still in use: {:?}",
arc.read().data_path(),
));
Err(DropDataOutcome::StillInUse(
LockedSegment::Original(arc),
err,
))
}
}
}
LockedSegment::Proxy(proxy) => {
match try_unwrap_with_timeout(proxy, DROP_SPIN_TIMEOUT, timeout) {
Ok(raw_locked_segment) => raw_locked_segment
.into_inner()
.drop_data()
.map_err(DropDataOutcome::Failed),
Err(arc) => {
let err = OperationError::service_error(format!(
"Removing proxy segment which is still in use: {:?}",
arc.read().data_path(),
));
Err(DropDataOutcome::StillInUse(LockedSegment::Proxy(arc), err))
}
}
}
}
}
}
impl From<Segment> for LockedSegment {
fn from(s: Segment) -> Self {
LockedSegment::Original(Arc::new(RwLock::new(s)))
}
}
impl From<ProxySegment> for LockedSegment {
fn from(s: ProxySegment) -> Self {
LockedSegment::Proxy(Arc::new(RwLock::new(s)))
}
}