Skip to content

Commit 4599b48

Browse files
authored
Merge pull request #10 from nomadiz/feat/sled
2 parents e27e833 + 7a9a287 commit 4599b48

24 files changed

+767
-238
lines changed

.DS_Store

0 Bytes
Binary file not shown.

Cargo.lock

+136-53
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

+23-5
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ border-radius: 10px;" src="https://user-images.githubusercontent.com/56880684/20
3939

4040
## Features
4141

42-
- Multi embedded database supported: `RocksDB`, `Redb`
42+
- Multi embedded database supported: `RocksDB`, `Redb`, `Sled
43+
- `
4344
- Cross-platform supported: `Windows`, `Linux` and `MacOS`
4445
- Custom byte layout deserialization
4546
- Execute database command directly in terminal
@@ -48,10 +49,22 @@ border-radius: 10px;" src="https://user-images.githubusercontent.com/56880684/20
4849

4950
## Roadmap
5051

51-
- [ ] NEW: Universal Key Value Storage support (UKV)
52-
- [ ] NEW: Sled support
53-
- [ ] NEW: LevelDB support
54-
- [ ] Adding consistent mode for editor view
52+
- [ ] NEW: Universal Key Value Storage support (UKV)
53+
- [x] NEW: Sled support
54+
- [ ] NEW: LevelDB support
55+
- [ ] Adding consistent mode for editor view
56+
57+
## Supported Storages
58+
59+
EDMA supports multiple databases with easy plug-to-play architecture. Please check below list for supported databases and its features:
60+
61+
| Database name | Description | EDMA release | Pull request |
62+
| ------------- | -------------------------------------------------------------- | --------------------------------------------------------------------------- | ----------------------------------------------------------- |
63+
| RocksDB | Support both non-column and column byte data viewer (`COLUMN`) | [v0.1.0-beta.4](https://github.com/nomadiz/edma/releases/tag/v0.1.0-beta.4) | N/A |
64+
| ReDB | Support default database (Will add `TABLE` view) | [v0.1.0-beta.4](https://github.com/nomadiz/edma/releases/tag/v0.1.0-beta.4) | N/A |
65+
| Sled | Support both non-tree and tree byte data viewer (`TREE`) | [v0.1.0-beta.5](https://github.com/nomadiz/edma/releases/tag/v0.1.0-beta.5) | [#8 Sled support](https://github.com/nomadiz/edma/issues/8) |
66+
67+
To create a PR for a database integration, please go to [`Issues > New Issue > Feature request`](https://github.com/nomadiz/edma/issues/new?assignees=&labels=&template=feature_request.md&title=)
5568

5669
## Getting Started
5770

@@ -202,6 +215,7 @@ Database name should be these two below
202215

203216
- `rocksdb`: RocksDB
204217
- `redb`: Redb
218+
- `sled`: Sled
205219

206220
Database path should be `String` type
207221

@@ -246,6 +260,10 @@ Configuration file example
246260
{
247261
"name": "rocksdb",
248262
"path": "/temp"
263+
},
264+
{
265+
"name": "sled",
266+
"path": "/temp/sled"
249267
}
250268
],
251269
"templates": [

db/CARGO.md

+10-62
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,15 @@
1-
<p align="center">
2-
<img src="https://user-images.githubusercontent.com/56880684/201497081-40976107-ef47-4a12-bf6d-ceafc8da3464.png" width="40%"/>
3-
</p>
4-
<h3 align="center">A distributed Gremlin-compatible graph database</h3>
1+
## Embedded database library for EDMA
52

6-
<h3 align="center">Open for contribution 🚀</h3>
7-
<br/>
3+
**NOTE:** (SolomonDB Forked)
84

9-
<p align="center">
10-
<a href="https://github.com/nomadiz/solomon-db"><img src="https://img.shields.io/badge/built_with-Rust-dca282.svg?style=flat-square"></a>
11-
&nbsp;
12-
<a href="https://github.com/nomadiz/solomon-db"><img src="https://img.shields.io/badge/build%20with-gremlin-green"></a>
13-
&nbsp;
14-
<a href="https://github.com/nomadiz/solomon-db"><img src="https://img.shields.io/github/v/release/nomadiz/solomon-db?color=%23ff00a0&include_prereleases&label=version&sort=semver&style=flat-square"></a>
15-
&nbsp;
16-
<a href="https://github.com/nomadiz/solomon-db/blob/master/LICENSE"><img src="https://img.shields.io/badge/license-MIT License-00bfff.svg?style=flat-square"></a>
5+
### Description
176

18-
</p>
19-
<p align="center">
20-
<a href="https://github.com/nomadiz/solomon-db/graphs/contributors" alt="Contributors">
21-
<img src="https://img.shields.io/github/contributors/nomadiz/solomon-db" /></a>
22-
<a href="https://github.com/nomadiz/solomon-db/pulse" alt="Activity">
23-
&nbsp;
24-
<img src="https://img.shields.io/github/commit-activity/m/nomadiz/solomon-db" /></a>
25-
<a href="https://users.rust-lang.org/t/solomondb-in-development-gremlin-compatible-graph-database-update/84750" alt="Activity">
26-
&nbsp;
27-
<img src="https://img.shields.io/badge/Rust%20User%20Forum-follow-orange"/>
28-
</a>
29-
</p>
7+
Embedded database library that can be installed as Rust crate. This can be used to run an embedded graph database on top of other multiple storage engines
308

31-
## What is SolomonDB?
9+
### Storage layer
3210

33-
**SolomonDB** is an open source, distributed, easy-to-use, user friendly graph database built by nomadic engineers. SolomonDB enhances the experience of working with graph database using GQL (Gremlin Query Language). SolomonDB can run in an offline mode and acts as an embedded graph database on top of **RocksDB**. Last but not least, SolomonDB is a database for community. It supports multiple storage layer with a set of plugins for serialization.
34-
35-
## Why is it named "Solomon"?
36-
37-
Solomon is the name of a the wisest person who ever lived, King Solomon. If you have ever read Bible, you might know some stories of King Solomon. One of those stories is when King Solomon asks for wishdom. Based on that idea, Solomon DB is built as my personal side project to gain more knowledge about database internals and graph database architecture.
38-
39-
## Roadmap
40-
41-
- [x] Implement RocskDB storage layer
42-
- [x] Implement Redb storage layer
43-
- [ ] Database server
44-
- [ ] Embedded library
45-
- [ ] Support Gremlin query language
46-
- [ ] Multi-row, multi-table ACID transactions
47-
- [ ] Single-node, or highly-scalable distributed mode
48-
- [ ] Store structured and unstructured data
49-
- [ ] Client (JS / Rust / Go) library
50-
51-
## Documentation
52-
53-
For guidance on installation, development, deployment, and administration, see our [SolomonDB documentation](https://nomadiz.github.io/solomon-db/).
54-
55-
The documentation page is built using Rust Mdbook. Shoutout to Rust ecosystem. ❤️
56-
57-
## Community
58-
59-
Join our growing community around the world, for help, ideas, and discussions regarding SurrealDB.
60-
61-
- View SolomonDB [Blog](https://nomadiz.hashnode.dev/)
62-
- View weekly announcement [Rust language Forum](https://users.rust-lang.org/t/solomondb-gremlin-compatible-graph-database-weekly-update/84750)
63-
64-
Support the creator
65-
66-
- [Github](https://github.com/chungquantin)
67-
- [Twitter](https://twitter.com/chasechung111)
11+
| Name | Type | Concurrency | Description |
12+
| ----------- | --------- | --------------- | ------------------------------------------------------------------------------------------------------------------ |
13+
| **RocksDB** | key-value | Multi-threaded | OptimisticTransactionDB of RocksDB is applied into SolomonDB to allow ACID transaction with multithreaded feature. |
14+
| **Redb** | key-value | Single-threaded | Simple use case of Redb is efficient for simple on-disk store. |
15+
| **Sled** | key-value | Single-threaded | The champagne of beta embedded databases |

db/Cargo.toml

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "edma_storage"
3-
version = "0.0.1"
3+
version = "0.0.2"
44
publish = true
55
edition = "2021"
66
readme = "CARGO.md"
@@ -21,14 +21,16 @@ license = "MIT"
2121

2222

2323
[features]
24-
default = ["kv-redb", "kv-rocksdb", "test-suite"]
24+
default = ["kv-redb", "kv-rocksdb", "kv-sled", "test-suite"]
25+
kv-sled = ["dep:sled"]
2526
kv-rocksdb = ["dep:rocksdb"]
2627
kv-redb = ["dep:redb"]
2728
test-suite = []
2829
debug-suite = []
2930

3031
[dependencies]
3132
redb = { version = "0.10.0", optional = true }
33+
sled = { version = "0.34.7", optional = true }
3234
rocksdb = { version = "0.19.0", optional = true, features = [
3335
"multi-threaded-cf",
3436
] }
@@ -45,6 +47,5 @@ path-absolutize = "3.0.14"
4547
[dev-dependencies]
4648
tokio = { version = "1.21.2", features = ["macros", "rt"] }
4749

48-
4950
[lib]
5051
name = "db"

db/src/constant/cf.rs

+3-12
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,10 @@ use lazy_static::lazy_static;
55
#[derive(Hash, PartialEq, Eq)]
66
pub enum ColumnFamily {
77
TestSuite,
8-
Edge,
9-
VertexProperty,
10-
Property,
11-
Vertex,
128
}
139

1410
lazy_static! {
15-
pub static ref COLUMN_FAMILIES: HashMap<ColumnFamily, String> = HashMap::from([
16-
(ColumnFamily::TestSuite, "test_suite:v1".to_string()),
17-
(ColumnFamily::Edge, "edges:v1".to_string()),
18-
(ColumnFamily::VertexProperty, "vertex-properties:v1".to_string()),
19-
(ColumnFamily::Property, "properties:v1".to_string()),
20-
(ColumnFamily::Vertex, "vertices:v1".to_string())
21-
]);
22-
pub static ref CF_NAMES: Vec<&'static String> = COLUMN_FAMILIES.values().collect();
11+
pub static ref KEYSPACES: HashMap<ColumnFamily, String> =
12+
HashMap::from([(ColumnFamily::TestSuite, "test_suite:v1".to_string()),]);
13+
pub static ref CF_NAMES: Vec<&'static String> = KEYSPACES.values().collect();
2314
}

db/src/err/mod.rs

+7
Original file line numberDiff line numberDiff line change
@@ -67,3 +67,10 @@ impl From<redb::Error> for Error {
6767
Error::Tx(e.to_string())
6868
}
6969
}
70+
71+
#[cfg(feature = "kv-sled")]
72+
impl From<sled::Error> for Error {
73+
fn from(e: sled::Error) -> Error {
74+
Error::Tx(e.to_string())
75+
}
76+
}

db/src/model/tag.rs

+4
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ impl TagBucket {
2828
self.0.get(key).cloned()
2929
}
3030

31+
pub fn insert(&mut self, key: TagKey, val: TagValue) {
32+
self.0.insert(key, val);
33+
}
34+
3135
pub fn unchecked_get(&self, key: TagKey) -> TagValue {
3236
self.0.get(key).unwrap().clone()
3337
}

db/src/storage/ds.rs

+80-15
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use super::ReDBAdapter;
77

88
#[cfg(feature = "kv-rocksdb")]
99
use super::RocksDBAdapter;
10+
use super::SledAdapter;
1011

1112
#[derive(Copy, Clone)]
1213
pub struct DatastoreRef<'a> {
@@ -27,6 +28,8 @@ pub enum Inner {
2728
RocksDB(RocksDBAdapter),
2829
#[cfg(feature = "kv-redb")]
2930
ReDB(ReDBAdapter),
31+
#[cfg(feature = "kv-sled")]
32+
Sled(SledAdapter),
3033
}
3134

3235
pub struct Datastore {
@@ -43,7 +46,7 @@ impl Datastore {
4346
pub fn new(path: &str) -> Datastore {
4447
match path {
4548
#[cfg(feature = "kv-rocksdb")]
46-
s if s.starts_with("default:") | s.starts_with("rocksdb:") | s.eq("default") => {
49+
s if s.starts_with("rocksdb:") => {
4750
let db = RocksDBAdapter::new(s, None).unwrap();
4851

4952
Datastore {
@@ -58,6 +61,14 @@ impl Datastore {
5861
inner: Inner::ReDB(db),
5962
}
6063
}
64+
#[cfg(feature = "kv-sled")]
65+
s if s.starts_with("sled:") => {
66+
let db = SledAdapter::new(s).unwrap();
67+
68+
Datastore {
69+
inner: Inner::Sled(db),
70+
}
71+
}
6172
_ => unimplemented!(),
6273
}
6374
}
@@ -81,7 +92,8 @@ impl Datastore {
8192
}
8293
impl_transaction_method!(
8394
RocksDB feat "kv-rocksdb",
84-
ReDB feat "kv-redb"
95+
ReDB feat "kv-redb",
96+
Sled feat "kv-sled"
8597
)
8698
}
8799

@@ -103,23 +115,49 @@ impl Datastore {
103115
}
104116
impl_transaction_method!(
105117
RocksDB feat "kv-rocksdb",
106-
ReDB feat "kv-redb"
118+
ReDB feat "kv-redb",
119+
Sled feat "kv-sled"
107120
)
108121
}
109122
}
110123

111124
#[cfg(test)]
112125
mod test {
113126
use crate::{
114-
constant::{ColumnFamily, COLUMN_FAMILIES},
127+
constant::{ColumnFamily, KEYSPACES},
115128
tag, SimpleTransaction,
116129
};
117130

118131
use super::Datastore;
119132

120133
#[tokio::test]
121-
async fn should_create() {
122-
let db = Datastore::new("redb:../temp/v1.redb");
134+
async fn should_rocksdb_create_with_cf() {
135+
let db = Datastore::new("rocksdb:../temp/cf");
136+
assert!(db.transaction(false).await.is_ok());
137+
138+
// Seeding database
139+
let cf_name = KEYSPACES.get(&ColumnFamily::TestSuite).unwrap();
140+
let key1 = i32::to_be_bytes(2100);
141+
let key2 = "cf => hello world";
142+
let key3 = "cf => this is a key";
143+
144+
let val1 = "cf => mock value";
145+
let val2 = "cf => mock value 2";
146+
let val3 = "cf => this is a new value";
147+
148+
let mut tx = db.transaction(true).await.unwrap();
149+
let tags = tag!("column_family" => cf_name.clone());
150+
tx.set(key1, val1, tags.clone()).await.unwrap();
151+
tx.set(key2, val2, tags.clone()).await.unwrap();
152+
tx.set(key3, val3, tags.clone()).await.unwrap();
153+
let iter = tx.iterate(tags.clone()).await.unwrap();
154+
assert!(iter.len() == 3);
155+
tx.commit().await.unwrap();
156+
}
157+
158+
#[tokio::test]
159+
async fn should_redb_create() {
160+
let db = Datastore::new("redb:../temp/redb");
123161
assert!(db.transaction(false).await.is_ok());
124162

125163
let key1 = i32::to_be_bytes(2001);
@@ -134,29 +172,56 @@ mod test {
134172
tx.set(key1, val1, tag!()).await.unwrap();
135173
tx.set(key2, val2, tag!()).await.unwrap();
136174
tx.set(key3, val3, tag!()).await.unwrap();
175+
let iter = tx.iterate(tag!()).await.unwrap();
176+
assert!(iter.len() == 3);
137177
tx.commit().await.unwrap();
138178
}
139179

140180
#[tokio::test]
141-
async fn should_create_with_cf() {
142-
let db = Datastore::new("rocksdb:../temp/cf");
181+
async fn should_sled_create() {
182+
let db = Datastore::new("sled:../temp");
143183
assert!(db.transaction(false).await.is_ok());
144184

145185
// Seeding database
146-
let cf_name = COLUMN_FAMILIES.get(&ColumnFamily::TestSuite).unwrap();
147186
let key1 = i32::to_be_bytes(2100);
148-
let key2 = "cf => hello world";
149-
let key3 = "cf => this is a key";
187+
let key2 = "hello world";
188+
let key3 = "this is a key";
150189

151-
let val1 = "cf => mock value";
152-
let val2 = "cf => mock value 2";
153-
let val3 = "cf => this is a new value";
190+
let val1 = "mock value";
191+
let val2 = "mock value 2";
192+
let val3 = "this is a new value";
154193

155194
let mut tx = db.transaction(true).await.unwrap();
156-
let tags = tag!("column_family" => cf_name.clone());
195+
tx.set(key1, val1, tag!()).await.unwrap();
196+
tx.set(key2, val2, tag!()).await.unwrap();
197+
tx.set(key3, val3, tag!()).await.unwrap();
198+
let iter = tx.iterate(tag!()).await.unwrap();
199+
assert!(iter.len() == 3);
200+
tx.commit().await.unwrap();
201+
}
202+
203+
#[tokio::test]
204+
async fn should_sled_create_tree() {
205+
let db = Datastore::new("sled:../temp/cf");
206+
assert!(db.transaction(false).await.is_ok());
207+
208+
// Seeding database
209+
let tree_name = KEYSPACES.get(&ColumnFamily::TestSuite).unwrap();
210+
let key1 = i32::to_be_bytes(2100);
211+
let key2 = "tree => hello world";
212+
let key3 = "tree => this is a key";
213+
214+
let val1 = "tree => mock value";
215+
let val2 = "tree => mock value 2";
216+
let val3 = "tree => this is a new value";
217+
218+
let mut tx = db.transaction(true).await.unwrap();
219+
let tags = tag!("tree" => tree_name.clone());
157220
tx.set(key1, val1, tags.clone()).await.unwrap();
158221
tx.set(key2, val2, tags.clone()).await.unwrap();
159222
tx.set(key3, val3, tags.clone()).await.unwrap();
223+
let iter = tx.iterate(tags.clone()).await.unwrap();
224+
assert!(iter.len() == 3);
160225
tx.commit().await.unwrap();
161226
}
162227
}

db/src/storage/kvs/mod.rs

+5
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,12 @@ mod redb;
33
#[cfg(feature = "kv-rocksdb")]
44
mod rocksdb;
55

6+
#[cfg(feature = "kv-sled")]
7+
mod sled;
8+
69
#[cfg(feature = "kv-redb")]
710
pub use self::redb::*;
811
#[cfg(feature = "kv-rocksdb")]
912
pub use self::rocksdb::*;
13+
#[cfg(feature = "kv-sled")]
14+
pub use self::sled::*;

0 commit comments

Comments
 (0)