From f3096d85e0c95239c77130e13b56cd12cb5c95d0 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Wed, 25 Jan 2023 16:30:55 +0200 Subject: [PATCH] Print in-memory layers info --- libs/pageserver_api/src/models.rs | 38 ++++++++++++++++++- pageserver/src/http/routes.rs | 23 +++++++++++ pageserver/src/tenant/layer_map.rs | 25 +++++++++++- .../tenant/storage_layer/inmemory_layer.rs | 12 +++++- pageserver/src/tenant/timeline.rs | 8 +++- 5 files changed, 101 insertions(+), 5 deletions(-) diff --git a/libs/pageserver_api/src/models.rs b/libs/pageserver_api/src/models.rs index 0d7aa2db55..e1c4216023 100644 --- a/libs/pageserver_api/src/models.rs +++ b/libs/pageserver_api/src/models.rs @@ -1,4 +1,7 @@ -use std::num::{NonZeroU64, NonZeroUsize}; +use std::{ + collections::BTreeSet, + num::{NonZeroU64, NonZeroUsize}, +}; use byteorder::{BigEndian, ReadBytesExt}; use serde::{Deserialize, Serialize}; @@ -227,6 +230,39 @@ pub struct TimelineInfo { pub state: TimelineState, } +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Serialize, Deserialize)] +pub struct LayerMapInfo { + pub in_memory_layers: BTreeSet, + pub historic_layers: BTreeSet, +} + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone, Serialize, Deserialize)] +#[serde(tag = "kind")] +pub enum InMemoryLayerInfo { + Open { lsn_start: Lsn }, + Frozen { lsn_start: Lsn, lsn_end: Lsn }, +} + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Serialize, Deserialize)] +#[serde(tag = "kind")] +pub enum HistoricLayerInfo { + Delta { + layer_file_name: String, + key_start: String, + key_end: String, + lsn_start: Lsn, + lsn_end: Lsn, + remote: bool, + }, + Image { + layer_file_name: String, + key_start: String, + key_end: String, + lsn_start: Lsn, + remote: bool, + }, +} + #[derive(Debug, Serialize, Deserialize)] pub struct DownloadRemoteLayersTaskSpawnRequest { pub max_concurrent_downloads: NonZeroUsize, diff --git a/pageserver/src/http/routes.rs b/pageserver/src/http/routes.rs index 750a242361..e3b8b3305a 100644 --- a/pageserver/src/http/routes.rs +++ b/pageserver/src/http/routes.rs @@ -556,6 +556,25 @@ async fn tenant_size_handler(request: Request) -> Result, A ) } +async fn timeline_layer_map_info_handler( + request: Request, +) -> Result, ApiError> { + let tenant_id: TenantId = parse_request_param(&request, "tenant_id")?; + let timeline_id: TimelineId = parse_request_param(&request, "timeline_id")?; + check_permission(&request, Some(tenant_id))?; + + let tenant = mgr::get_tenant(tenant_id, true) + .await + .map_err(ApiError::NotFound)?; + let timeline = tenant + .get_timeline(timeline_id, true) + .map_err(ApiError::NotFound)?; + + let layer_map_info = timeline.layer_map_info(); + + json_response(StatusCode::OK, layer_map_info) +} + // Helper function to standardize the error messages we produce on bad durations // // Intended to be used with anyhow's `with_context`, e.g.: @@ -986,5 +1005,9 @@ pub fn make_router( "/v1/tenant/:tenant_id/timeline/:timeline_id", timeline_delete_handler, ) + .get( + "/v1/tenant/:tenant_id/timeline/:timeline_id/layer", + timeline_layer_map_info_handler, + ) .any(handler_404)) } diff --git a/pageserver/src/tenant/layer_map.rs b/pageserver/src/tenant/layer_map.rs index ed1a32c8fd..de42fcec0b 100644 --- a/pageserver/src/tenant/layer_map.rs +++ b/pageserver/src/tenant/layer_map.rs @@ -52,12 +52,13 @@ use crate::repository::Key; use crate::tenant::storage_layer::InMemoryLayer; use crate::tenant::storage_layer::Layer; use anyhow::Result; -use std::collections::VecDeque; +use std::collections::{BTreeSet, VecDeque}; use std::ops::Range; use std::sync::Arc; use utils::lsn::Lsn; use historic_layer_coverage::BufferedHistoricLayerCoverage; +use pageserver_api::models::LayerMapInfo; use super::storage_layer::range_eq; @@ -674,4 +675,26 @@ where println!("End dump LayerMap"); Ok(()) } + + // TODO kb comments + swagger schema + pub fn info(&self) -> LayerMapInfo { + let mut in_memory_layers = BTreeSet::new(); + if let Some(open_layer) = &self.open_layer { + in_memory_layers.insert(open_layer.info()); + } + for frozen_layer in &self.frozen_layers { + in_memory_layers.insert(frozen_layer.info()); + } + + let mut historic_layers = BTreeSet::new(); + for historic_layer in self.iter_historic_layers() { + // TODO kb what to use here for `historic_layer: Arc` ? + // historic_layers.insert(historic_layer.info()); + } + + LayerMapInfo { + in_memory_layers, + historic_layers, + } + } } diff --git a/pageserver/src/tenant/storage_layer/inmemory_layer.rs b/pageserver/src/tenant/storage_layer/inmemory_layer.rs index 93356a9d8c..c2a04d8ec9 100644 --- a/pageserver/src/tenant/storage_layer/inmemory_layer.rs +++ b/pageserver/src/tenant/storage_layer/inmemory_layer.rs @@ -12,6 +12,7 @@ use crate::tenant::ephemeral_file::EphemeralFile; use crate::tenant::storage_layer::{ValueReconstructResult, ValueReconstructState}; use crate::walrecord; use anyhow::{ensure, Result}; +use pageserver_api::models::InMemoryLayerInfo; use std::cell::RefCell; use std::collections::HashMap; use tracing::*; @@ -79,6 +80,16 @@ impl InMemoryLayer { pub fn get_timeline_id(&self) -> TimelineId { self.timeline_id } + + pub fn info(&self) -> InMemoryLayerInfo { + let lsn_start = self.start_lsn; + let lsn_end = self.inner.read().unwrap().end_lsn; + + match lsn_end { + Some(lsn_end) => InMemoryLayerInfo::Frozen { lsn_start, lsn_end }, + None => InMemoryLayerInfo::Open { lsn_start }, + } + } } impl Layer for InMemoryLayer { @@ -108,7 +119,6 @@ impl Layer for InMemoryLayer { let end_lsn = inner.end_lsn.unwrap_or(Lsn(u64::MAX)); format!("inmem-{:016X}-{:016X}", self.start_lsn.0, end_lsn.0) } - /// debugging function to print out the contents of the layer fn dump(&self, verbose: bool) -> Result<()> { let inner = self.inner.read().unwrap(); diff --git a/pageserver/src/tenant/timeline.rs b/pageserver/src/tenant/timeline.rs index ad68770404..e96daf1bb8 100644 --- a/pageserver/src/tenant/timeline.rs +++ b/pageserver/src/tenant/timeline.rs @@ -10,7 +10,7 @@ use itertools::Itertools; use once_cell::sync::OnceCell; use pageserver_api::models::{ DownloadRemoteLayersTaskInfo, DownloadRemoteLayersTaskSpawnRequest, - DownloadRemoteLayersTaskState, TimelineState, + DownloadRemoteLayersTaskState, LayerMapInfo, TimelineState, }; use tokio::sync::{oneshot, watch, Semaphore, TryAcquireError}; use tokio_util::sync::CancellationToken; @@ -90,7 +90,7 @@ pub struct Timeline { pub pg_version: u32, - pub layers: RwLock>, + pub(super) layers: RwLock>, last_freeze_at: AtomicLsn, // Atomic would be more appropriate here. @@ -818,6 +818,10 @@ impl Timeline { pub fn subscribe_for_state_updates(&self) -> watch::Receiver { self.state.subscribe() } + + pub fn layer_map_info(&self) -> LayerMapInfo { + self.layers.read().unwrap().info() + } } // Private functions