Skip to content

Commit

Permalink
[server] Add cluster election #1225 (#1230)
Browse files Browse the repository at this point in the history
* Update cluster_test.rs

* working cluster

* tweaks

* Update agdb_server.yaml
  • Loading branch information
michaelvlach authored Sep 5, 2024
1 parent 6155769 commit 67c0033
Show file tree
Hide file tree
Showing 20 changed files with 852 additions and 311 deletions.
7 changes: 5 additions & 2 deletions .github/workflows/agdb_server.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@ jobs:
- uses: actions-rust-lang/setup-rust-toolchain@v1
- uses: taiki-e/install-action@cargo-llvm-cov
- run: rustup component add llvm-tools-preview
- run: cargo llvm-cov --package agdb_server --all-features --ignore-filename-regex "agdb(.|..)src|agdb_derive|agdb_api|api.rs" --fail-uncovered-functions 0 --show-missing-lines
- run: cargo llvm-cov clean --workspace
- run: cargo llvm-cov --package agdb_server --all-features --no-report
- run: cargo llvm-cov --package agdb_server --all-features --no-report -- --ignored
- run: cargo llvm-cov report --ignore-filename-regex "agdb(.|..)src|agdb_derive|agdb_api|api.rs" --fail-uncovered-functions 0 --show-missing-lines

agdb_server_test:
runs-on: ubuntu-latest
Expand All @@ -45,7 +48,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: actions-rust-lang/setup-rust-toolchain@v1
- run: threshold=2; count=0; while cargo test --release --package agdb_server &> test.log && [[ "$count" != "$threshold" ]]; do count=$((count+1)); echo -n "."; done; cat test.log; echo "$count of $threshold tests run"
- run: threshold=2; count=0; while cargo test --release --package agdb_server -- --include-ignored &> test.log && [[ "$count" != "$threshold" ]]; do count=$((count+1)); echo -n "."; done; cat test.log; echo "$count of $threshold tests run"

