#![allow(clippy::unwrap_used, clippy::expect_used)]
use std::collections::BTreeMap;
use chronix::prelude::*;
use chronix::{Chronix, fields, tags};
const HOUR_NS: i64 = 3_600_000_000_000;
fn open_without_lvc(dir: &tempfile::TempDir) -> Chronix {
let config = ChronixConfig::builder()
.data_dir(dir.path())
.enable_last_value_cache(false)
.build()
.unwrap();
Chronix::open(config).unwrap()
}
fn value(db: &Chronix, ts_expected: i64) {
let tags: BTreeMap<String, String> = BTreeMap::from([("h".to_string(), "a".to_string())]);
let got = db.last_value("m", &tags).unwrap().expect("a point");
assert_eq!(
got.timestamp(),
ts_expected,
"last_value returned a stale point"
);
}
#[test]
fn late_write_does_not_shadow_a_newer_flushed_point() {
let dir = tempfile::tempdir().unwrap();
let db = open_without_lvc(&dir);
let key = SeriesKey::new("m", tags! { "h" => "a" }).unwrap();
let newest = 2 * HOUR_NS;
db.insert(&Point::new(key.clone(), fields! { "v" => 2.0 }, newest).unwrap())
.unwrap();
db.flush().unwrap();
let late = HOUR_NS;
db.insert(&Point::new(key.clone(), fields! { "v" => 1.0 }, late).unwrap())
.unwrap();
value(&db, newest);
}
#[test]
fn memtable_point_wins_when_it_is_actually_newer() {
let dir = tempfile::tempdir().unwrap();
let db = open_without_lvc(&dir);
let key = SeriesKey::new("m", tags! { "h" => "a" }).unwrap();
db.insert(&Point::new(key.clone(), fields! { "v" => 1.0 }, HOUR_NS).unwrap())
.unwrap();
db.flush().unwrap();
db.insert(&Point::new(key.clone(), fields! { "v" => 2.0 }, 2 * HOUR_NS).unwrap())
.unwrap();
value(&db, 2 * HOUR_NS);
}
#[test]
fn segment_only_series_reads_back() {
let dir = tempfile::tempdir().unwrap();
let db = open_without_lvc(&dir);
let key = SeriesKey::new("m", tags! { "h" => "a" }).unwrap();
db.insert(&Point::new(key.clone(), fields! { "v" => 1.0 }, HOUR_NS).unwrap())
.unwrap();
db.insert(&Point::new(key.clone(), fields! { "v" => 2.0 }, 2 * HOUR_NS).unwrap())
.unwrap();
db.flush().unwrap();
value(&db, 2 * HOUR_NS);
}
#[test]
fn last_value_finds_a_pre_epoch_point() {
let dir = tempfile::tempdir().unwrap();
let db = chronix::Chronix::open(
chronix::prelude::ChronixConfig::builder()
.data_dir(dir.path())
.build()
.unwrap(),
)
.unwrap();
let key = chronix::prelude::SeriesKey::new("cpu", chronix::tags! { "host" => "a" }).unwrap();
let ts = -3_600_000_000_000; db.insert(
&chronix::prelude::Point::new(key.clone(), chronix::fields! { "v" => 7.0 }, ts).unwrap(),
)
.unwrap();
let plan = db
.query()
.measurement("cpu")
.range(i64::MIN, i64::MAX)
.build()
.unwrap();
assert_eq!(
db.execute(&plan).unwrap().num_rows(),
1,
"a query sees the row"
);
let last = db
.last_value(
"cpu",
&std::collections::BTreeMap::from([("host".to_owned(), "a".to_owned())]),
)
.unwrap()
.expect("last_value must see it too");
assert_eq!(last.timestamp(), ts);
db.close().unwrap();
}
#[test]
fn tag_discovery_sees_unflushed_series() {
let dir = tempfile::tempdir().unwrap();
let db = chronix::Chronix::open(
chronix::prelude::ChronixConfig::builder()
.data_dir(dir.path())
.build()
.unwrap(),
)
.unwrap();
let key =
chronix::prelude::SeriesKey::new("cpu", chronix::tags! { "host" => "a", "region" => "eu" })
.unwrap();
db.insert(&chronix::prelude::Point::new(key, chronix::fields! { "v" => 1.0 }, 1_000).unwrap())
.unwrap();
assert!(db.tag_keys().contains(&"host".to_owned()));
assert!(db.tag_keys().contains(&"region".to_owned()));
assert_eq!(db.tag_values("host"), vec!["a".to_owned()]);
db.flush().unwrap();
assert!(db.tag_keys().contains(&"host".to_owned()));
assert_eq!(db.tag_values("region"), vec!["eu".to_owned()]);
let key2 =
chronix::prelude::SeriesKey::new("cpu", chronix::tags! { "host" => "b", "region" => "us" })
.unwrap();
db.insert(&chronix::prelude::Point::new(key2, chronix::fields! { "v" => 2.0 }, 2_000).unwrap())
.unwrap();
assert_eq!(db.tag_values("host"), vec!["a".to_owned(), "b".to_owned()]);
db.close().unwrap();
}