LCOV - code coverage report
Current view: top level - pageserver/ctl/src - main.rs (source / functions) Coverage Total Hit
Test: 32f4a56327bc9da697706839ed4836b2a00a408f.info Lines: 11.8 % 102 12
Test Date: 2024-02-07 07:37:29 Functions: 17.3 % 52 9

            Line data    Source code
       1              : //! A helper tool to manage pageserver binary files.
       2              : //! Accepts a file as an argument, attempts to parse it with all ways possible
       3              : //! and prints its interpreted context.
       4              : //!
       5              : //! Separate, `metadata` subcommand allows to print and update pageserver's metadata file.
       6              : 
       7              : mod draw_timeline_dir;
       8              : mod index_part;
       9              : mod layer_map_analyzer;
      10              : mod layers;
      11              : 
      12              : use camino::{Utf8Path, Utf8PathBuf};
      13              : use clap::{Parser, Subcommand};
      14              : use index_part::IndexPartCmd;
      15              : use layers::LayerCmd;
      16              : use pageserver::{
      17              :     context::{DownloadBehavior, RequestContext},
      18              :     page_cache,
      19              :     task_mgr::TaskKind,
      20              :     tenant::{dump_layerfile_from_path, metadata::TimelineMetadata},
      21              :     virtual_file,
      22              : };
      23              : use postgres_ffi::ControlFileData;
      24              : use utils::{lsn::Lsn, project_git_version};
      25              : 
      26              : project_git_version!(GIT_VERSION);
      27              : 
      28            6 : #[derive(Parser)]
      29              : #[command(
      30              :     version = GIT_VERSION,
      31              :     about = "Neon Pageserver binutils",
      32              :     long_about = "Reads pageserver (and related) binary files management utility"
      33              : )]
      34              : #[command(propagate_version = true)]
      35              : struct CliOpts {
      36              :     #[command(subcommand)]
      37              :     command: Commands,
      38              : }
      39              : 
      40           12 : #[derive(Subcommand)]
      41              : enum Commands {
      42              :     Metadata(MetadataCmd),
      43              :     #[command(subcommand)]
      44              :     IndexPart(IndexPartCmd),
      45              :     PrintLayerFile(PrintLayerFileCmd),
      46              :     DrawTimeline {},
      47              :     AnalyzeLayerMap(AnalyzeLayerMapCmd),
      48              :     #[command(subcommand)]
      49              :     Layer(LayerCmd),
      50              : }
      51              : 
      52              : /// Read and update pageserver metadata file
      53            6 : #[derive(Parser)]
      54              : struct MetadataCmd {
      55              :     /// Input metadata file path
      56            0 :     metadata_path: Utf8PathBuf,
      57              :     /// Replace disk consistent Lsn
      58              :     disk_consistent_lsn: Option<Lsn>,
      59              :     /// Replace previous record Lsn
      60              :     prev_record_lsn: Option<Lsn>,
      61              :     /// Replace latest gc cuttoff
      62              :     latest_gc_cuttoff: Option<Lsn>,
      63              : }
      64              : 
      65            6 : #[derive(Parser)]
      66              : struct PrintLayerFileCmd {
      67              :     /// Pageserver data path
      68            0 :     path: Utf8PathBuf,
      69              : }
      70              : 
      71            6 : #[derive(Parser)]
      72              : struct AnalyzeLayerMapCmd {
      73              :     /// Pageserver data path
      74            0 :     path: Utf8PathBuf,
      75              :     /// Max holes
      76              :     max_holes: Option<usize>,
      77              : }
      78              : 
      79              : #[tokio::main]
      80            6 : async fn main() -> anyhow::Result<()> {
      81            6 :     let cli = CliOpts::parse();
      82            6 : 
      83            6 :     match cli.command {
      84            0 :         Commands::Layer(cmd) => {
      85            0 :             layers::main(&cmd).await?;
      86              :         }
      87            0 :         Commands::Metadata(cmd) => {
      88            0 :             handle_metadata(&cmd)?;
      89              :         }
      90            6 :         Commands::IndexPart(cmd) => {
      91            6 :             index_part::main(&cmd).await?;
      92              :         }
      93              :         Commands::DrawTimeline {} => {
      94            0 :             draw_timeline_dir::main()?;
      95              :         }
      96            0 :         Commands::AnalyzeLayerMap(cmd) => {
      97            0 :             layer_map_analyzer::main(&cmd).await?;
      98              :         }
      99            0 :         Commands::PrintLayerFile(cmd) => {
     100            0 :             if let Err(e) = read_pg_control_file(&cmd.path) {
     101            0 :                 println!(
     102            0 :                     "Failed to read input file as a pg control one: {e:#}\n\
     103            0 :                     Attempting to read it as layer file"
     104            0 :                 );
     105            0 :                 print_layerfile(&cmd.path).await?;
     106            0 :             }
     107              :         }
     108              :     };
     109            6 :     Ok(())
     110              : }
     111              : 
     112            0 : fn read_pg_control_file(control_file_path: &Utf8Path) -> anyhow::Result<()> {
     113            0 :     let control_file = ControlFileData::decode(&std::fs::read(control_file_path)?)?;
     114            0 :     println!("{control_file:?}");
     115            0 :     let control_file_initdb = Lsn(control_file.checkPoint);
     116            0 :     println!(
     117            0 :         "pg_initdb_lsn: {}, aligned: {}",
     118            0 :         control_file_initdb,
     119            0 :         control_file_initdb.align()
     120            0 :     );
     121            0 :     Ok(())
     122            0 : }
     123              : 
     124            0 : async fn print_layerfile(path: &Utf8Path) -> anyhow::Result<()> {
     125            0 :     // Basic initialization of things that don't change after startup
     126            0 :     virtual_file::init(10, virtual_file::IoEngineKind::StdFs);
     127            0 :     page_cache::init(100);
     128            0 :     let ctx = RequestContext::new(TaskKind::DebugTool, DownloadBehavior::Error);
     129            0 :     dump_layerfile_from_path(path, true, &ctx).await
     130            0 : }
     131              : 
     132            0 : fn handle_metadata(
     133            0 :     MetadataCmd {
     134            0 :         metadata_path: path,
     135            0 :         disk_consistent_lsn,
     136            0 :         prev_record_lsn,
     137            0 :         latest_gc_cuttoff,
     138            0 :     }: &MetadataCmd,
     139            0 : ) -> Result<(), anyhow::Error> {
     140            0 :     let metadata_bytes = std::fs::read(path)?;
     141            0 :     let mut meta = TimelineMetadata::from_bytes(&metadata_bytes)?;
     142            0 :     println!("Current metadata:\n{meta:?}");
     143            0 :     let mut update_meta = false;
     144            0 :     if let Some(disk_consistent_lsn) = disk_consistent_lsn {
     145            0 :         meta = TimelineMetadata::new(
     146            0 :             *disk_consistent_lsn,
     147            0 :             meta.prev_record_lsn(),
     148            0 :             meta.ancestor_timeline(),
     149            0 :             meta.ancestor_lsn(),
     150            0 :             meta.latest_gc_cutoff_lsn(),
     151            0 :             meta.initdb_lsn(),
     152            0 :             meta.pg_version(),
     153            0 :         );
     154            0 :         update_meta = true;
     155            0 :     }
     156            0 :     if let Some(prev_record_lsn) = prev_record_lsn {
     157            0 :         meta = TimelineMetadata::new(
     158            0 :             meta.disk_consistent_lsn(),
     159            0 :             Some(*prev_record_lsn),
     160            0 :             meta.ancestor_timeline(),
     161            0 :             meta.ancestor_lsn(),
     162            0 :             meta.latest_gc_cutoff_lsn(),
     163            0 :             meta.initdb_lsn(),
     164            0 :             meta.pg_version(),
     165            0 :         );
     166            0 :         update_meta = true;
     167            0 :     }
     168            0 :     if let Some(latest_gc_cuttoff) = latest_gc_cuttoff {
     169            0 :         meta = TimelineMetadata::new(
     170            0 :             meta.disk_consistent_lsn(),
     171            0 :             meta.prev_record_lsn(),
     172            0 :             meta.ancestor_timeline(),
     173            0 :             meta.ancestor_lsn(),
     174            0 :             *latest_gc_cuttoff,
     175            0 :             meta.initdb_lsn(),
     176            0 :             meta.pg_version(),
     177            0 :         );
     178            0 :         update_meta = true;
     179            0 :     }
     180              : 
     181            0 :     if update_meta {
     182            0 :         let metadata_bytes = meta.to_bytes()?;
     183            0 :         std::fs::write(path, metadata_bytes)?;
     184            0 :     }
     185              : 
     186            0 :     Ok(())
     187            0 : }
        

Generated by: LCOV version 2.1-beta