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

Generated by: LCOV version 2.1-beta