use std::{sync::Arc, time::Duration};
use aok::{OK, Void};
use compio::{runtime::Runtime, time::sleep};
use tempfile::{TempDir, tempdir};
use wbase::time::now_ms;
use wdev::SegmentedDevice;
use wkv::{StoreConfig, TtlOpt, WedbStore};
use wval::{CollectionType, MetaValue};
async fn open_store(tag: &str) -> aok::Result<(TempDir, Arc<WedbStore<SegmentedDevice>>)> {
let dir = tempdir()?;
let device = Arc::new(SegmentedDevice::single_file(
dir.path().join(format!("keyspace_{tag}.db")),
)?);
let mut config = StoreConfig::new(1024, 4096, 16, 0.5)?;
config.gc.enabled = false;
let store = Arc::new(WedbStore::open(config, device)?);
Ok((dir, store))
}
#[test]
fn test_keyspace_basic_counts() -> Void {
let rt = Runtime::new()?;
rt.block_on(async {
let (_dir, store) = open_store("basic").await?;
let session = store.new_session()?;
assert_eq!(store.keyspace_stats().await?, (0, 0));
for k in ["k1", "k2", "k3"] {
session.upsert(k.as_bytes(), b"v").await?;
}
assert_eq!(store.keyspace_stats().await?, (3, 0));
assert_eq!(
session
.expire_at(b"k2", now_ms() + 60_000, TtlOpt::NONE)
.await?,
1
);
assert_eq!(store.keyspace_stats().await?, (3, 1));
session.upsert(b"k1", b"v2").await?;
session.upsert(b"k1", b"v3").await?;
assert_eq!(store.keyspace_stats().await?, (3, 1));
assert!(session.delete(b"k3").await?);
assert_eq!(store.keyspace_stats().await?, (2, 1));
aok::Result::<()>::Ok(())
})?;
OK
}
#[test]
fn test_keyspace_expired_not_counted() -> Void {
let rt = Runtime::new()?;
rt.block_on(async {
let (_dir, store) = open_store("expired").await?;
let session = store.new_session()?;
session.upsert(b"dead", b"v").await?;
session.upsert(b"alive", b"v").await?;
assert_eq!(
session
.expire_at(b"dead", now_ms() + 50, TtlOpt::NONE)
.await?,
1
);
sleep(Duration::from_millis(120)).await;
assert_eq!(store.keyspace_stats().await?, (1, 0));
aok::Result::<()>::Ok(())
})?;
OK
}
#[test]
fn test_keyspace_collections() -> Void {
let rt = Runtime::new()?;
rt.block_on(async {
let (_dir, store) = open_store("collections").await?;
let session = store.new_session()?;
let meta = MetaValue::new(1, CollectionType::Hash, 0, 2);
session.save_meta(b"coll", &meta).await?;
assert_eq!(store.keyspace_stats().await?, (1, 0));
assert_eq!(
session
.expire_at(b"coll", now_ms() + 60_000, TtlOpt::NONE)
.await?,
1
);
assert_eq!(store.keyspace_stats().await?, (1, 1));
let ghost = MetaValue::new(2, CollectionType::Hash, 0, 0);
let meta_k = session.session_meta_key(b"ghost");
session.upsert_raw(&meta_k, &ghost.to_bytes()).await?;
assert_eq!(store.keyspace_stats().await?, (1, 1));
aok::Result::<()>::Ok(())
})?;
OK
}
#[test]
fn test_keyspace_ns_db_isolation() -> Void {
let rt = Runtime::new()?;
rt.block_on(async {
let (_dir, store) = open_store("ns_db").await?;
let session = store.new_session()?;
session.upsert(b"a", b"v").await?;
session.upsert(b"b", b"v").await?;
session.set_active_db(1);
session.upsert(b"a", b"v").await?;
session.upsert(b"c", b"v").await?;
session.set_context(1, 0);
session.upsert(b"a", b"v").await?;
session.upsert(b"d", b"v").await?;
assert_eq!(store.keyspace_stats().await?, (6, 0));
session.set_context(0, 1);
assert_eq!(
session
.expire_at(b"a", now_ms() + 60_000, TtlOpt::NONE)
.await?,
1
);
assert_eq!(store.keyspace_stats().await?, (6, 1));
aok::Result::<()>::Ok(())
})?;
OK
}
#[test]
fn test_keyspace_after_evict() -> Void {
let rt = Runtime::new()?;
rt.block_on(async {
let (_dir, store) = open_store("evict").await?;
let session = store.new_session()?;
for i in 0..8 {
let key = format!("k{i}");
session.upsert(key.as_bytes(), b"value").await?;
}
assert_eq!(
session
.expire_at(b"k0", now_ms() + 60_000, TtlOpt::NONE)
.await?,
1
);
store.flush_and_evict_all().await?;
assert_eq!(store.keyspace_stats().await?, (8, 1));
aok::Result::<()>::Ok(())
})?;
OK
}