agdb_server_format:
runs-on: ubuntu-latest
Expand Down
18 changes: 12 additions & 6 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,23 @@
},
{
"label": "Coverage Server",
"type": "process",
"command": "cargo",
"args": ["llvm-cov", "--package", "agdb_server", "--all-features", "--ignore-filename-regex", "agdb(.|..)src|agdb_derive|agdb_api|api.rs", "--show-missing-lines"],
"type": "shell",
"command": "bash",
"args": [
"-c",
"cargo llvm-cov clean --workspace && cargo llvm-cov -p agdb_server --all-features --no-report && cargo llvm-cov -p agdb_server --all-features --no-report -- --ignored && cargo llvm-cov report --ignore-filename-regex \"agdb(.|..)src|agdb_derive|agdb_api|api.rs\" --show-missing-lines"
],
"problemMatcher": ["$rustc"],
"group": "build"
},
{
"label": "Coverage Server HTML",
"type": "process",
"command": "cargo",
"args": ["llvm-cov", "--package", "agdb_server", "--all-features", "--ignore-filename-regex", "agdb(.|..)src|agdb_derive|agdb_api|api.rs", "--html", "--open"],
"type": "shell",
"command": "bash",
"args": [
"-c",
"cargo llvm-cov clean --workspace && cargo llvm-cov -p agdb_server --all-features --no-report && cargo llvm-cov -p agdb_server --all-features --no-report -- --ignored && cargo llvm-cov report --ignore-filename-regex \"agdb(.|..)src|agdb_derive|agdb_api|api.rs\" --html --open"
],
"problemMatcher": ["$rustc"],
"group": "build"
},
Expand Down
2 changes: 1 addition & 1 deletion agdb/src/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ mod tests {
.into(),
QueryBuilder::remove().ids("node1").query().into(),
];
format!("{:?}", queries);
let _ = format!("{:?}", queries);
assert_eq!(queries, queries);
}
}
2 changes: 1 addition & 1 deletion agdb_api/rust/src/api_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ mod tests {

#[test]
fn derived_from_debug() {
format!(
let _ = format!(
"{:?}",
AgdbApiError {
status: 0,
Expand Down
32 changes: 23 additions & 9 deletions agdb_api/rust/src/api_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ pub struct ChangePassword {
pub new_password: String,
}

#[derive(Debug, Deserialize, Serialize, ToSchema, PartialEq)]
#[derive(Debug, Default, Clone, Deserialize, Serialize, ToSchema, PartialEq)]
pub struct ClusterStatus {
pub address: String,
pub status: bool,
Expand Down Expand Up @@ -186,16 +186,16 @@ mod tests {

#[test]
fn derived_from_debug() {
format!("{:?}", DbType::Memory);
format!("{:?}", DbUserRole::Admin);
format!(
let _ = format!("{:?}", DbType::Memory);
let _ = format!("{:?}", DbUserRole::Admin);
let _ = format!(
"{:?}",
DbUser {
user: "user".to_string(),
role: DbUserRole::Admin
}
);
format!(
let _ = format!(
"{:?}",
ServerDatabase {
name: "db".to_string(),
Expand All @@ -205,22 +205,22 @@ mod tests {
backup: 0
}
);
format!(
let _ = format!(
"{:?}",
UserStatus {
name: "user".to_string()
}
);
format!(
let _ = format!(
"{:?}",
QueryAudit {
timestamp: 0,
user: "user".to_string(),
query: QueryType::SelectIndexes(SelectIndexesQuery {})
}
);
format!("{:?}", DbAudit(vec![]));
format!(
let _ = format!("{:?}", DbAudit(vec![]));
let _ = format!(
"{:?}",
ClusterStatus {
address: "localhost".to_string(),
Expand Down Expand Up @@ -314,4 +314,18 @@ mod tests {

assert_eq!(status.cmp(&status), std::cmp::Ordering::Equal);
}

#[test]
fn derived_from_serde() {
let cs1 = ClusterStatus {
address: "localhost".to_string(),
status: true,
leader: false,
term: 0,
commit: 0,
};
let data = serde_json::to_string(&cs1).unwrap();
let cs2: ClusterStatus = serde_json::from_str(&data).unwrap();
assert_eq!(cs1, cs2);
}
}
162 changes: 162 additions & 0 deletions agdb_server/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,3 +124,165 @@ impl Modify for BearerToken {
}
}
}

#[cfg(test)]
mod tests {
use super::*;
use agdb::Comparison;
use agdb::CountComparison;
use agdb::DbKeyOrder;
use agdb::QueryBuilder;
use agdb::QueryId;
use agdb::QueryType;
use agdb::UserValue;
use std::fs::File;
use std::io::Write;

macro_rules! queries {
($($x:expr),+ $(,)?) => {
{
let mut vec: Vec<(String, QueryType)> = Vec::new();
$(
{
let mut as_string = stringify!($x).to_string();
as_string.retain(|c| !c.is_whitespace());
vec.push((as_string, $x.into()));
}
)*
vec
}
};
}

#[derive(Default, UserValue)]
struct T {
db_id: Option<QueryId>,
value1: String,
value2: i64,
}

#[test]
fn openapi() {
let schema = Api::openapi().to_pretty_json().unwrap();
let mut file = File::create("openapi.json").unwrap();
file.write_all(schema.as_bytes()).unwrap();
}

#[test]
fn test_queries() {
#[rustfmt::skip]
let queries = queries![
QueryBuilder::insert().aliases("a").ids(1).query(),
QueryBuilder::insert().aliases("a").ids("b").query(),
QueryBuilder::insert().aliases(vec!["a", "b"]).ids(vec![1, 2]).query(),
QueryBuilder::insert().edges().from(1).to(2).query(),
QueryBuilder::insert().edges().from("a").to("b").query(),
QueryBuilder::insert().edges().from("a").to(vec![1, 2]).query(),
QueryBuilder::insert().edges().from(vec![1, 2]).to(vec![2, 3]).query(),
QueryBuilder::insert().edges().from(vec![1, 2]).to(vec![2, 3]).each().query(),
QueryBuilder::insert().edges().from(vec![1, 2]).to(vec![2, 3]).each().values(vec![vec![("k", 1).into()], vec![("k", 2).into()]]).query(),
QueryBuilder::insert().edges().from(vec![1, 2]).to(vec![2, 3]).each().values_uniform(vec![("k", 1).into(), (1, 10).into()]).query(),
QueryBuilder::insert().edges().from("a").to(vec![1, 2]).values(vec![vec![("k", 1).into()], vec![("k", 2).into()]]).query(),
QueryBuilder::insert().edges().from("a").to(vec![1, 2]).values_uniform(vec![("k", "v").into(), (1, 10).into()]).query(),
QueryBuilder::insert().edges().from(QueryBuilder::search().from("a").where_().node().query()).to(QueryBuilder::search().from("b").where_().node().query()).query(),
QueryBuilder::insert().edges().from(QueryBuilder::search().from("a").where_().node().query()).to(QueryBuilder::search().from("b").where_().node().query()).values(vec![vec![("k", 1).into()], vec![("k", 2).into()]]).query(),
QueryBuilder::insert().edges().from(QueryBuilder::search().from("a").where_().node().query()).to(QueryBuilder::search().from("b").where_().node().query()).values_uniform(vec![("k", "v").into(), (1, 10).into()]).query(),
QueryBuilder::insert().edges().ids(-3).from(1).to(2).query(),
QueryBuilder::insert().edges().ids(vec![-3, -4]).from(1).to(2).query(),
QueryBuilder::insert().edges().ids(QueryBuilder::search().from(1).where_().edge().query()).from(1).to(2).query(),
QueryBuilder::insert().index("key").query(),
QueryBuilder::insert().nodes().count(2).query(),
QueryBuilder::insert().nodes().count(2).values_uniform(vec![("k", "v").into(), (1, 10).into()]).query(),
QueryBuilder::insert().nodes().aliases(vec!["a", "b"]).query(),
QueryBuilder::insert().nodes().aliases(vec!["a", "b"]).values(vec![vec![("k", 1).into()], vec![("k", 2).into()]]).query(),
QueryBuilder::insert().nodes().aliases(vec!["a", "b"]).values_uniform(vec![("k", "v").into(), (1, 10).into()]).query(),
QueryBuilder::insert().nodes().values(vec![vec![("k", 1).into()], vec![("k", 2).into()]]).query(),
QueryBuilder::insert().nodes().ids(1).count(1).query(),
QueryBuilder::insert().nodes().ids(vec![1, 2]).count(1).query(),
QueryBuilder::insert().nodes().ids("a").count(1).query(),
QueryBuilder::insert().nodes().ids("a").aliases("a").query(),
QueryBuilder::insert().nodes().ids(vec!["a", "b"]).count(1).query(),
QueryBuilder::insert().nodes().ids(vec![1, 2]).values(vec![vec![("k", "v").into()], vec![(1, 10).into()]]).query(),
QueryBuilder::insert().nodes().ids(vec![1, 2]).values_uniform(vec![("k", "v").into(), (1, 10).into()]).query(),
QueryBuilder::insert().nodes().ids(QueryBuilder::search().from(1).query()).count(1).query(),
QueryBuilder::insert().element(&T::default()).query(),
QueryBuilder::insert().elements(&[T::default(), T::default()]).query(),
QueryBuilder::insert().values(vec![vec![("k", "v").into(), (1, 10).into()], vec![("k", 2).into()]]).ids(vec![1, 2]).query(),
QueryBuilder::insert().values(vec![vec![("k", "v").into(), (1, 10).into()], vec![("k", 2).into()]]).ids(QueryBuilder::search().from("a").query()).query(),
QueryBuilder::insert().values_uniform(vec![("k", "v").into(), (1, 10).into()]).ids(vec![1, 2]).query(),
QueryBuilder::insert().values_uniform(vec![("k", "v").into(), (1, 10).into()]).ids(QueryBuilder::search().from("a").query()).query(),
QueryBuilder::remove().aliases("a").query(),
QueryBuilder::remove().aliases(vec!["a", "b"]).query(),
QueryBuilder::remove().ids(1).query(),
QueryBuilder::remove().ids("a").query(),
QueryBuilder::remove().ids(vec![1, 2]).query(),
QueryBuilder::remove().ids(vec!["a", "b"]).query(),
QueryBuilder::remove().ids(QueryBuilder::search().from("a").query()).query(),
QueryBuilder::remove().index("key").query(),
QueryBuilder::remove().values(vec!["k1".into(), "k2".into()]).ids(vec![1, 2]).query(),
QueryBuilder::remove().values(vec!["k1".into(), "k2".into()]).ids(QueryBuilder::search().from("a").query()).query(),
QueryBuilder::select().aliases().ids(vec![1, 2]).query(),
QueryBuilder::select().aliases().ids(QueryBuilder::search().from(1).query()).query(),
QueryBuilder::select().aliases().query(),
QueryBuilder::select().edge_count().ids(vec![1, 2]).query(),
QueryBuilder::select().edge_count_from().ids(vec![1, 2]).query(),
QueryBuilder::select().edge_count_to().ids(vec![1, 2]).query(),
QueryBuilder::select().ids("a").query(),
QueryBuilder::select().ids(vec![1, 2]).query(),
QueryBuilder::select().ids(QueryBuilder::search().from(1).query()).query(),
QueryBuilder::select().indexes().query(),
QueryBuilder::select().keys().ids("a").query(),
QueryBuilder::select().keys().ids(vec![1, 2]).query(),
QueryBuilder::select().keys().ids(QueryBuilder::search().from(1).query()).query(),
QueryBuilder::select().key_count().ids("a").query(),
QueryBuilder::select().key_count().ids(vec![1, 2]).query(),
QueryBuilder::select().key_count().ids(QueryBuilder::search().from(1).query()).query(),
QueryBuilder::select().node_count().query(),
QueryBuilder::select().values(vec!["k".into(), "k2".into()]).ids("a").query(),
QueryBuilder::select().values(vec!["k".into(), "k2".into()]).ids(vec![1, 2]).query(),
QueryBuilder::select().values(vec!["k".into(), "k2".into()]).ids(QueryBuilder::search().from(1).query()).query(),
QueryBuilder::search().from("a").query(),
QueryBuilder::search().to(1).query(),
QueryBuilder::search().from("a").to("b").query(),
QueryBuilder::search().breadth_first().from("a").query(),
QueryBuilder::search().depth_first().to(1).query(),
QueryBuilder::search().depth_first().from("a").query(),
QueryBuilder::search().elements().query(),
QueryBuilder::search().index("age").value(20).query(),
QueryBuilder::search().from(1).order_by(vec![DbKeyOrder::Desc("age".into()), DbKeyOrder::Asc("name".into())]).query(),
QueryBuilder::search().from(1).offset(10).query(),
QueryBuilder::search().from(1).limit(5).query(),
QueryBuilder::search().from(1).order_by(vec![DbKeyOrder::Desc("k".into())]).offset(10).query(),
QueryBuilder::search().from(1).order_by(vec![DbKeyOrder::Desc("k".into())]).limit(5).query(),
QueryBuilder::search().from(1).order_by(vec![DbKeyOrder::Desc("k".into())]).offset(10).limit(5).query(),
QueryBuilder::search().from(1).offset(10).limit(5).query(),
QueryBuilder::search().from(1).where_().distance(CountComparison::LessThan(3)).query(),
QueryBuilder::search().from(1).where_().edge().query(),
QueryBuilder::search().from(1).where_().edge_count(CountComparison::GreaterThan(2)).query(),
QueryBuilder::search().from(1).where_().edge_count_from(CountComparison::Equal(1)).query(),
QueryBuilder::search().from(1).where_().edge_count_to(CountComparison::NotEqual(1)).query(),
QueryBuilder::search().from(1).where_().node().query(),
QueryBuilder::search().from(1).where_().key("k").value(Comparison::Equal(1.into())).query(),
QueryBuilder::search().from(1).where_().keys(vec!["k1".into(), "k2".into()]).query(),
QueryBuilder::search().from(1).where_().not().keys(vec!["k1".into(), "k2".into()]).query(),
QueryBuilder::search().from(1).where_().ids(vec![1, 2]).query(),
QueryBuilder::search().from(1).where_().beyond().keys(vec!["k".into()]).query(),
QueryBuilder::search().from(1).where_().not().ids(vec![1, 2]).query(),
QueryBuilder::search().from(1).where_().not_beyond().ids("a").query(),
QueryBuilder::search().from(1).where_().node().or().edge().query(),
QueryBuilder::search().from(1).where_().node().and().distance(CountComparison::GreaterThanOrEqual(3)).query(),
QueryBuilder::search().from(1).where_().node().or().where_().edge().and().key("k").value(Comparison::Equal(1.into())).end_where().query(),
QueryBuilder::search().from(1).where_().node().or().where_().edge().and().key("k").value(Comparison::Contains(1.into())).end_where().query(),
QueryBuilder::search().from(1).where_().node().or().where_().edge().and().key("k").value(Comparison::Contains((vec![1, 2]).into())).end_where().query(),
QueryBuilder::search().from(1).order_by(vec![DbKeyOrder::Asc("k".into())]).where_().node().query(),
QueryBuilder::search().from(1).limit(1).where_().node().query(),
QueryBuilder::search().from(1).offset(1).where_().node().query(),
QueryBuilder::search().to(1).offset(1).query(),
QueryBuilder::search().to(1).limit(1).query(),
QueryBuilder::search().to(1).where_().node().query(),
QueryBuilder::search().to(1).order_by(vec![DbKeyOrder::Asc("k".into())]).where_().node().query()
];

serde_json::to_writer_pretty(File::create("test_queries.json").unwrap(), &queries).unwrap();
}
}
3 changes: 2 additions & 1 deletion agdb_server/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,9 @@ pub(crate) fn app(
.route("/cluster/status", routing::get(routes::cluster::status))
.route(
"/cluster/heartbeat",
routing::get(routes::cluster::heartbeat),
routing::post(routes::cluster::heartbeat),
)
.route("/cluster/vote", routing::get(routes::cluster::vote))
.route("/user/login", routing::post(routes::user::login))
.route("/user/logout", routing::post(routes::user::logout))
.route(
Expand Down
Loading

0 comments on commit 67c0033

Please sign in to comment.