use super::*;
#[derive(Debug, Clone)]
pub struct CheckpointArtifact {
pub metadata: CheckpointMetadata,
pub native_session_id: String,
pub event_frontier_digest: String,
}
pub(in crate::controller) enum ControllerRelayLease {
Managed {
handle: ManagedSessionHandle,
lease: Option<ManagedSessionLease>,
},
Standalone(StandaloneSession),
}
impl ControllerRelayLease {
pub(in crate::controller) fn connection_mut(&mut self) -> &mut StandaloneSession {
match self {
Self::Managed { lease, .. } => lease
.as_mut()
.expect("checkpoint latch has already returned its connection")
.connection_mut(),
Self::Standalone(connection) => connection,
}
}
pub(super) async fn submit(
&mut self,
command_id: String,
command: RelayCommand,
) -> Result<u64> {
match self {
Self::Managed {
lease: Some(lease), ..
} => lease.connection_mut().submit(command_id, command).await,
Self::Managed { handle, .. } => handle.submit(command_id, command).await,
Self::Standalone(connection) => connection.submit(command_id, command).await,
}
}
pub(super) async fn sync_snapshot(&mut self) -> Result<ManagedSessionSnapshot> {
match self {
Self::Managed {
lease: Some(lease), ..
} => lease.connection_mut().sync().await,
Self::Managed { handle, .. } => {
handle.sync_now().await?;
handle
.view()
.snapshot
.context("managed session has no snapshot")
}
Self::Standalone(connection) => connection.sync().await,
}
}
pub(super) fn replace_connection(&mut self, connection: StandaloneSession) {
match self {
Self::Managed {
lease: Some(lease), ..
} => lease.replace_connection(connection),
Self::Standalone(existing) => *existing = connection,
Self::Managed { lease: None, .. } => {
*self = Self::Standalone(connection);
}
}
}
pub(super) fn end_latch(&mut self) {
if let Self::Managed { lease, .. } = self
&& let Some(lease) = lease.take()
{
lease.release();
}
}
pub(super) async fn cancel_abandoned_barrier(&mut self) -> Result<()> {
let Self::Managed { handle, lease } = self else {
return Ok(());
};
match lease.take() {
Some(lease) => drop(lease),
None => drop(handle.lease_connection().await?),
}
Ok(())
}
pub(in crate::controller) fn release(self) {
if let Self::Managed {
lease: Some(lease), ..
} = self
{
lease.release();
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(in crate::controller) enum LatchExclusivity {
ReleaseAfterLatch,
HoldThroughClose,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(in crate::controller) enum CheckpointExportPolicy {
Always,
ReuseUnchangedArchive,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(in crate::controller) enum CheckpointCompletion {
HeldBarrier,
ReleasedAfterCapture,
}
pub(in crate::controller) struct LatchedCheckpoint {
pub(in crate::controller) artifact: CheckpointArtifact,
pub(in crate::controller) relay: ControllerRelayLease,
pub(in crate::controller) barrier_command_id: String,
pub(in crate::controller) cursor: RelayCursor,
pub(in crate::controller) completion: CheckpointCompletion,
}
impl LatchedCheckpoint {
pub(super) async fn complete(mut self) -> Result<()> {
let (prefix, command) = match self.completion {
CheckpointCompletion::HeldBarrier => (
"checkpoint-complete",
RelayCommand::CompleteCheckpoint {
barrier_command_id: self.barrier_command_id.clone(),
},
),
CheckpointCompletion::ReleasedAfterCapture => (
"checkpoint-floor",
RelayCommand::AdvanceRecoveryFloor {
through: self.cursor.clone(),
},
),
};
let command_id = new_command_id(prefix)?;
self.relay.submit(command_id, command).await.map(|_| ())
}
pub(super) async fn abandon(mut self, session_id: &str) {
if self.completion == CheckpointCompletion::ReleasedAfterCapture {
return;
}
if let Err(error) = self.relay.cancel_abandoned_barrier().await {
tracing::warn!(
session_id,
"abandoned checkpoint could not cancel its relay barrier: {error:#}"
);
}
}
}