mirror of
https://github.com/neondatabase/neon.git
synced 2026-01-07 13:32:57 +00:00
storcon: register Pageserver gRPC address (#12268)
## Problem Pageservers now expose a gRPC API on a separate address and port. This must be registered with the storage controller such that it can be plumbed through to the compute via cplane. Touches #11926. ## Summary of changes This patch registers the gRPC address and port with the storage controller: * Add gRPC address to `nodes` database table and `NodePersistence`, with a Diesel migration. * Add gRPC address in `NodeMetadata`, `NodeRegisterRequest`, `NodeDescribeResponse`, and `TenantLocateResponseShard`. * Add gRPC address flags to `storcon_cli node-register`. These changes are backwards-compatible, since all structs will ignore unknown fields during deserialization.
This commit is contained in:
@@ -37,6 +37,8 @@ pub(crate) struct Node {
|
||||
|
||||
listen_pg_addr: String,
|
||||
listen_pg_port: u16,
|
||||
listen_grpc_addr: Option<String>,
|
||||
listen_grpc_port: Option<u16>,
|
||||
|
||||
availability_zone_id: AvailabilityZone,
|
||||
|
||||
@@ -100,8 +102,8 @@ impl Node {
|
||||
self.id == register_req.node_id
|
||||
&& self.listen_http_addr == register_req.listen_http_addr
|
||||
&& self.listen_http_port == register_req.listen_http_port
|
||||
// Note: listen_https_port may change. See [`Self::need_update`] for mode details.
|
||||
// && self.listen_https_port == register_req.listen_https_port
|
||||
// Note: HTTPS and gRPC addresses may change, to allow for migrations. See
|
||||
// [`Self::need_update`] for more details.
|
||||
&& self.listen_pg_addr == register_req.listen_pg_addr
|
||||
&& self.listen_pg_port == register_req.listen_pg_port
|
||||
&& self.availability_zone_id == register_req.availability_zone_id
|
||||
@@ -109,9 +111,10 @@ impl Node {
|
||||
|
||||
// Do we need to update an existing record in DB on this registration request?
|
||||
pub(crate) fn need_update(&self, register_req: &NodeRegisterRequest) -> bool {
|
||||
// listen_https_port is checked here because it may change during migration to https.
|
||||
// After migration, this check may be moved to registration_match.
|
||||
// These are checked here, since they may change before we're fully migrated.
|
||||
self.listen_https_port != register_req.listen_https_port
|
||||
|| self.listen_grpc_addr != register_req.listen_grpc_addr
|
||||
|| self.listen_grpc_port != register_req.listen_grpc_port
|
||||
}
|
||||
|
||||
/// For a shard located on this node, populate a response object
|
||||
@@ -125,6 +128,8 @@ impl Node {
|
||||
listen_https_port: self.listen_https_port,
|
||||
listen_pg_addr: self.listen_pg_addr.clone(),
|
||||
listen_pg_port: self.listen_pg_port,
|
||||
listen_grpc_addr: self.listen_grpc_addr.clone(),
|
||||
listen_grpc_port: self.listen_grpc_port,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -211,6 +216,8 @@ impl Node {
|
||||
listen_https_port: Option<u16>,
|
||||
listen_pg_addr: String,
|
||||
listen_pg_port: u16,
|
||||
listen_grpc_addr: Option<String>,
|
||||
listen_grpc_port: Option<u16>,
|
||||
availability_zone_id: AvailabilityZone,
|
||||
use_https: bool,
|
||||
) -> anyhow::Result<Self> {
|
||||
@@ -221,6 +228,10 @@ impl Node {
|
||||
);
|
||||
}
|
||||
|
||||
if listen_grpc_addr.is_some() != listen_grpc_port.is_some() {
|
||||
anyhow::bail!("cannot create node {id}: must specify both gRPC address and port");
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
id,
|
||||
listen_http_addr,
|
||||
@@ -228,6 +239,8 @@ impl Node {
|
||||
listen_https_port,
|
||||
listen_pg_addr,
|
||||
listen_pg_port,
|
||||
listen_grpc_addr,
|
||||
listen_grpc_port,
|
||||
scheduling: NodeSchedulingPolicy::Active,
|
||||
lifecycle: NodeLifecycle::Active,
|
||||
availability: NodeAvailability::Offline,
|
||||
@@ -247,6 +260,8 @@ impl Node {
|
||||
listen_https_port: self.listen_https_port.map(|x| x as i32),
|
||||
listen_pg_addr: self.listen_pg_addr.clone(),
|
||||
listen_pg_port: self.listen_pg_port as i32,
|
||||
listen_grpc_addr: self.listen_grpc_addr.clone(),
|
||||
listen_grpc_port: self.listen_grpc_port.map(|port| port as i32),
|
||||
availability_zone_id: self.availability_zone_id.0.clone(),
|
||||
}
|
||||
}
|
||||
@@ -260,6 +275,13 @@ impl Node {
|
||||
);
|
||||
}
|
||||
|
||||
if np.listen_grpc_addr.is_some() != np.listen_grpc_port.is_some() {
|
||||
anyhow::bail!(
|
||||
"can't load node {}: must specify both gRPC address and port",
|
||||
np.node_id
|
||||
);
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
id: NodeId(np.node_id as u64),
|
||||
// At startup we consider a node offline until proven otherwise.
|
||||
@@ -272,6 +294,8 @@ impl Node {
|
||||
listen_https_port: np.listen_https_port.map(|x| x as u16),
|
||||
listen_pg_addr: np.listen_pg_addr,
|
||||
listen_pg_port: np.listen_pg_port as u16,
|
||||
listen_grpc_addr: np.listen_grpc_addr,
|
||||
listen_grpc_port: np.listen_grpc_port.map(|port| port as u16),
|
||||
availability_zone_id: AvailabilityZone(np.availability_zone_id),
|
||||
use_https,
|
||||
cancel: CancellationToken::new(),
|
||||
@@ -361,6 +385,8 @@ impl Node {
|
||||
listen_https_port: self.listen_https_port,
|
||||
listen_pg_addr: self.listen_pg_addr.clone(),
|
||||
listen_pg_port: self.listen_pg_port,
|
||||
listen_grpc_addr: self.listen_grpc_addr.clone(),
|
||||
listen_grpc_port: self.listen_grpc_port,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2125,6 +2125,8 @@ pub(crate) struct NodePersistence {
|
||||
pub(crate) availability_zone_id: String,
|
||||
pub(crate) listen_https_port: Option<i32>,
|
||||
pub(crate) lifecycle: String,
|
||||
pub(crate) listen_grpc_addr: Option<String>,
|
||||
pub(crate) listen_grpc_port: Option<i32>,
|
||||
}
|
||||
|
||||
/// Tenant metadata health status that are stored durably.
|
||||
|
||||
@@ -945,6 +945,8 @@ pub(crate) mod test_utils {
|
||||
None,
|
||||
format!("pghost-{i}"),
|
||||
5432 + i as u16,
|
||||
Some(format!("grpchost-{i}")),
|
||||
Some(51051 + i as u16),
|
||||
az_iter
|
||||
.next()
|
||||
.cloned()
|
||||
|
||||
@@ -34,6 +34,8 @@ diesel::table! {
|
||||
availability_zone_id -> Varchar,
|
||||
listen_https_port -> Nullable<Int4>,
|
||||
lifecycle -> Varchar,
|
||||
listen_grpc_addr -> Nullable<Varchar>,
|
||||
listen_grpc_port -> Nullable<Int4>,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1683,6 +1683,8 @@ impl Service {
|
||||
None,
|
||||
"".to_string(),
|
||||
123,
|
||||
None,
|
||||
None,
|
||||
AvailabilityZone("test_az".to_string()),
|
||||
false,
|
||||
)
|
||||
@@ -7254,6 +7256,12 @@ impl Service {
|
||||
));
|
||||
}
|
||||
|
||||
if register_req.listen_grpc_addr.is_some() != register_req.listen_grpc_port.is_some() {
|
||||
return Err(ApiError::BadRequest(anyhow::anyhow!(
|
||||
"must specify both gRPC address and port"
|
||||
)));
|
||||
}
|
||||
|
||||
// Ordering: we must persist the new node _before_ adding it to in-memory state.
|
||||
// This ensures that before we use it for anything or expose it via any external
|
||||
// API, it is guaranteed to be available after a restart.
|
||||
@@ -7264,6 +7272,8 @@ impl Service {
|
||||
register_req.listen_https_port,
|
||||
register_req.listen_pg_addr,
|
||||
register_req.listen_pg_port,
|
||||
register_req.listen_grpc_addr,
|
||||
register_req.listen_grpc_port,
|
||||
register_req.availability_zone_id.clone(),
|
||||
self.config.use_https_pageserver_api,
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user