Line data Source code
1 : use super::*;
2 : use std::collections::HashMap;
3 :
4 : #[test]
5 2 : fn startup_collected_timeline_metrics_before_advancing() {
6 2 : let tenant_id = TenantId::generate();
7 2 : let timeline_id = TimelineId::generate();
8 2 :
9 2 : let mut metrics = Vec::new();
10 2 : let cache = HashMap::new();
11 2 :
12 2 : let initdb_lsn = Lsn(0x10000);
13 2 : let disk_consistent_lsn = Lsn(initdb_lsn.0 * 2);
14 2 :
15 2 : let snap = TimelineSnapshot {
16 2 : loaded_at: (disk_consistent_lsn, SystemTime::now()),
17 2 : last_record_lsn: disk_consistent_lsn,
18 2 : current_exact_logical_size: Some(0x42000),
19 2 : };
20 2 :
21 2 : let now = DateTime::<Utc>::from(SystemTime::now());
22 2 :
23 2 : snap.to_metrics(tenant_id, timeline_id, now, &mut metrics, &cache);
24 2 :
25 2 : assert_eq!(
26 2 : metrics,
27 2 : &[
28 2 : MetricsKey::written_size_delta(tenant_id, timeline_id).from_until(
29 2 : snap.loaded_at.1.into(),
30 2 : now,
31 2 : 0
32 2 : ),
33 2 : MetricsKey::written_size(tenant_id, timeline_id).at(now, disk_consistent_lsn.0),
34 2 : MetricsKey::timeline_logical_size(tenant_id, timeline_id).at(now, 0x42000)
35 2 : ]
36 2 : );
37 2 : }
38 :
39 : #[test]
40 2 : fn startup_collected_timeline_metrics_second_round() {
41 2 : let tenant_id = TenantId::generate();
42 2 : let timeline_id = TimelineId::generate();
43 2 :
44 2 : let [now, before, init] = time_backwards();
45 2 :
46 2 : let now = DateTime::<Utc>::from(now);
47 2 : let before = DateTime::<Utc>::from(before);
48 2 :
49 2 : let initdb_lsn = Lsn(0x10000);
50 2 : let disk_consistent_lsn = Lsn(initdb_lsn.0 * 2);
51 2 :
52 2 : let mut metrics = Vec::new();
53 2 : let cache = HashMap::from([
54 2 : MetricsKey::written_size(tenant_id, timeline_id).at(before, disk_consistent_lsn.0)
55 2 : ]);
56 2 :
57 2 : let snap = TimelineSnapshot {
58 2 : loaded_at: (disk_consistent_lsn, init),
59 2 : last_record_lsn: disk_consistent_lsn,
60 2 : current_exact_logical_size: Some(0x42000),
61 2 : };
62 2 :
63 2 : snap.to_metrics(tenant_id, timeline_id, now, &mut metrics, &cache);
64 2 :
65 2 : assert_eq!(
66 2 : metrics,
67 2 : &[
68 2 : MetricsKey::written_size_delta(tenant_id, timeline_id).from_until(before, now, 0),
69 2 : MetricsKey::written_size(tenant_id, timeline_id).at(now, disk_consistent_lsn.0),
70 2 : MetricsKey::timeline_logical_size(tenant_id, timeline_id).at(now, 0x42000)
71 2 : ]
72 2 : );
73 2 : }
74 :
75 : #[test]
76 2 : fn startup_collected_timeline_metrics_nth_round_at_same_lsn() {
77 2 : let tenant_id = TenantId::generate();
78 2 : let timeline_id = TimelineId::generate();
79 2 :
80 2 : let [now, just_before, before, init] = time_backwards();
81 2 :
82 2 : let now = DateTime::<Utc>::from(now);
83 2 : let just_before = DateTime::<Utc>::from(just_before);
84 2 : let before = DateTime::<Utc>::from(before);
85 2 :
86 2 : let initdb_lsn = Lsn(0x10000);
87 2 : let disk_consistent_lsn = Lsn(initdb_lsn.0 * 2);
88 2 :
89 2 : let mut metrics = Vec::new();
90 2 : let cache = HashMap::from([
91 2 : // at t=before was the last time the last_record_lsn changed
92 2 : MetricsKey::written_size(tenant_id, timeline_id).at(before, disk_consistent_lsn.0),
93 2 : // end time of this event is used for the next ones
94 2 : MetricsKey::written_size_delta(tenant_id, timeline_id).from_until(before, just_before, 0),
95 2 : ]);
96 2 :
97 2 : let snap = TimelineSnapshot {
98 2 : loaded_at: (disk_consistent_lsn, init),
99 2 : last_record_lsn: disk_consistent_lsn,
100 2 : current_exact_logical_size: Some(0x42000),
101 2 : };
102 2 :
103 2 : snap.to_metrics(tenant_id, timeline_id, now, &mut metrics, &cache);
104 2 :
105 2 : assert_eq!(
106 2 : metrics,
107 2 : &[
108 2 : MetricsKey::written_size_delta(tenant_id, timeline_id).from_until(just_before, now, 0),
109 2 : MetricsKey::written_size(tenant_id, timeline_id).at(now, disk_consistent_lsn.0),
110 2 : MetricsKey::timeline_logical_size(tenant_id, timeline_id).at(now, 0x42000)
111 2 : ]
112 2 : );
113 2 : }
114 :
115 : #[test]
116 2 : fn post_restart_written_sizes_with_rolled_back_last_record_lsn() {
117 2 : // it can happen that we lose the inmemorylayer but have previously sent metrics and we
118 2 : // should never go backwards
119 2 :
120 2 : let tenant_id = TenantId::generate();
121 2 : let timeline_id = TimelineId::generate();
122 2 :
123 2 : let [later, now, at_restart] = time_backwards();
124 2 :
125 2 : // FIXME: tests would be so much easier if we did not need to juggle back and forth
126 2 : // SystemTime and DateTime::<Utc> ... Could do the conversion only at upload time?
127 2 : let now = DateTime::<Utc>::from(now);
128 2 : let later = DateTime::<Utc>::from(later);
129 2 : let before_restart = at_restart - std::time::Duration::from_secs(5 * 60);
130 2 : let way_before = before_restart - std::time::Duration::from_secs(10 * 60);
131 2 : let before_restart = DateTime::<Utc>::from(before_restart);
132 2 : let way_before = DateTime::<Utc>::from(way_before);
133 2 :
134 2 : let snap = TimelineSnapshot {
135 2 : loaded_at: (Lsn(50), at_restart),
136 2 : last_record_lsn: Lsn(50),
137 2 : current_exact_logical_size: None,
138 2 : };
139 2 :
140 2 : let mut cache = HashMap::from([
141 2 : MetricsKey::written_size(tenant_id, timeline_id).at(before_restart, 100),
142 2 : MetricsKey::written_size_delta(tenant_id, timeline_id).from_until(
143 2 : way_before,
144 2 : before_restart,
145 2 : // not taken into account, but the timestamps are important
146 2 : 999_999_999,
147 2 : ),
148 2 : ]);
149 2 :
150 2 : let mut metrics = Vec::new();
151 2 : snap.to_metrics(tenant_id, timeline_id, now, &mut metrics, &cache);
152 2 :
153 2 : assert_eq!(
154 2 : metrics,
155 2 : &[
156 2 : MetricsKey::written_size_delta(tenant_id, timeline_id).from_until(
157 2 : before_restart,
158 2 : now,
159 2 : 0
160 2 : ),
161 2 : MetricsKey::written_size(tenant_id, timeline_id).at(now, 100),
162 2 : ]
163 2 : );
164 :
165 : // now if we cache these metrics, and re-run while "still in recovery"
166 2 : cache.extend(metrics.drain(..));
167 2 :
168 2 : // "still in recovery", because our snapshot did not change
169 2 : snap.to_metrics(tenant_id, timeline_id, later, &mut metrics, &cache);
170 2 :
171 2 : assert_eq!(
172 2 : metrics,
173 2 : &[
174 2 : MetricsKey::written_size_delta(tenant_id, timeline_id).from_until(now, later, 0),
175 2 : MetricsKey::written_size(tenant_id, timeline_id).at(later, 100),
176 2 : ]
177 2 : );
178 2 : }
179 :
180 : #[test]
181 2 : fn post_restart_current_exact_logical_size_uses_cached() {
182 2 : let tenant_id = TenantId::generate();
183 2 : let timeline_id = TimelineId::generate();
184 2 :
185 2 : let [now, at_restart] = time_backwards();
186 2 :
187 2 : let now = DateTime::<Utc>::from(now);
188 2 : let before_restart = at_restart - std::time::Duration::from_secs(5 * 60);
189 2 : let before_restart = DateTime::<Utc>::from(before_restart);
190 2 :
191 2 : let snap = TimelineSnapshot {
192 2 : loaded_at: (Lsn(50), at_restart),
193 2 : last_record_lsn: Lsn(50),
194 2 : current_exact_logical_size: None,
195 2 : };
196 2 :
197 2 : let cache = HashMap::from([
198 2 : MetricsKey::timeline_logical_size(tenant_id, timeline_id).at(before_restart, 100)
199 2 : ]);
200 2 :
201 2 : let mut metrics = Vec::new();
202 2 : snap.to_metrics(tenant_id, timeline_id, now, &mut metrics, &cache);
203 2 :
204 6 : metrics.retain(|(key, _)| key.metric == Name::LogicalSize);
205 2 :
206 2 : assert_eq!(
207 2 : metrics,
208 2 : &[MetricsKey::timeline_logical_size(tenant_id, timeline_id).at(now, 100)]
209 2 : );
210 2 : }
211 :
212 : #[test]
213 2 : fn post_restart_synthetic_size_uses_cached_if_available() {
214 2 : let tenant_id = TenantId::generate();
215 2 :
216 2 : let ts = TenantSnapshot {
217 2 : resident_size: 1000,
218 2 : remote_size: 1000,
219 2 : // not yet calculated
220 2 : synthetic_size: 0,
221 2 : };
222 2 :
223 2 : let now = SystemTime::now();
224 2 : let before_restart = DateTime::<Utc>::from(now - std::time::Duration::from_secs(5 * 60));
225 2 : let now = DateTime::<Utc>::from(now);
226 2 :
227 2 : let cached = HashMap::from([MetricsKey::synthetic_size(tenant_id).at(before_restart, 1000)]);
228 2 :
229 2 : let mut metrics = Vec::new();
230 2 : ts.to_metrics(tenant_id, now, &cached, &mut metrics);
231 2 :
232 2 : assert_eq!(
233 2 : metrics,
234 2 : &[
235 2 : MetricsKey::remote_storage_size(tenant_id).at(now, 1000),
236 2 : MetricsKey::resident_size(tenant_id).at(now, 1000),
237 2 : MetricsKey::synthetic_size(tenant_id).at(now, 1000),
238 2 : ]
239 2 : );
240 2 : }
241 :
242 : #[test]
243 2 : fn post_restart_synthetic_size_is_not_sent_when_not_cached() {
244 2 : let tenant_id = TenantId::generate();
245 2 :
246 2 : let ts = TenantSnapshot {
247 2 : resident_size: 1000,
248 2 : remote_size: 1000,
249 2 : // not yet calculated
250 2 : synthetic_size: 0,
251 2 : };
252 2 :
253 2 : let now = SystemTime::now();
254 2 : let now = DateTime::<Utc>::from(now);
255 2 :
256 2 : let cached = HashMap::new();
257 2 :
258 2 : let mut metrics = Vec::new();
259 2 : ts.to_metrics(tenant_id, now, &cached, &mut metrics);
260 2 :
261 2 : assert_eq!(
262 2 : metrics,
263 2 : &[
264 2 : MetricsKey::remote_storage_size(tenant_id).at(now, 1000),
265 2 : MetricsKey::resident_size(tenant_id).at(now, 1000),
266 2 : // no synthetic size here
267 2 : ]
268 2 : );
269 2 : }
270 :
271 8 : fn time_backwards<const N: usize>() -> [std::time::SystemTime; N] {
272 8 : let mut times = [std::time::SystemTime::UNIX_EPOCH; N];
273 8 : times[0] = std::time::SystemTime::now();
274 24 : for behind in 1..N {
275 16 : times[behind] = times[0] - std::time::Duration::from_secs(behind as u64);
276 16 : }
277 :
278 8 : times
279 8 : }
280 :
281 4 : pub(crate) const fn metric_examples(
282 4 : tenant_id: TenantId,
283 4 : timeline_id: TimelineId,
284 4 : now: DateTime<Utc>,
285 4 : before: DateTime<Utc>,
286 4 : ) -> [RawMetric; 6] {
287 4 : [
288 4 : MetricsKey::written_size(tenant_id, timeline_id).at(now, 0),
289 4 : MetricsKey::written_size_delta(tenant_id, timeline_id).from_until(before, now, 0),
290 4 : MetricsKey::timeline_logical_size(tenant_id, timeline_id).at(now, 0),
291 4 : MetricsKey::remote_storage_size(tenant_id).at(now, 0),
292 4 : MetricsKey::resident_size(tenant_id).at(now, 0),
293 4 : MetricsKey::synthetic_size(tenant_id).at(now, 1),
294 4 : ]
295 4 : }
|