Remove rocksdb storage

This commit is contained in:
Maybe Waffle 2022-11-08 11:59:19 +04:00
parent 0df19e5fc9
commit ba8c316a4a
6 changed files with 12 additions and 224 deletions

View file

@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## unreleased ## unreleased
## Removed
- `rocksdb-storage` feature and associated items [**BC**]
## 0.11.1 - 2022-10-31 ## 0.11.1 - 2022-10-31
### Added ### Added

View file

@ -1,6 +1,14 @@
This document describes breaking changes of `teloxide` crate, as well as the ways to update code. This document describes breaking changes of `teloxide` crate, as well as the ways to update code.
Note that the list of required changes is not fully exhaustive and it may lack something in rare cases. Note that the list of required changes is not fully exhaustive and it may lack something in rare cases.
## 0.11 -> 0.??
### teloxide
`rocksdb-storage` feature and associated items were removed.
If you are using rocksdb storage, you'll need to either write `Storage` impl yourself, or use a third party crate.
<!-- FIXME: add a link once there *is* a third party crate -->
## 0.11 -> 0.11.1 ## 0.11 -> 0.11.1
### teloxide ### teloxide

View file

@ -19,7 +19,6 @@ webhooks-axum = ["webhooks", "axum", "tower", "tower-http"]
sqlite-storage = ["sqlx"] sqlite-storage = ["sqlx"]
redis-storage = ["redis"] redis-storage = ["redis"]
rocksdb-storage = ["rocksdb"]
cbor-serializer = ["serde_cbor"] cbor-serializer = ["serde_cbor"]
bincode-serializer = ["bincode"] bincode-serializer = ["bincode"]
@ -43,7 +42,6 @@ full = [
"webhooks-axum", "webhooks-axum",
"sqlite-storage", "sqlite-storage",
"redis-storage", "redis-storage",
"rocksdb-storage",
"cbor-serializer", "cbor-serializer",
"bincode-serializer", "bincode-serializer",
"macros", "macros",
@ -94,9 +92,6 @@ sqlx = { version = "0.6", optional = true, default-features = false, features =
"sqlite", "sqlite",
] } ] }
redis = { version = "0.21", features = ["tokio-comp"], optional = true } redis = { version = "0.21", features = ["tokio-comp"], optional = true }
rocksdb = { version = "0.19", optional = true, default-features = false, features = [
"lz4",
] }
serde_cbor = { version = "0.11", optional = true } serde_cbor = { version = "0.11", optional = true }
bincode = { version = "1.3", optional = true } bincode = { version = "1.3", optional = true }
axum = { version = "0.5.13", optional = true } axum = { version = "0.5.13", optional = true }
@ -131,11 +126,6 @@ name = "sqlite"
path = "tests/sqlite.rs" path = "tests/sqlite.rs"
required-features = ["sqlite-storage", "cbor-serializer", "bincode-serializer"] required-features = ["sqlite-storage", "cbor-serializer", "bincode-serializer"]
[[test]]
name = "rocksdb"
path = "tests/rocksdb.rs"
required-features = ["rocksdb-storage", "cbor-serializer", "bincode-serializer"]
[[example]] [[example]]
name = "dialogue" name = "dialogue"
required-features = ["macros"] required-features = ["macros"]

View file

@ -9,9 +9,6 @@ mod redis_storage;
#[cfg(feature = "sqlite-storage")] #[cfg(feature = "sqlite-storage")]
mod sqlite_storage; mod sqlite_storage;
#[cfg(feature = "rocksdb-storage")]
mod rocksdb_storage;
use futures::future::BoxFuture; use futures::future::BoxFuture;
use teloxide_core::types::ChatId; use teloxide_core::types::ChatId;
@ -28,9 +25,6 @@ use std::sync::Arc;
#[cfg(feature = "sqlite-storage")] #[cfg(feature = "sqlite-storage")]
pub use sqlite_storage::{SqliteStorage, SqliteStorageError}; pub use sqlite_storage::{SqliteStorage, SqliteStorageError};
#[cfg(feature = "rocksdb-storage")]
pub use rocksdb_storage::{RocksDbStorage, RocksDbStorageError};
/// A storage with an erased error type. /// A storage with an erased error type.
pub type ErasedStorage<D> = pub type ErasedStorage<D> =
dyn Storage<D, Error = Box<dyn std::error::Error + Send + Sync>> + Send + Sync; dyn Storage<D, Error = Box<dyn std::error::Error + Send + Sync>> + Send + Sync;

View file

