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