Add handle refcount, delete on full removal

This commit is contained in:
asonix 2021-02-08 18:49:14 -06:00
parent 504baf208a
commit da2c1eacae
3 changed files with 79 additions and 21 deletions

View file

@ -559,7 +559,7 @@ fn count(
tree: &sled::transaction::TransactionalTree, tree: &sled::transaction::TransactionalTree,
key: &str, key: &str,
f: impl Fn(u64) -> u64, f: impl Fn(u64) -> u64,
) -> Result<(), sled::transaction::ConflictableTransactionError> { ) -> Result<u64, sled::transaction::ConflictableTransactionError> {
let count = match tree.get(key.as_bytes())? { let count = match tree.get(key.as_bytes())? {
Some(ivec) => { Some(ivec) => {
let s = String::from_utf8_lossy(&ivec); let s = String::from_utf8_lossy(&ivec);
@ -569,11 +569,12 @@ fn count(
None => 0, None => 0,
}; };
let count = (f)(count).to_string(); let count = (f)(count);
let count_string = count.to_string();
tree.insert(key.as_bytes(), count.as_bytes())?; tree.insert(key.as_bytes(), count_string.as_bytes())?;
Ok(()) Ok(count)
} }
impl fmt::Display for OwnerSource { impl fmt::Display for OwnerSource {

View file

@ -237,7 +237,7 @@ impl Store {
domain: &str, domain: &str,
published: DateTime<Utc>, published: DateTime<Utc>,
) -> Result<Profile, StoreError> { ) -> Result<Profile, StoreError> {
let handle = handle.to_lowercase(); let handle_lower = handle.to_lowercase();
let mut id; let mut id;
let mut stored_profile; let mut stored_profile;
@ -285,7 +285,7 @@ impl Store {
// ensure handle uniqueness // ensure handle uniqueness
match self.handle_tree.compare_and_swap( match self.handle_tree.compare_and_swap(
handle_id_key(&handle, domain), handle_id_key(&handle_lower, domain),
None as Option<&[u8]>, None as Option<&[u8]>,
Some(id.as_bytes()), Some(id.as_bytes()),
) { ) {
@ -323,8 +323,8 @@ impl Store {
Ok(()) Ok(())
}); });
if let Err(e) = self.handle_index.insert(&handle) { if let Err(e) = self.handle_index.insert(&handle_lower) {
log::error!("Failed to add {} to search index: {}", handle, e); log::error!("Failed to add {} to search index: {}", handle_lower, e);
} }
if let Err(e) = res { if let Err(e) = res {
@ -333,7 +333,7 @@ impl Store {
let handle_tree = &trees[1]; let handle_tree = &trees[1];
profile_tree.remove(id_profile_key(id).as_bytes())?; profile_tree.remove(id_profile_key(id).as_bytes())?;
handle_tree.remove(handle_id_key(&handle, domain).as_bytes())?; handle_tree.remove(handle_id_key(&handle_lower, domain).as_bytes())?;
Ok(()) Ok(())
})?; })?;
@ -558,6 +558,14 @@ impl Store {
stored_profile.updated = Some(now); stored_profile.updated = Some(now);
let stored_profile_vec = serde_json::to_vec(&stored_profile)?; let stored_profile_vec = serde_json::to_vec(&stored_profile)?;
if let Err(e) = self.handle_index.remove(&stored_profile.handle) {
log::error!(
"Failed to remove {} from handle index: {}",
stored_profile.handle,
e
);
}
if self if self
.profile_tree .profile_tree
.compare_and_swap( .compare_and_swap(

View file

@ -1,10 +1,11 @@
use super::StoreError; use super::{count, StoreError};
use sled::{Db, Tree}; use sled::{Db, Transactional, Tree};
use std::collections::HashSet; use std::collections::HashSet;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct TermSearch { pub struct TermSearch {
trees: Vec<Tree>, trees: Vec<Tree>,
counts: Tree,
} }
impl TermSearch { impl TermSearch {
@ -15,11 +16,14 @@ impl TermSearch {
trees.push(db.open_tree(&format!("/profiles/search/{}/{}", name, i))?); trees.push(db.open_tree(&format!("/profiles/search/{}/{}", name, i))?);
} }
Ok(TermSearch { trees }) let counts = db.open_tree(&format!("/profiles/search/{}/counts", name))?;
Ok(TermSearch { trees, counts })
} }
pub(crate) fn insert(&self, term: &str) -> Result<(), StoreError> { pub(crate) fn insert(&self, term: &str) -> Result<(), StoreError> {
let term_vec = term.to_lowercase().as_bytes().to_vec(); let term = term.to_lowercase();
let term_vec = term.as_bytes().to_vec();
if term.is_empty() { if term.is_empty() {
return Err(StoreError::Empty); return Err(StoreError::Empty);
@ -31,17 +35,51 @@ impl TermSearch {
let tree = &self.trees[term.len() - 1]; let tree = &self.trees[term.len() - 1];
tree.transaction(|tree| { [tree, &self.counts].transaction(|trees| {
for i in 0..term_vec.len() { let tree = &trees[0];
let (start, end) = term_vec.split_at(i); let counts = &trees[1];
let mut key = Vec::with_capacity(term_vec.len() + 1); if count(counts, &term, |c| c.saturating_add(1))? == 1 {
key.extend(end); for i in 0..term_vec.len() {
key.push(127); let key = key(&term_vec, i);
key.extend(start);
tree.insert(key.as_slice(), term_vec.as_slice())?; tree.insert(key.as_slice(), term_vec.as_slice())?;
} }
}
Ok(())
})?;
Ok(())
}
pub(crate) fn remove(&self, term: &str) -> Result<(), StoreError> {
let term = term.to_lowercase();
let term_vec = term.as_bytes().to_vec();
if term.is_empty() {
return Err(StoreError::Empty);
}
if term.len() > self.trees.len() {
return Err(StoreError::TooLong);
}
let tree = &self.trees[term.len() - 1];
[tree, &self.counts].transaction(|trees| {
let tree = &trees[0];
let counts = &trees[1];
if count(counts, &term, |c| c.saturating_add(1))? == 0 {
for i in 0..term_vec.len() {
let key = key(&term_vec, i);
tree.remove(key.as_slice())?;
}
counts.remove(term.as_bytes())?;
}
Ok(()) Ok(())
})?; })?;
@ -67,6 +105,17 @@ impl TermSearch {
} }
} }
fn key(term_vec: &[u8], i: usize) -> Vec<u8> {
let (start, end) = term_vec.split_at(i);
let mut key = Vec::with_capacity(term_vec.len() + 1);
key.extend(end);
key.push(127);
key.extend(start);
key
}
struct Deduplicate<T> { struct Deduplicate<T> {
iter: T, iter: T,
seen: HashSet<sled::IVec>, seen: HashSet<sled::IVec>,