LCOV - code coverage report
Current view: top level - pageserver/src/consumption_metrics/metrics - tests.rs (source / functions) Coverage Total Hit
Test: aca806cab4756d7eb6a304846130f4a73a5d5393.info Lines: 100.0 % 300 300
Test Date: 2025-04-24 20:31:15 Functions: 100.0 % 14 14

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

Generated by: LCOV version 2.1-beta