Skip to content

Commit 1d336ea

Browse files
authored
Timeseries: use SlateDB's cache manager in Timeseries' cache warmer (#509)
Uses SlateDB's cache manager in Timeseries' cache warmer.
1 parent afa06ac commit 1d336ea

2 files changed

Lines changed: 22 additions & 68 deletions

File tree

timeseries/src/server/cache_warmer.rs

Lines changed: 17 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,18 @@
1-
//! Startup cache warmer that pre-populates the block cache by scanning
2-
//! recent time bucket key ranges through the normal storage read API.
1+
//! Startup cache warmer that pre-populates the block cache for recent time
2+
//! buckets.
33
//!
4-
//! This is a temporary workaround until SlateDB's CacheManager
5-
//! (`set_warm_prefixes()` + `warm_current()`) is available. Delete this
6-
//! module when that lands.
4+
//! It discovers the buckets overlapping the configured recent window and hands
5+
//! them to [`WarmStorage::warm`], which drives SlateDB's cache manager
6+
//! (`warm_sst`) over the SSTs backing those buckets — pulling their filters,
7+
//! index, and (optionally) sample data blocks into the block cache.
78
89
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
910

10-
use common::BytesRange;
11-
use common::storage::StorageError;
1211
use tokio::task::JoinHandle;
1312
use tokio_util::sync::CancellationToken;
1413

1514
use crate::promql::config::CacheWarmerConfig;
16-
use crate::serde::TimeBucketScoped;
17-
use crate::serde::key::{ForwardIndexKey, InvertedIndexKey, SeriesDictionaryKey, TimeSeriesKey};
18-
use crate::storage::StorageRead;
19-
20-
const LOG_INTERVAL: Duration = Duration::from_secs(30);
15+
use crate::storage::WarmStorage;
2116

2217
pub(crate) struct CacheWarmerHandle {
2318
cancel: CancellationToken,
@@ -35,7 +30,7 @@ impl CacheWarmerHandle {
3530

3631
/// Spawns a one-off cache warming task. Returns a handle that must be
3732
/// shut down before closing the database.
38-
pub(crate) fn start<R: StorageRead + 'static>(
33+
pub(crate) fn start<R: WarmStorage + 'static>(
3934
storage: R,
4035
config: CacheWarmerConfig,
4136
) -> CacheWarmerHandle {
@@ -46,7 +41,6 @@ pub(crate) fn start<R: StorageRead + 'static>(
4641
match warm(&storage, &config, &cancel).await {
4742
Ok(stats) => tracing::info!(
4843
buckets = stats.buckets,
49-
records = stats.records,
5044
elapsed = ?stats.elapsed,
5145
"Cache warming complete"
5246
),
@@ -59,11 +53,10 @@ pub(crate) fn start<R: StorageRead + 'static>(
5953

6054
struct WarmStats {
6155
buckets: usize,
62-
records: u64,
6356
elapsed: Duration,
6457
}
6558

66-
async fn warm<R: StorageRead>(
59+
async fn warm<R: WarmStorage>(
6760
storage: &R,
6861
config: &CacheWarmerConfig,
6962
cancel: &CancellationToken,
@@ -80,58 +73,16 @@ async fn warm<R: StorageRead>(
8073
.await?;
8174
let total = buckets.len();
8275

83-
let mut records: u64 = 0;
84-
let mut last_log = Instant::now();
85-
86-
for (i, bucket) in buckets.iter().enumerate() {
87-
if cancel.is_cancelled() {
88-
tracing::info!("Cache warming cancelled");
89-
break;
90-
}
91-
92-
records += drain_scan(storage, ForwardIndexKey::bucket_range(bucket)).await?;
93-
records += drain_scan(storage, InvertedIndexKey::bucket_range(bucket)).await?;
94-
records += drain_scan(storage, SeriesDictionaryKey::bucket_range(bucket)).await?;
95-
96-
if config.include_samples {
97-
records += drain_scan(storage, TimeSeriesKey::bucket_range(bucket)).await?;
98-
}
99-
100-
if last_log.elapsed() >= LOG_INTERVAL {
101-
tracing::info!(
102-
bucket = i + 1,
103-
total,
104-
records,
105-
elapsed = ?start.elapsed(),
106-
"Cache warming progress"
107-
);
108-
last_log = Instant::now();
109-
}
110-
}
76+
storage
77+
.warm(buckets, config.include_samples, cancel)
78+
.await?;
11179

11280
Ok(WarmStats {
11381
buckets: total,
114-
records,
11582
elapsed: start.elapsed(),
11683
})
11784
}
11885

119-
/// Scan a key range, consuming all records to populate the block cache.
120-
/// Returns the number of records touched.
121-
async fn drain_scan<R: StorageRead>(storage: &R, range: BytesRange) -> crate::util::Result<u64> {
122-
let mut iter = storage.scan(range).await?;
123-
let mut count = 0u64;
124-
while iter
125-
.next()
126-
.await
127-
.map_err(StorageError::from_storage)?
128-
.is_some()
129-
{
130-
count += 1;
131-
}
132-
Ok(count)
133-
}
134-
13586
#[cfg(test)]
13687
mod tests {
13788
use super::*;
@@ -173,9 +124,8 @@ mod tests {
173124
let cancel = CancellationToken::new();
174125
let stats = warm(storage.as_ref(), &config, &cancel).await.unwrap();
175126

176-
// then
127+
// then — the recent bucket is discovered and warming succeeds
177128
assert!(stats.buckets > 0);
178-
assert!(stats.records > 0);
179129
}
180130

181131
#[tokio::test]
@@ -193,11 +143,10 @@ mod tests {
193143

194144
// then
195145
assert_eq!(stats.buckets, 0);
196-
assert_eq!(stats.records, 0);
197146
}
198147

199148
#[tokio::test]
200-
async fn should_stop_on_cancellation() {
149+
async fn should_complete_when_cancelled() {
201150
// given
202151
let storage = create_storage().await;
203152
let tsdb = Tsdb::new(storage.clone());
@@ -224,9 +173,9 @@ mod tests {
224173
// when — cancel before starting
225174
let cancel = CancellationToken::new();
226175
cancel.cancel();
227-
let stats = warm(storage.as_ref(), &config, &cancel).await.unwrap();
228176

229-
// then — should have found buckets but processed none
230-
assert_eq!(stats.records, 0);
177+
// then — warming returns gracefully (the warm short-circuits on the
178+
// pre-cancelled token) rather than erroring or hanging
179+
warm(storage.as_ref(), &config, &cancel).await.unwrap();
231180
}
232181
}

timeseries/src/storage/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ pub(crate) use slate::{
2323
insert_series_id, merge_inverted_index, merge_samples,
2424
};
2525

26+
// The cache warmer is the only consumer; featureless builds would flag an
27+
// unconditional re-export as unused.
28+
#[cfg(feature = "http-server")]
29+
pub(crate) use slate::WarmStorage;
30+
2631
#[cfg(any(test, feature = "testing"))]
2732
pub(crate) use factory::in_memory_storage;
2833
#[cfg(test)]

0 commit comments

Comments
 (0)