#![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, ScanResult};
use super::err::{Error, Result};
use super::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: u32,
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 = limit.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: u32,
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 = limit.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: u32,
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 res = inner.scan(rng, limit).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: u32,
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 res = inner.scanr(rng, limit).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: u32, skip: u32) -> KeysResult {
for _ in 0..skip {
if iter.next().is_none() {
return KeysResult::default();
}
}
let mut key_bytes = 0u64;
let mut keys = Vec::with_capacity(limit.min(4096) as usize);
while keys.len() < limit as usize {
if let Some(k) = iter.next() {
key_bytes += k.len() as u64;
keys.push(k);
} else {
break;
}
}
KeysResult {
keys,
key_bytes,
}
}
fn consume_vals<I: Iterator<Item = (Key, Val)>>(iter: &mut I, limit: u32) -> ScanResult {
let mut key_bytes = 0u64;
let mut value_bytes = 0u64;
let mut values = Vec::with_capacity(limit.min(4096) as usize);
while values.len() < limit as usize {
if let Some((k, v)) = iter.next() {
key_bytes += k.len() as u64;
value_bytes += v.len() as u64;
values.push((k, v));
} else {
break;
}
}
ScanResult {
values,
key_bytes,
value_bytes,
}
}