diff --git a/pageserver/page_api/proto/page_service.proto b/pageserver/page_api/proto/page_service.proto index f6acb3eeeb..44976084bf 100644 --- a/pageserver/page_api/proto/page_service.proto +++ b/pageserver/page_api/proto/page_service.proto @@ -54,9 +54,9 @@ service PageService { // RPCs use regular unary requests, since they are not as frequent and // performance-critical, and this simplifies implementation. // - // NB: a status response (e.g. errors) will terminate the stream. The stream - // may be shared by e.g. multiple Postgres backends, so we should avoid this. - // Most errors are therefore sent as GetPageResponse.status instead. + // NB: a gRPC status response (e.g. errors) will terminate the stream. The + // stream may be shared by multiple Postgres backends, so we avoid this by + // sending them as GetPageResponse.status_code instead. rpc GetPages (stream GetPageRequest) returns (stream GetPageResponse); // Returns the size of a relation, as # of blocks. @@ -159,8 +159,8 @@ message GetPageRequest { // A GetPageRequest class. Primarily intended for observability, but may also be // used for prioritization in the future. enum GetPageClass { - // Unknown class. For forwards compatibility: used when the client sends a - // class that the server doesn't know about. + // Unknown class. For backwards compatibility: used when an older client version sends a class + // that a newer server version has removed. GET_PAGE_CLASS_UNKNOWN = 0; // A normal request. This is the default. GET_PAGE_CLASS_NORMAL = 1; @@ -180,31 +180,37 @@ message GetPageResponse { // The original request's ID. uint64 request_id = 1; // The response status code. - GetPageStatus status = 2; + GetPageStatusCode status_code = 2; // A string describing the status, if any. string reason = 3; - // The 8KB page images, in the same order as the request. Empty if status != OK. + // The 8KB page images, in the same order as the request. Empty if status_code != OK. repeated bytes page_image = 4; } -// A GetPageResponse status code. Since we use a bidirectional stream, we don't -// want to send errors as gRPC statuses, since this would terminate the stream. -enum GetPageStatus { - // Unknown status. For forwards compatibility: used when the server sends a - // status code that the client doesn't know about. - GET_PAGE_STATUS_UNKNOWN = 0; +// A GetPageResponse status code. +// +// These are effectively equivalent to gRPC statuses. However, we use a bidirectional stream +// (potentially shared by many backends), and a gRPC status response would terminate the stream so +// we send GetPageResponse messages with these codes instead. +enum GetPageStatusCode { + // Unknown status. For forwards compatibility: used when an older client version receives a new + // status code from a newer server version. + GET_PAGE_STATUS_CODE_UNKNOWN = 0; // The request was successful. - GET_PAGE_STATUS_OK = 1; + GET_PAGE_STATUS_CODE_OK = 1; // The page did not exist. The tenant/timeline/shard has already been // validated during stream setup. - GET_PAGE_STATUS_NOT_FOUND = 2; + GET_PAGE_STATUS_CODE_NOT_FOUND = 2; // The request was invalid. - GET_PAGE_STATUS_INVALID = 3; + GET_PAGE_STATUS_CODE_INVALID_REQUEST = 3; + // The request failed due to an internal server error. + GET_PAGE_STATUS_CODE_INTERNAL_ERROR = 4; // The tenant is rate limited. Slow down and retry later. - GET_PAGE_STATUS_SLOW_DOWN = 4; - // TODO: consider adding a GET_PAGE_STATUS_LAYER_DOWNLOAD in the case of a - // layer download. This could free up the server task to process other - // requests while the layer download is in progress. + GET_PAGE_STATUS_CODE_SLOW_DOWN = 5; + // NB: shutdown errors are emitted as a gRPC Unavailable status. + // + // TODO: consider adding a GET_PAGE_STATUS_CODE_LAYER_DOWNLOAD in the case of a layer download. + // This could free up the server task to process other requests while the download is in progress. } // Fetches the size of a relation at a given LSN, as # of blocks. Only valid on diff --git a/pageserver/page_api/src/model.rs b/pageserver/page_api/src/model.rs index a83d0a5947..7ab97a994e 100644 --- a/pageserver/page_api/src/model.rs +++ b/pageserver/page_api/src/model.rs @@ -35,6 +35,12 @@ impl ProtocolError { } } +impl From for tonic::Status { + fn from(err: ProtocolError) -> Self { + tonic::Status::invalid_argument(format!("{err}")) + } +} + /// The LSN a request should read at. #[derive(Clone, Copy, Debug)] pub struct ReadLsn { @@ -328,7 +334,7 @@ pub type RequestID = u64; /// A GetPage request class. #[derive(Clone, Copy, Debug)] pub enum GetPageClass { - /// Unknown status. For backwards compatibility: used when an older client version sends a class + /// Unknown class. For backwards compatibility: used when an older client version sends a class /// that a newer server version has removed. Unknown, /// A normal request. This is the default. @@ -386,7 +392,7 @@ pub struct GetPageResponse { /// The original request's ID. pub request_id: RequestID, /// The response status code. - pub status: GetPageStatus, + pub status_code: GetPageStatusCode, /// A string describing the status, if any. pub reason: Option, /// The 8KB page images, in the same order as the request. Empty if status != OK. @@ -397,7 +403,7 @@ impl From for GetPageResponse { fn from(pb: proto::GetPageResponse) -> Self { Self { request_id: pb.request_id, - status: pb.status.into(), + status_code: pb.status_code.into(), reason: Some(pb.reason).filter(|r| !r.is_empty()), page_images: pb.page_image.into(), } @@ -408,16 +414,20 @@ impl From for proto::GetPageResponse { fn from(response: GetPageResponse) -> Self { Self { request_id: response.request_id, - status: response.status.into(), + status_code: response.status_code.into(), reason: response.reason.unwrap_or_default(), page_image: response.page_images.into_vec(), } } } -/// A GetPage response status. +/// A GetPage response status code. +/// +/// These are effectively equivalent to gRPC statuses. However, we use a bidirectional stream +/// (potentially shared by many backends), and a gRPC status response would terminate the stream so +/// we send GetPageResponse messages with these codes instead. #[derive(Clone, Copy, Debug)] -pub enum GetPageStatus { +pub enum GetPageStatusCode { /// Unknown status. For forwards compatibility: used when an older client version receives a new /// status code from a newer server version. Unknown, @@ -427,46 +437,50 @@ pub enum GetPageStatus { /// setup. NotFound, /// The request was invalid. - Invalid, + InvalidRequest, + /// The request failed due to an internal server error. + InternalError, /// The tenant is rate limited. Slow down and retry later. SlowDown, } -impl From for GetPageStatus { - fn from(pb: proto::GetPageStatus) -> Self { +impl From for GetPageStatusCode { + fn from(pb: proto::GetPageStatusCode) -> Self { match pb { - proto::GetPageStatus::Unknown => Self::Unknown, - proto::GetPageStatus::Ok => Self::Ok, - proto::GetPageStatus::NotFound => Self::NotFound, - proto::GetPageStatus::Invalid => Self::Invalid, - proto::GetPageStatus::SlowDown => Self::SlowDown, + proto::GetPageStatusCode::Unknown => Self::Unknown, + proto::GetPageStatusCode::Ok => Self::Ok, + proto::GetPageStatusCode::NotFound => Self::NotFound, + proto::GetPageStatusCode::InvalidRequest => Self::InvalidRequest, + proto::GetPageStatusCode::InternalError => Self::InternalError, + proto::GetPageStatusCode::SlowDown => Self::SlowDown, } } } -impl From for GetPageStatus { - fn from(status: i32) -> Self { - proto::GetPageStatus::try_from(status) - .unwrap_or(proto::GetPageStatus::Unknown) +impl From for GetPageStatusCode { + fn from(status_code: i32) -> Self { + proto::GetPageStatusCode::try_from(status_code) + .unwrap_or(proto::GetPageStatusCode::Unknown) .into() } } -impl From for proto::GetPageStatus { - fn from(status: GetPageStatus) -> Self { - match status { - GetPageStatus::Unknown => Self::Unknown, - GetPageStatus::Ok => Self::Ok, - GetPageStatus::NotFound => Self::NotFound, - GetPageStatus::Invalid => Self::Invalid, - GetPageStatus::SlowDown => Self::SlowDown, +impl From for proto::GetPageStatusCode { + fn from(status_code: GetPageStatusCode) -> Self { + match status_code { + GetPageStatusCode::Unknown => Self::Unknown, + GetPageStatusCode::Ok => Self::Ok, + GetPageStatusCode::NotFound => Self::NotFound, + GetPageStatusCode::InvalidRequest => Self::InvalidRequest, + GetPageStatusCode::InternalError => Self::InternalError, + GetPageStatusCode::SlowDown => Self::SlowDown, } } } -impl From for i32 { - fn from(status: GetPageStatus) -> Self { - proto::GetPageStatus::from(status).into() +impl From for i32 { + fn from(status_code: GetPageStatusCode) -> Self { + proto::GetPageStatusCode::from(status_code).into() } }