This commit is contained in:
Ellie Huxtable 2023-12-13 09:03:43 +00:00
parent b99de3c4d7
commit 012b8723af
4 changed files with 54 additions and 42 deletions

View File

@ -135,14 +135,23 @@ impl KvStore {
// well.
pub async fn get(
&self,
_store: &impl Store,
_encryption_key: &[u8; 32],
_namespace: &str,
_key: &str,
store: &impl Store,
encryption_key: &[u8; 32],
namespace: &str,
key: &str,
) -> Result<Option<KvRecord>> {
// TODO: implement
// TODO: don't rebuild every time...
let map = self.build_kv(store, encryption_key).await?;
Ok(None)
let res = map.get(namespace);
if let Some(ns) = res {
let value = ns.get(key);
Ok(value.cloned())
} else {
Ok(None)
}
}
// Build a kv map out of the linked list kv store
@ -153,27 +162,30 @@ impl KvStore {
&self,
store: &impl Store,
encryption_key: &[u8; 32],
) -> Result<BTreeMap<String, BTreeMap<String, String>>> {
let map = BTreeMap::new();
) -> Result<BTreeMap<String, BTreeMap<String, KvRecord>>> {
let mut map = BTreeMap::new();
// get the status of all stores
let mut tagged = store.all_tagged(KV_TAG).await?;
// TODO: maybe don't load the entire tag into memory to build the kv
// we can be smart about it and only load values since the last build
// or, iterate/paginate
let tagged = store.all_tagged(KV_TAG).await?;
// iterate through all tags and play each KV record at a time
for (host, record) in tagged {
// this is "last write wins"
// probably good enough for now, but revisit in future
for record in tagged {
let decrypted = match record.version.as_str() {
KV_VERSION => record.decrypt::<PASETO_V4>(encryption_key)?,
version => bail!("unknown version {version:?}"),
};
let kv = KvRecord::deserialize(&decrypted.data, &decrypted.version)?;
let kv = KvRecord::deserialize(&decrypted.data, KV_VERSION)?;
let next = store.next(host, KV_TAG, decrypted.idx, 1).await?;
let ns = map
.entry(kv.namespace.clone())
.or_insert_with(BTreeMap::new);
write a function that iterates the kv records in order and builds a map
maybe next_record(tag) that returns the next of that tag in time, ignoring host?
then write some tests for this new sync
also see what happens if we run the new server but try and sync with v17 client. I imagine `atuin sync` breaks, but auto sync is fine. maybe version the api now.
ns.entry(kv.key.clone()).or_insert_with(|| kv);
}
Ok(map)
@ -225,19 +237,27 @@ mod tests {
let map = kv.build_kv(&store, &key).await.unwrap();
assert_eq!(
map.get("test-kv")
*map.get("test-kv")
.expect("map namespace not set")
.get("foo")
.expect("map key not set"),
"bar"
KvRecord {
namespace: String::from("test-kv"),
key: String::from("foo"),
value: String::from("bar")
}
);
assert_eq!(
map.get("test-kv")
*map.get("test-kv")
.expect("map namespace not set")
.get("1")
.expect("map key not set"),
"2"
KvRecord {
namespace: String::from("test-kv"),
key: String::from("1"),
value: String::from("2")
}
);
}
}

View File

@ -2,8 +2,8 @@
// Multiple stores of multiple types are all stored in one chonky table (for now), and we just index
// by tag/host
use std::path::Path;
use std::str::FromStr;
use std::{collections::HashMap, path::Path};
use async_trait::async_trait;
use eyre::{eyre, Result};
@ -227,22 +227,14 @@ impl Store for SqliteStore {
Ok(status)
}
async fn all_tagged(&self, tag: &str) -> Result<HashMap<HostId, Record<EncryptedData>>> {
let res = sqlx::query("select * from store where idx = 0 and tag = ?1")
async fn all_tagged(&self, tag: &str) -> Result<Vec<Record<EncryptedData>>> {
let res = sqlx::query("select * from store where tag = ?1 order by timestamp asc")
.bind(tag)
.map(Self::query_row)
.fetch_all(&self.pool)
.await?;
let mut ret = HashMap::new();
for i in res {
assert!(ret.get(&i.host.id).is_none());
ret.insert(i.host.id, i);
}
Ok(ret)
Ok(res)
}
}

View File

@ -1,5 +1,3 @@
use std::collections::HashMap;
use async_trait::async_trait;
use eyre::Result;
@ -50,5 +48,5 @@ pub trait Store {
/// Get every start record for a given tag, regardless of host.
/// Useful when actually operating on synchronized data, and will often have conflict
/// resolution applied.
async fn all_tagged(&self, tag: &str) -> Result<HashMap<HostId, Record<EncryptedData>>>;
async fn all_tagged(&self, tag: &str) -> Result<Vec<Record<EncryptedData>>>;
}

View File

@ -39,17 +39,19 @@ impl Cmd {
println!("\t\tidx: {idx}");
if let Some(first) = first {
println!("\t\tfirst: {}", first.id.0.as_hyphenated().to_string());
println!("\t\tfirst: {}", first.id.0.as_hyphenated());
let time = OffsetDateTime::from_unix_timestamp_nanos(first.timestamp as i128)?;
println!("\t\t\tcreated: {}", time.to_string());
let time =
OffsetDateTime::from_unix_timestamp_nanos(i128::from(first.timestamp))?;
println!("\t\t\tcreated: {time}");
}
if let Some(last) = last {
println!("\t\tlast: {}", last.id.0.as_hyphenated().to_string());
println!("\t\tlast: {}", last.id.0.as_hyphenated());
let time = OffsetDateTime::from_unix_timestamp_nanos(last.timestamp as i128)?;
println!("\t\t\tcreated: {:?}", time.to_string());
let time =
OffsetDateTime::from_unix_timestamp_nanos(i128::from(last.timestamp))?;
println!("\t\t\tcreated: {time}");
}
}