#![cfg(feature = "kv-indxdb")]
use std::ops::Range;
use std::sync::atomic::{AtomicBool, Ordering};
use indxdb::{Database as Db, Transaction as Tx};
use tokio::sync::RwLock;
use super::api::{BoxFut, KeysResult, ScanLimit, ScanResult};
use super::err::{Error, Result};
use super::{ESTIMATED_BYTES_PER_KEY, ESTIMATED_BYTES_PER_KV, util};
use crate::key::debug::Sprintable;
use crate::kvs::api::Transactable;
use crate::kvs::{Key, Val};
pub struct Datastore {
db: Db,
}
pub struct Transaction {
done: AtomicBool,
write: bool,
inner: RwLock<Tx>,
}
impl Datastore {
pub async fn new(path: &str) -> Result<Datastore> {
match indxdb::Database::new(path).await {
Ok(db) => Ok(Datastore {
db,
}),
Err(e) => Err(Error::Datastore(e.to_string())),
}
}
pub(crate) async fn shutdown(&self) -> Result<()> {
Ok(())
}
pub async fn transaction(&self, write: bool, _: bool) -> Result<Box<dyn Transactable>> {
match self.db.begin(write).await {
Ok(txn) => Ok(Box::new(Transaction {
done: AtomicBool::new(false),
write,
inner: RwLock::new(txn),
})),
Err(e) => Err(Error::from(e)),
}
}
}
impl Transactable for Transaction {
fn kind(&self) -> &'static str {
"indxdb"
}
fn closed(&self) -> bool {
self.done.load(Ordering::Relaxed)
}
fn writeable(&self) -> bool {
self.write
}
#[instrument(level = "trace", target = "surrealdb::core::kvs::api", skip(self))]
fn cancel(&self) -> BoxFut<'_, Result<()>> {
Box::pin(async move {
if self.done.swap(true, Ordering::AcqRel) {
return Err(Error::TransactionFinished);
}
let mut inner = self.inner.write().await;
inner.cancel().await?;
Ok(())
})
}
#[instrument(level = "trace", target = "surrealdb::core::kvs::api", skip(self))]
fn commit(&self) -> BoxFut<'_, Result<()>> {
Box::pin(async move {
if self.done.swap(true, Ordering::AcqRel) {
return Err(Error::TransactionFinished);
}
if !self.writeable() {
return Err(Error::TransactionReadonly);
}
let mut inner = self.inner.write().await;
inner.commit().await?;
Ok(())
})
}
#[instrument(level = "trace", target = "surrealdb::core::kvs::api", skip(self), fields(key = key.sprint()))]
fn exists(&self, key: Key, version: Option<u64>) -> BoxFut<'_, Result<bool>> {
Box::pin(async move {
if version.is_some() {
return Err(Error::UnsupportedVersionedQueries);
}
if self.closed() {
return Err(Error::TransactionFinished);
}
let inner = self.inner.read().await;
let res = inner.exists(key).await?;
Ok(res)
})
}
#[instrument(level = "trace", target = "surrealdb::core::kvs::api", skip(self), fields(key = key.sprint()))]
fn get(&self, key: Key, version: Option<u64>) -> BoxFut<'_, Result<Option<Val>>> {
Box::pin(async move {
if version.is_some() {
return Err(Error::UnsupportedVersionedQueries);
}
if self.closed() {
return Err(Error::TransactionFinished);
}
let inner = self.inner.read().await;
let res = inner.get(key).await?;
Ok(res)
})
}
#[instrument(level = "trace", target = "surrealdb::core::kvs::api", skip(self), fields(key = key.sprint()))]
fn set(&self, key: Key, val: Val) -> BoxFut<'_, Result<()>> {
Box::pin(async move {
if self.closed() {
return Err(Error::TransactionFinished);
}
if !self.writeable() {
return Err(Error::TransactionReadonly);
}
let mut inner = self.inner.write().await;
inner.set(key, val).await?;
Ok(())
})
}
#[instrument(level = "trace", target = "surrealdb::core::kvs::api", skip(self), fields(key = key.sprint()))]
fn put(&self, key: Key, val: Val) -> BoxFut<'_, Result<()>> {
Box::pin(async move {
if self.closed() {
return Err(Error::TransactionFinished);
}
if !self.writeable() {
return Err(Error::TransactionReadonly);
}
let mut inner = self.inner.write().await;
inner.put(key, val).await?;
Ok(())
})
}
#[instrument(level = "trace", target = "surrealdb::core::kvs::api", skip(self), fields(key = key.sprint()))]
fn putc(&self, key: Key, val: Val, chk: Option<Val>) -> BoxFut<'_, Result<()>> {
Box::pin(async move {
if self.closed() {
return Err(Error::TransactionFinished);
}
if !self.writeable() {
return Err(Error::TransactionReadonly);
}
let mut inner = self.inner.write().await;
inner.putc(key, val, chk.map(Into::into)).await?;
Ok(())
})
}
#[instrument(level = "trace", target = "surrealdb::core::kvs::api", skip(self), fields(key = key.sprint()))]
fn del(&self, key: Key) -> BoxFut<'_, Result<()>> {
Box::pin(async move {
if self.closed() {
return Err(Error::TransactionFinished);
}
if !self.writeable() {
return Err(Error::TransactionReadonly);
}
let mut inner = self.inner.write().await;
let res = inner.del(key).await?;
Ok(res)
})
}
#[instrument(level = "trace", target = "surrealdb::core::kvs::api", skip(self), fields(key = key.sprint()))]
fn delc(&self, key: Key, chk: Option<Val>) -> BoxFut<'_, Result<()>> {
Box::pin(async move {
if self.closed() {
return Err(Error::TransactionFinished);
}
if !self.writeable() {
return Err(Error::TransactionReadonly);
}
let mut inner = self.inner.write().await;
let res = inner.delc(key, chk.map(Into::into)).await?;
Ok(res)
})
}
#[instrument(level = "trace", target = "surrealdb::core::kvs::api", skip(self), fields(rng = rng.sprint()))]
fn keys(
&self,
rng: Range<Key>,
limit: ScanLimit,
skip: u32,
version: Option<u64>,
) -> BoxFut<'_, Result<KeysResult>> {
Box::pin(async move {
if version.is_some() {
return Err(Error::UnsupportedVersionedQueries);
}
if self.closed() {
return Err(Error::TransactionFinished);
}
let inner = self.inner.read().await;
let count = match limit {
ScanLimit::Count(c) => c.saturating_add(skip),
ScanLimit::Bytes(b) => (b / ESTIMATED_BYTES_PER_KEY).max(1).saturating_add(skip),
ScanLimit::BytesOrCount(_, c) => c.saturating_add(skip),
};
let res = inner.keys(rng, count).await?;
Ok(consume_keys(&mut res.into_iter(), limit, skip))
})
}
#[instrument(level = "trace", target = "surrealdb::core::kvs::api", skip(self), fields(rng = rng.sprint()))]
fn keysr(
&self,
rng: Range<Key>,
limit: ScanLimit,
skip: u32,
version: Option<u64>,
) -> BoxFut<'_, Result<KeysResult>> {
Box::pin(async move {
if version.is_some() {
return Err(Error::UnsupportedVersionedQueries);
}
if self.closed() {
return Err(Error::TransactionFinished);
}
let inner = self.inner.read().await;
let count = match limit {
ScanLimit::Count(c) => c.saturating_add(skip),
ScanLimit::Bytes(b) => (b / ESTIMATED_BYTES_PER_KEY).max(1).saturating_add(skip),
ScanLimit::BytesOrCount(_, c) => c.saturating_add(skip),
};
let res = inner.keysr(rng, count).await?;
Ok(consume_keys(&mut res.into_iter(), limit, skip))
})
}
#[instrument(level = "trace", target = "surrealdb::core::kvs::api", skip(self), fields(rng = rng.sprint()))]
fn scan(
&self,
rng: Range<Key>,
limit: ScanLimit,
skip: u32,
version: Option<u64>,
) -> BoxFut<'_, Result<ScanResult>> {
Box::pin(async move {
if version.is_some() {
return Err(Error::UnsupportedVersionedQueries);
}
if self.closed() {
return Err(Error::TransactionFinished);
}
let inner = self.inner.read().await;
let rng = if skip > 0 {
let skipped = inner.keys(rng.clone(), skip).await?;
match skipped.last() {
Some(last) => {
let mut start = last.clone();
util::advance_key(&mut start);
start..rng.end
}
None => return Ok(ScanResult::default()),
}
} else {
rng
};
let count = match limit {
ScanLimit::Count(c) => c,
ScanLimit::Bytes(b) => (b / ESTIMATED_BYTES_PER_KV).max(1),
ScanLimit::BytesOrCount(_, c) => c,
};
let res = inner.scan(rng, count).await?;
Ok(consume_vals(&mut res.into_iter(), limit))
})
}
#[instrument(level = "trace", target = "surrealdb::core::kvs::api", skip(self), fields(rng = rng.sprint()))]
fn scanr(
&self,
rng: Range<Key>,
limit: ScanLimit,
skip: u32,
version: Option<u64>,
) -> BoxFut<'_, Result<ScanResult>> {
Box::pin(async move {
if version.is_some() {
return Err(Error::UnsupportedVersionedQueries);
}
if self.closed() {
return Err(Error::TransactionFinished);
}
let inner = self.inner.read().await;
let rng = if skip > 0 {
let skipped = inner.keysr(rng.clone(), skip).await?;
match skipped.last() {
Some(last) => {
let end = last.clone();
rng.start..end
}
None => return Ok(ScanResult::default()),
}
} else {
rng
};
let count = match limit {
ScanLimit::Count(c) => c,
ScanLimit::Bytes(b) => (b / ESTIMATED_BYTES_PER_KV).max(1),
ScanLimit::BytesOrCount(_, c) => c,
};
let res = inner.scanr(rng, count).await?;
Ok(consume_vals(&mut res.into_iter(), limit))
})
}
fn new_save_point(&self) -> BoxFut<'_, Result<()>> {
Box::pin(async move {
self.inner.write().await.set_savepoint().await?;
Ok(())
})
}
fn rollback_to_save_point(&self) -> BoxFut<'_, Result<()>> {
Box::pin(async move {
self.inner.write().await.rollback_to_savepoint().await?;
Ok(())
})
}
fn release_last_save_point(&self) -> BoxFut<'_, Result<()>> {
Box::pin(async move { Ok(()) })
}
}
fn consume_keys<I: Iterator<Item = Key>>(iter: &mut I, limit: ScanLimit, skip: u32) -> KeysResult {
for _ in 0..skip {
if iter.next().is_none() {
return KeysResult::default();
}
}
let mut key_bytes = 0u64;
let keys = match limit {
ScanLimit::Count(c) => {
let mut res = Vec::with_capacity(c.min(4096) as usize);
while res.len() < c as usize {
if let Some(k) = iter.next() {
key_bytes += k.len() as u64;
res.push(k);
} else {
break;
}
}
res
}
ScanLimit::Bytes(b) => {
let mut res = Vec::with_capacity((b / ESTIMATED_BYTES_PER_KEY).min(4096) as usize);
while key_bytes < b as u64 {
if let Some(k) = iter.next() {
key_bytes += k.len() as u64;
res.push(k);
} else {
break;
}
}
res
}
ScanLimit::BytesOrCount(b, c) => {
let mut res = Vec::with_capacity(c.min(4096) as usize);
while res.len() < c as usize && key_bytes < b as u64 {
if let Some(k) = iter.next() {
key_bytes += k.len() as u64;
res.push(k);
} else {
break;
}
}
res
}
};
KeysResult {
keys,
key_bytes,
}
}
fn consume_vals<I: Iterator<Item = (Key, Val)>>(iter: &mut I, limit: ScanLimit) -> ScanResult {
let mut key_bytes = 0u64;
let mut value_bytes = 0u64;
let values = match limit {
ScanLimit::Count(c) => {
let mut res = Vec::with_capacity(c.min(4096) as usize);
while res.len() < c as usize {
if let Some((k, v)) = iter.next() {
key_bytes += k.len() as u64;
value_bytes += v.len() as u64;
res.push((k, v));
} else {
break;
}
}
res
}
ScanLimit::Bytes(b) => {
let mut res = Vec::with_capacity((b / ESTIMATED_BYTES_PER_KV).min(4096) as usize);
let mut bytes_fetched = 0u64;
while bytes_fetched < b as u64 {
if let Some((k, v)) = iter.next() {
let key_len = k.len() as u64;
let value_len = v.len() as u64;
bytes_fetched += key_len + value_len;
key_bytes += key_len;
value_bytes += value_len;
res.push((k, v));
} else {
break;
}
}
res
}
ScanLimit::BytesOrCount(b, c) => {
let mut res = Vec::with_capacity(c.min(4096) as usize);
let mut bytes_fetched = 0u64;
while res.len() < c as usize && bytes_fetched < b as u64 {
if let Some((k, v)) = iter.next() {
let key_len = k.len() as u64;
let value_len = v.len() as u64;
bytes_fetched += key_len + value_len;
key_bytes += key_len;
value_bytes += value_len;
res.push((k, v));
} else {
break;
}
}
res
}
};
ScanResult {
values,
key_bytes,
value_bytes,
}
}