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 : }
|