#![cfg(all(feature = "armour", feature = "typed-tree", feature = "rapira-codec"))]
use armdb::armour::{Db, TypedMigration};
use armdb::{CollectionMeta, Config, MigrateAction, NoHook, RapiraCodec, TtlPolicy};
use armour_core::GetType;
use rapira::Rapira;
use tempfile::tempdir;
#[derive(Clone, Debug, PartialEq, Rapira, GetType)]
struct Row {
expires_at_ms: u64,
}
impl CollectionMeta for Row {
type SelfId = [u8; 8];
const NAME: &'static str = "ttl_bootstrap_rows";
const TTL: Option<TtlPolicy<Self>> = Some(TtlPolicy {
revision: 1,
deadline: |row, _| (row.expires_at_ms > 0).then_some(row.expires_at_ms),
});
}
#[derive(Clone, Debug, PartialEq, Rapira, GetType)]
struct MigRow {
expires_at_ms: u64,
}
impl CollectionMeta for MigRow {
type SelfId = [u8; 8];
const NAME: &'static str = "ttl_bootstrap_mig_rows";
const VERSION: u16 = 2;
const TTL: Option<TtlPolicy<Self>> = Some(TtlPolicy {
revision: 1,
deadline: |row, _| (row.expires_at_ms > 0).then_some(row.expires_at_ms),
});
}
fn key(id: u64) -> [u8; 8] {
id.to_be_bytes()
}
fn patch_stored_version(db: &Db, name: &str) {
db.db_info_update(|info| {
info.collections.get_mut(name).unwrap().version = 1;
})
.unwrap();
}
#[test]
fn reopen_rebuilds_from_live_state() {
let dir = tempdir().unwrap();
{
let db = Db::open_test(dir.path()).unwrap();
let tree = db
.open_typed_tree::<Row, RapiraCodec, NoHook>(Config::test(), NoHook, &[])
.unwrap();
tree.put(&key(1), Row { expires_at_ms: 100 }).unwrap();
tree.put(&key(2), Row { expires_at_ms: 200 }).unwrap();
}
let db = Db::open_test(dir.path()).unwrap();
let tree = db
.open_typed_tree::<Row, RapiraCodec, NoHook>(Config::test(), NoHook, &[])
.unwrap();
assert_eq!(tree.ttl_debug_len(), 2);
assert_eq!(tree.ttl_debug_deadline(&key(1)), Some(100));
assert_eq!(tree.ttl_debug_deadline(&key(2)), Some(200));
}
fn v1_to_v2(k: &[u8; 8], _row: &MigRow) -> MigrateAction<MigRow> {
let id = u64::from_be_bytes(*k);
match id {
1 => MigrateAction::Update(MigRow { expires_at_ms: 500 }),
2 => MigrateAction::Delete,
_ => MigrateAction::Keep,
}
}
const MIGRATIONS: &[TypedMigration<[u8; 8], MigRow>] = &[(1, v1_to_v2)];
#[test]
fn migration_update_and_delete_leave_correct_index() {
let dir = tempdir().unwrap();
{
let db = Db::open_test(dir.path()).unwrap();
let tree = db
.open_typed_tree::<MigRow, RapiraCodec, NoHook>(Config::test(), NoHook, &[])
.unwrap();
tree.put(&key(1), MigRow { expires_at_ms: 100 }).unwrap();
tree.put(&key(2), MigRow { expires_at_ms: 200 }).unwrap();
tree.put(&key(3), MigRow { expires_at_ms: 300 }).unwrap();
patch_stored_version(&db, MigRow::NAME);
}
let db = Db::open_test(dir.path()).unwrap();
let tree = db
.open_typed_tree::<MigRow, RapiraCodec, NoHook>(Config::test(), NoHook, MIGRATIONS)
.unwrap();
assert_eq!(tree.ttl_debug_len(), 2);
assert_eq!(tree.ttl_debug_deadline(&key(1)), Some(500));
assert_eq!(tree.ttl_debug_deadline(&key(2)), None);
assert_eq!(tree.ttl_debug_deadline(&key(3)), Some(300));
}