mirror of
https://github.com/neondatabase/neon.git
synced 2026-05-28 10:30:40 +00:00
WIP
This commit is contained in:
@@ -70,6 +70,18 @@ service PageService {
|
||||
// Acquires or extends a lease on the given LSN. This guarantees that the Pageserver won't garbage
|
||||
// collect the LSN until the lease expires. Must be acquired on all relevant shards.
|
||||
rpc LeaseLsn (LeaseLsnRequest) returns (LeaseLsnResponse);
|
||||
|
||||
// Upserts a standby_horizon lease. RO replicas rely on this type of lease.
|
||||
// In slightly more detail: RO replicas always lag to some degree behind the
|
||||
// primary, and request pages at their respective apply LSN. The standby horizon mechanism
|
||||
// ensures that the Pageserver does not garbage-collect old page versions in
|
||||
// the interval between `min(valid standby horizon leases)` and the most recent page version.
|
||||
//
|
||||
// Each RO replica call this method continuously as it applies more WAL.
|
||||
// It identifies its lease through an opaque "lease_id" across these requests.
|
||||
// The response contains the lease expiration time.
|
||||
// Status `FailedPrecondition` is returned if the lease cannot be granted.
|
||||
rpc LeaseStandbyHorizon(LeaseStandbyHorizonRequest) returns (LeaseStandbyHorizonResponse);
|
||||
}
|
||||
|
||||
// The LSN a request should read at.
|
||||
@@ -272,3 +284,16 @@ message LeaseLsnResponse {
|
||||
// The lease expiration time.
|
||||
google.protobuf.Timestamp expires = 1;
|
||||
}
|
||||
|
||||
// Request for LeaseStandbyHorizon rpc.
|
||||
// The lease_id identifies the lease in subsequent requests.
|
||||
// The lsn must be monotonic; the request will fail if it is not.
|
||||
message LeaseStandbyHorizonRequest {
|
||||
string lease_id = 1;
|
||||
uint64 lsn = 2;
|
||||
}
|
||||
|
||||
// Response for the success case of LeaseStandbyHorizon rpc.
|
||||
message LeaseStandbyHorizonResponse {
|
||||
google.protobuf.Timestamp expiration = 1;
|
||||
}
|
||||
|
||||
@@ -143,6 +143,12 @@ impl Client {
|
||||
let resp = self.inner.lease_lsn(req).await?.into_inner();
|
||||
Ok(resp.try_into()?)
|
||||
}
|
||||
|
||||
pub async fn lease_standby_horizon(&mut self, req: LeaseStandbyHorizonRequest) -> tonic::Result<LeaseStandbyHorizonResponse> {
|
||||
let req = proto::LeaseStandbyHorizonRequest::from(req);
|
||||
let resp = self.inner.lease_standby_horizon(req).await?.into_inner();
|
||||
Ok(resp.try_into()?)
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds authentication metadata to gRPC requests.
|
||||
|
||||
@@ -755,3 +755,64 @@ impl From<LeaseLsnResponse> for proto::LeaseLsnResponse {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LeaseStandbyHorizonRequest {
|
||||
pub lease_id: String,
|
||||
pub lsn: Lsn,
|
||||
}
|
||||
|
||||
impl TryFrom<proto::LeaseStandbyHorizonRequest> for LeaseStandbyHorizonRequest {
|
||||
type Error = ProtocolError;
|
||||
|
||||
fn try_from(pb: proto::LeaseStandbyHorizonRequest) -> Result<Self, Self::Error> {
|
||||
if pb.lsn == 0 {
|
||||
return Err(ProtocolError::Missing("lsn"));
|
||||
}
|
||||
if pb.lease_id.len() == 0 {
|
||||
return Err(ProtocolError::Invalid("lease_id", pb.lease_id));
|
||||
}
|
||||
Ok(Self {
|
||||
lease_id: pb.lease_id,
|
||||
lsn: Lsn(pb.lsn),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LeaseStandbyHorizonRequest> for proto::LeaseStandbyHorizonRequest {
|
||||
fn from(request: LeaseStandbyHorizonRequest) -> Self {
|
||||
Self {
|
||||
lease_id: request.lease_id,
|
||||
lsn: request.lsn.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Lease expiration time. If the lease could not be granted because the LSN has already been
|
||||
/// garbage collected, a FailedPrecondition status will be returned instead.
|
||||
pub type LeaseStandbyHorizonResponse = SystemTime;
|
||||
|
||||
impl TryFrom<proto::LeaseStandbyHorizonResponse> for LeaseStandbyHorizonResponse {
|
||||
type Error = ProtocolError;
|
||||
|
||||
fn try_from(pb: proto::LeaseStandbyHorizonResponse) -> Result<Self, Self::Error> {
|
||||
let expiration = pb.expiration.ok_or(ProtocolError::Missing("expiration"))?;
|
||||
UNIX_EPOCH
|
||||
.checked_add(Duration::new(
|
||||
expiration.seconds as u64,
|
||||
expiration.nanos as u32,
|
||||
))
|
||||
.ok_or_else(|| ProtocolError::invalid("expiration", expiration))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LeaseStandbyHorizonResponse> for proto::LeaseStandbyHorizonResponse {
|
||||
fn from(response: LeaseStandbyHorizonResponse) -> Self {
|
||||
let expiration = response.duration_since(UNIX_EPOCH).unwrap_or_default();
|
||||
Self {
|
||||
expiration: Some(prost_types::Timestamp {
|
||||
seconds: expiration.as_secs() as i64,
|
||||
nanos: expiration.subsec_nanos() as i32,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user