Line data Source code
1 : #![recursion_limit = "300"]
2 : #![deny(clippy::undocumented_unsafe_blocks)]
3 :
4 : mod auth;
5 : pub mod basebackup;
6 : pub mod config;
7 : pub mod consumption_metrics;
8 : pub mod context;
9 : pub mod control_plane_client;
10 : pub mod deletion_queue;
11 : pub mod disk_usage_eviction_task;
12 : pub mod http;
13 : pub mod import_datadir;
14 : pub use pageserver_api::keyspace;
15 : pub mod aux_file;
16 : pub mod metrics;
17 : pub mod page_cache;
18 : pub mod page_service;
19 : pub mod pgdatadir_mapping;
20 : pub mod repository;
21 : pub mod span;
22 : pub(crate) mod statvfs;
23 : pub mod task_mgr;
24 : pub mod tenant;
25 : pub mod trace;
26 : pub mod utilization;
27 : pub mod virtual_file;
28 : pub mod walingest;
29 : pub mod walrecord;
30 : pub mod walredo;
31 :
32 : use crate::task_mgr::TaskKind;
33 : use camino::Utf8Path;
34 : use deletion_queue::DeletionQueue;
35 : use tenant::mgr::TenantManager;
36 : use tracing::info;
37 :
38 : /// Current storage format version
39 : ///
40 : /// This is embedded in the header of all the layer files.
41 : /// If you make any backwards-incompatible changes to the storage
42 : /// format, bump this!
43 : /// Note that TimelineMetadata uses its own version number to track
44 : /// backwards-compatible changes to the metadata format.
45 : pub const STORAGE_FORMAT_VERSION: u16 = 3;
46 :
47 : pub const DEFAULT_PG_VERSION: u32 = 15;
48 :
49 : // Magic constants used to identify different kinds of files
50 : pub const IMAGE_FILE_MAGIC: u16 = 0x5A60;
51 : pub const DELTA_FILE_MAGIC: u16 = 0x5A61;
52 :
53 : static ZERO_PAGE: bytes::Bytes = bytes::Bytes::from_static(&[0u8; 8192]);
54 :
55 : pub use crate::metrics::preinitialize_metrics;
56 :
57 0 : #[tracing::instrument(skip_all, fields(%exit_code))]
58 : pub async fn shutdown_pageserver(
59 : tenant_manager: &TenantManager,
60 : mut deletion_queue: DeletionQueue,
61 : exit_code: i32,
62 : ) {
63 : use std::time::Duration;
64 : // Shut down the libpq endpoint task. This prevents new connections from
65 : // being accepted.
66 : timed(
67 : task_mgr::shutdown_tasks(Some(TaskKind::LibpqEndpointListener), None, None),
68 : "shutdown LibpqEndpointListener",
69 : Duration::from_secs(1),
70 : )
71 : .await;
72 :
73 : // Shut down all the tenants. This flushes everything to disk and kills
74 : // the checkpoint and GC tasks.
75 : timed(
76 : tenant_manager.shutdown(),
77 : "shutdown all tenants",
78 : Duration::from_secs(5),
79 : )
80 : .await;
81 :
82 : // Shut down any page service tasks: any in-progress work for particular timelines or tenants
83 : // should already have been canclled via mgr::shutdown_all_tenants
84 : timed(
85 : task_mgr::shutdown_tasks(Some(TaskKind::PageRequestHandler), None, None),
86 : "shutdown PageRequestHandlers",
87 : Duration::from_secs(1),
88 : )
89 : .await;
90 :
91 : // Best effort to persist any outstanding deletions, to avoid leaking objects
92 : deletion_queue.shutdown(Duration::from_secs(5)).await;
93 :
94 : // Shut down the HTTP endpoint last, so that you can still check the server's
95 : // status while it's shutting down.
96 : // FIXME: We should probably stop accepting commands like attach/detach earlier.
97 : timed(
98 : task_mgr::shutdown_tasks(Some(TaskKind::HttpEndpointListener), None, None),
99 : "shutdown http",
100 : Duration::from_secs(1),
101 : )
102 : .await;
103 :
104 : // There should be nothing left, but let's be sure
105 : timed(
106 : task_mgr::shutdown_tasks(None, None, None),
107 : "shutdown leftovers",
108 : Duration::from_secs(1),
109 : )
110 : .await;
111 : info!("Shut down successfully completed");
112 : std::process::exit(exit_code);
113 : }
114 :
115 : /// Per-tenant configuration file.
116 : /// Full path: `tenants/<tenant_id>/config-v1`.
117 : pub(crate) const TENANT_LOCATION_CONFIG_NAME: &str = "config-v1";
118 :
119 : /// Per-tenant copy of their remote heatmap, downloaded into the local
120 : /// tenant path while in secondary mode.
121 : pub(crate) const TENANT_HEATMAP_BASENAME: &str = "heatmap-v1.json";
122 :
123 : /// A suffix used for various temporary files. Any temporary files found in the
124 : /// data directory at pageserver startup can be automatically removed.
125 : pub(crate) const TEMP_FILE_SUFFIX: &str = "___temp";
126 :
127 : /// A marker file to mark that a timeline directory was not fully initialized.
128 : /// If a timeline directory with this marker is encountered at pageserver startup,
129 : /// the timeline directory and the marker file are both removed.
130 : /// Full path: `tenants/<tenant_id>/timelines/<timeline_id>___uninit`.
131 : pub(crate) const TIMELINE_UNINIT_MARK_SUFFIX: &str = "___uninit";
132 :
133 : pub(crate) const TIMELINE_DELETE_MARK_SUFFIX: &str = "___delete";
134 :
135 8 : pub fn is_temporary(path: &Utf8Path) -> bool {
136 8 : match path.file_name() {
137 8 : Some(name) => name.ends_with(TEMP_FILE_SUFFIX),
138 0 : None => false,
139 : }
140 8 : }
141 :
142 16 : fn ends_with_suffix(path: &Utf8Path, suffix: &str) -> bool {
143 16 : match path.file_name() {
144 16 : Some(name) => name.ends_with(suffix),
145 0 : None => false,
146 : }
147 16 : }
148 :
149 : // FIXME: DO NOT ADD new query methods like this, which will have a next step of parsing timelineid
150 : // from the directory name. Instead create type "UninitMark(TimelineId)" and only parse it once
151 : // from the name.
152 :
153 8 : pub(crate) fn is_uninit_mark(path: &Utf8Path) -> bool {
154 8 : ends_with_suffix(path, TIMELINE_UNINIT_MARK_SUFFIX)
155 8 : }
156 :
157 8 : pub(crate) fn is_delete_mark(path: &Utf8Path) -> bool {
158 8 : ends_with_suffix(path, TIMELINE_DELETE_MARK_SUFFIX)
159 8 : }
160 :
161 : /// During pageserver startup, we need to order operations not to exhaust tokio worker threads by
162 : /// blocking.
163 : ///
164 : /// The instances of this value exist only during startup, otherwise `None` is provided, meaning no
165 : /// delaying is needed.
166 : #[derive(Clone)]
167 : pub struct InitializationOrder {
168 : /// Each initial tenant load task carries this until it is done loading timelines from remote storage
169 : pub initial_tenant_load_remote: Option<utils::completion::Completion>,
170 :
171 : /// Each initial tenant load task carries this until completion.
172 : pub initial_tenant_load: Option<utils::completion::Completion>,
173 :
174 : /// Barrier for when we can start any background jobs.
175 : ///
176 : /// This can be broken up later on, but right now there is just one class of a background job.
177 : pub background_jobs_can_start: utils::completion::Barrier,
178 : }
179 :
180 : /// Time the future with a warning when it exceeds a threshold.
181 4 : async fn timed<Fut: std::future::Future>(
182 4 : fut: Fut,
183 4 : name: &str,
184 4 : warn_at: std::time::Duration,
185 4 : ) -> <Fut as std::future::Future>::Output {
186 4 : let started = std::time::Instant::now();
187 4 :
188 4 : let mut fut = std::pin::pin!(fut);
189 4 :
190 4 : match tokio::time::timeout(warn_at, &mut fut).await {
191 2 : Ok(ret) => {
192 2 : tracing::info!(
193 : stage = name,
194 0 : elapsed_ms = started.elapsed().as_millis(),
195 0 : "completed"
196 : );
197 2 : ret
198 : }
199 : Err(_) => {
200 2 : tracing::info!(
201 : stage = name,
202 0 : elapsed_ms = started.elapsed().as_millis(),
203 0 : "still waiting, taking longer than expected..."
204 : );
205 :
206 2 : let ret = fut.await;
207 :
208 : // this has a global allowed_errors
209 2 : tracing::warn!(
210 : stage = name,
211 0 : elapsed_ms = started.elapsed().as_millis(),
212 0 : "completed, took longer than expected"
213 : );
214 :
215 2 : ret
216 : }
217 : }
218 4 : }
219 :
220 : #[cfg(test)]
221 : mod timed_tests {
222 : use super::timed;
223 : use std::time::Duration;
224 :
225 : #[tokio::test]
226 2 : async fn timed_completes_when_inner_future_completes() {
227 2 : // A future that completes on time should have its result returned
228 2 : let r1 = timed(
229 2 : async move {
230 2 : tokio::time::sleep(Duration::from_millis(10)).await;
231 2 : 123
232 2 : },
233 2 : "test 1",
234 2 : Duration::from_millis(50),
235 2 : )
236 2 : .await;
237 2 : assert_eq!(r1, 123);
238 2 :
239 2 : // A future that completes too slowly should also have its result returned
240 2 : let r1 = timed(
241 2 : async move {
242 6 : tokio::time::sleep(Duration::from_millis(50)).await;
243 2 : 456
244 2 : },
245 2 : "test 1",
246 2 : Duration::from_millis(10),
247 2 : )
248 4 : .await;
249 2 : assert_eq!(r1, 456);
250 2 : }
251 : }
|