Allow reading tail across hosts

This commit is contained in:
Ellie Huxtable 2023-07-09 21:20:12 +01:00
parent 1a6e012295
commit 7b0c72e7e0
4 changed files with 24 additions and 11 deletions

View File

@ -101,7 +101,10 @@ impl KvStore {
let bytes = record.serialize()?; let bytes = record.serialize()?;
let parent = store.tail(host_id, KV_TAG).await?.map(|entry| entry.id); let parent = store
.tail(Some(host_id), KV_TAG)
.await?
.map(|entry| entry.id);
let record = atuin_common::record::Record::builder() let record = atuin_common::record::Record::builder()
.host(host_id) .host(host_id)
@ -127,15 +130,12 @@ impl KvStore {
namespace: &str, namespace: &str,
key: &str, key: &str,
) -> Result<Option<KvRecord>> { ) -> Result<Option<KvRecord>> {
// TODO: don't load this from disk so much
let host_id = Settings::host_id().expect("failed to get host_id");
// Currently, this is O(n). When we have an actual KV store, it can be better // Currently, this is O(n). When we have an actual KV store, it can be better
// Just a poc for now! // Just a poc for now!
// iterate records to find the value we want // iterate records to find the value we want
// start at the end, so we get the most recent version // start at the end, so we get the most recent version
let Some(mut record) = store.tail(host_id, KV_TAG).await? else { let Some(mut record) = store.tail(None, KV_TAG).await? else {
return Ok(None); return Ok(None);
}; };

View File

@ -171,15 +171,28 @@ impl Store for SqliteStore {
Ok(res) Ok(res)
} }
async fn tail(&self, host: Uuid, tag: &str) -> Result<Option<Record<EncryptedData>>> { // Get the tail for a given tag
let res = sqlx::query( // If a host is provided, get the tail for a tag for that host
// Otherwise, the latest record across hosts
async fn tail(&self, host: Option<Uuid>, tag: &str) -> Result<Option<Record<EncryptedData>>> {
let res = if let Some(host) = host {
sqlx::query(
"select * from records rp where tag=?1 and host=?2 and (select count(1) from records where parent=rp.id) = 0;", "select * from records rp where tag=?1 and host=?2 and (select count(1) from records where parent=rp.id) = 0;",
) )
.bind(tag) .bind(tag)
.bind(host.as_simple().to_string()) .bind(host.as_simple().to_string())
.map(Self::query_row) .map(Self::query_row)
.fetch_optional(&self.pool) .fetch_optional(&self.pool)
.await?; .await?
} else {
sqlx::query(
"select * from records rp where tag=?1 and (select count(1) from records where parent=rp.id) = 0;",
)
.bind(tag)
.map(Self::query_row)
.fetch_optional(&self.pool)
.await?
};
Ok(res) Ok(res)
} }

View File

@ -30,7 +30,7 @@ pub trait Store {
/// Get the first record for a given host and tag /// Get the first record for a given host and tag
async fn head(&self, host: Uuid, tag: &str) -> Result<Option<Record<EncryptedData>>>; async fn head(&self, host: Uuid, tag: &str) -> Result<Option<Record<EncryptedData>>>;
/// Get the last record for a given host and tag /// Get the last record for a given host and tag
async fn tail(&self, host: Uuid, tag: &str) -> Result<Option<Record<EncryptedData>>>; async fn tail(&self, host: Option<Uuid>, tag: &str) -> Result<Option<Record<EncryptedData>>>;
async fn tail_records(&self) -> Result<Vec<(Uuid, String, Uuid)>>; async fn tail_records(&self) -> Result<Vec<(Uuid, String, Uuid)>>;
} }

View File

@ -49,7 +49,7 @@ pub async fn operations(diff: Diff, store: &impl Store) -> Result<Vec<Operation>
// if local has the ID, then we should find the actual tail of this // if local has the ID, then we should find the actual tail of this
// store, so we know what we need to update the remote to. // store, so we know what we need to update the remote to.
let tail = store let tail = store
.tail(host, tag.as_str()) .tail(Some(host), tag.as_str())
.await? .await?
.expect("failed to fetch last record, expected tag/host to exist"); .expect("failed to fetch last record, expected tag/host to exist");
@ -163,7 +163,7 @@ async fn sync_download(
let remote_tail = remote_index let remote_tail = remote_index
.get(op.0, op.1.clone()) .get(op.0, op.1.clone())
.expect("remote index does not contain expected tail during download"); .expect("remote index does not contain expected tail during download");
let local_tail = store.tail(op.0, op.1.as_str()).await?; let local_tail = store.tail(Some(op.0), op.1.as_str()).await?;
// //
// We expect that the operations diff will represent the desired state // We expect that the operations diff will represent the desired state
// In this case, that contains the remote tail. // In this case, that contains the remote tail.