@ -1,113 +0,0 @@
use super::{serializer::Serializer, Storage};
use futures::future::BoxFuture;
use rocksdb::{DBCompressionType, DBWithThreadMode, MultiThreaded};
use serde::{de::DeserializeOwned, Serialize};
use std::{
convert::Infallible,
fmt::{Debug, Display},
str,
sync::Arc,
};
use teloxide_core::types::ChatId;
use thiserror::Error;
/// A persistent dialogue storage based on [RocksDb](http://rocksdb.org/).
pub struct RocksDbStorage<S> {
db: DBWithThreadMode<MultiThreaded>,
serializer: S,
}
/// An error returned from [`RocksDbStorage`].
#[derive(Debug, Error)]
pub enum RocksDbStorageError<SE>
where
SE: Debug + Display,
{
#[error("dialogue serialization error: {0}")]
SerdeError(SE),
#[error("RocksDb error: {0}")]
RocksDbError(#[from] rocksdb::Error),
/// Returned from [`RocksDbStorage::remove_dialogue`].
#[error("row not found")]
DialogueNotFound,
}
impl<S> RocksDbStorage<S> {
pub async fn open(
path: &str,
serializer: S,
options: Option<rocksdb::Options>,
) -> Result<Arc<Self>, RocksDbStorageError<Infallible>> {
let options = match options {
Some(opts) => opts,
None => {
let mut opts = rocksdb::Options::default();
opts.set_compression_type(DBCompressionType::Lz4);
opts.create_if_missing(true);
opts
}
};
let db = DBWithThreadMode::<MultiThreaded>::open(&options, path)?;
Ok(Arc::new(Self { db, serializer }))
}
}
impl<S, D> Storage<D> for RocksDbStorage<S>
where
S: Send + Sync + Serializer<D> + 'static,
D: Send + Serialize + DeserializeOwned + 'static,
<S as Serializer<D>>::Error: Debug + Display,
{
type Error = RocksDbStorageError<<S as Serializer<D>>::Error>;
/// Returns [`RocksDbStorageError::DialogueNotFound`] if a dialogue does not
/// exist.
fn remove_dialogue(
self: Arc<Self>,
ChatId(chat_id): ChatId,
) -> BoxFuture<'static, Result<(), Self::Error>> {
Box::pin(async move {
let key = chat_id.to_le_bytes();
if self.db.get(key)?.is_none() {
return Err(RocksDbStorageError::DialogueNotFound);
}
self.db.delete(key).unwrap();
Ok(())
})
}
fn update_dialogue(
self: Arc<Self>,
ChatId(chat_id): ChatId,
dialogue: D,
) -> BoxFuture<'static, Result<(), Self::Error>> {
Box::pin(async move {
let d =
self.serializer.serialize(&dialogue).map_err(RocksDbStorageError::SerdeError)?;
let key = chat_id.to_le_bytes();
self.db.put(key, &d)?;
Ok(())
})
}
fn get_dialogue(
self: Arc<Self>,
ChatId(chat_id): ChatId,
) -> BoxFuture<'static, Result<Option<D>, Self::Error>> {
Box::pin(async move {
let key = chat_id.to_le_bytes();
self.db
.get(key)?
.map(|d| self.serializer.deserialize(&d).map_err(RocksDbStorageError::SerdeError))
.transpose()
})
}
}

View file

@ -1,95 +0,0 @@
use std::{
fmt::{Debug, Display},
fs,
sync::Arc,
};
use teloxide::{
dispatching::dialogue::{RocksDbStorage, RocksDbStorageError, Serializer, Storage},
types::ChatId,
};
#[tokio::test(flavor = "multi_thread")]
async fn test_rocksdb_json() {
fs::remove_dir_all("./test_db1").ok();
fs::create_dir("./test_db1").unwrap();
let storage = RocksDbStorage::open(
"./test_db1/test_db1.rocksdb",
teloxide::dispatching::dialogue::serializer::Json,
None,
)
.await
.unwrap();
test_rocksdb(storage).await;
fs::remove_dir_all("./test_db1").unwrap();
}
#[tokio::test(flavor = "multi_thread")]
async fn test_rocksdb_bincode() {
fs::remove_dir_all("./test_db2").ok();
fs::create_dir("./test_db2").unwrap();
let storage = RocksDbStorage::open(
"./test_db2/test_db2.rocksdb",
teloxide::dispatching::dialogue::serializer::Bincode,
None,
)
.await
.unwrap();
test_rocksdb(storage).await;
fs::remove_dir_all("./test_db2").unwrap();
}
#[tokio::test(flavor = "multi_thread")]
async fn test_rocksdb_cbor() {
fs::remove_dir_all("./test_db3").ok();
fs::create_dir("./test_db3").unwrap();
let storage = RocksDbStorage::open(
"./test_db3/test_db3.rocksdb",
teloxide::dispatching::dialogue::serializer::Cbor,
None,
)
.await
.unwrap();
test_rocksdb(storage).await;
fs::remove_dir_all("./test_db3").unwrap();
}
type Dialogue = String;
macro_rules! test_dialogues {
($storage:expr, $_0:expr, $_1:expr, $_2:expr) => {
assert_eq!(Arc::clone(&$storage).get_dialogue(ChatId(1)).await.unwrap(), $_0);
assert_eq!(Arc::clone(&$storage).get_dialogue(ChatId(11)).await.unwrap(), $_1);
assert_eq!(Arc::clone(&$storage).get_dialogue(ChatId(256)).await.unwrap(), $_2);
};
}
async fn test_rocksdb<S>(storage: Arc<RocksDbStorage<S>>)
where
S: Send + Sync + Serializer<Dialogue> + 'static,
<S as Serializer<Dialogue>>::Error: Debug + Display,
{
test_dialogues!(storage, None, None, None);
Arc::clone(&storage).update_dialogue(ChatId(1), "ABC".to_owned()).await.unwrap();
Arc::clone(&storage).update_dialogue(ChatId(11), "DEF".to_owned()).await.unwrap();
Arc::clone(&storage).update_dialogue(ChatId(256), "GHI".to_owned()).await.unwrap();
test_dialogues!(
storage,
Some("ABC".to_owned()),
Some("DEF".to_owned()),
Some("GHI".to_owned())
);
Arc::clone(&storage).remove_dialogue(ChatId(1)).await.unwrap();
Arc::clone(&storage).remove_dialogue(ChatId(11)).await.unwrap();
Arc::clone(&storage).remove_dialogue(ChatId(256)).await.unwrap();
test_dialogues!(storage, None, None, None);
// Check that a try to remove a non-existing dialogue results in an error.
assert!(matches!(
Arc::clone(&storage).remove_dialogue(ChatId(1)).await.unwrap_err(),
RocksDbStorageError::DialogueNotFound
));
}