diff --git a/Cargo.lock b/Cargo.lock index 44c1fab..7a3ee7c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -36,6 +36,8 @@ dependencies = [ name = "api" version = "0.1.0" dependencies = [ + "once_cell", + "redis", "salvo", "serde", "serde_json", @@ -133,6 +135,16 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "combine" +version = "4.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" +dependencies = [ + "bytes", + "memchr", +] + [[package]] name = "cookie" version = "0.16.1" @@ -757,6 +769,20 @@ dependencies = [ "rand_core", ] +[[package]] +name = "redis" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "513b3649f1a111c17954296e4a3b9eecb108b766c803e2b99f179ebe27005985" +dependencies = [ + "combine", + "itoa", + "percent-encoding", + "ryu", + "sha1_smol", + "url", +] + [[package]] name = "redox_syscall" version = "0.2.16" @@ -934,6 +960,12 @@ dependencies = [ "digest", ] +[[package]] +name = "sha1_smol" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" + [[package]] name = "slab" version = "0.4.7" diff --git a/README.md b/README.md index d47d489..730ab5f 100644 --- a/README.md +++ b/README.md @@ -70,5 +70,6 @@ This API only supports GET, and supports CORS. - Serves https://saerro.harasse.rs - Redis - Using ZADD with score as timestamp, ZCOUNTBYSCORE by timestamp in 15 minute windows, and cleaned up with SCAN+ZREMBYSCORE, population data is tracked. + - There is deliberately no persistence. - Redis "Tender" - Cleans up Redis every 5 mins. diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..da086b0 --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,10 @@ +version: "3" + +services: + redis: + image: redis:alpine + command: redis-server --save "" --appendonly no + container_name: redis + restart: always + ports: + - "6379:6379" \ No newline at end of file diff --git a/services/api/Cargo.toml b/services/api/Cargo.toml index 400744c..72d2f5b 100644 --- a/services/api/Cargo.toml +++ b/services/api/Cargo.toml @@ -9,4 +9,6 @@ edition = "2021" salvo = { version = "0.37.4", features = ["cors"] } tokio = { version = "1.22.0", features = ["macros"] } serde_json = "1.0.88" -serde = "1.0.147" \ No newline at end of file +serde = "1.0.147" +redis = "0.22.1" +once_cell = "1.16.0" \ No newline at end of file diff --git a/services/api/src/main.rs b/services/api/src/main.rs index a23981d..7a3a417 100644 --- a/services/api/src/main.rs +++ b/services/api/src/main.rs @@ -1,3 +1,7 @@ +use core::time; +use std::{ops::Sub, time::SystemTime}; + +use once_cell::sync::Lazy; use salvo::cors::Cors; use salvo::prelude::*; use serde::{Deserialize, Serialize}; @@ -29,6 +33,11 @@ struct MultipleWorldPopulation { worlds: Vec, } +pub static REDIS_CLIENT: Lazy = Lazy::new(|| { + redis::Client::open(std::env::var("REDIS_ADDR").unwrap_or("redis://localhost:6379".to_string())) + .unwrap() +}); + #[handler] async fn info(req: &mut Request, res: &mut Response) { let headers: IncomingHeaders = req.parse_headers().unwrap(); @@ -78,14 +87,27 @@ async fn get_world_multi(req: &mut Request, res: &mut Response) { } async fn get_world_pop(world_id: String) -> WorldPopulation { + let mut con = REDIS_CLIENT.get_connection().unwrap(); + + let filter_timestamp = SystemTime::now() + .sub(time::Duration::from_secs(60 * 15)) + .duration_since(SystemTime::UNIX_EPOCH) + .unwrap() + .as_secs(); + + let (tr, vs, nc): (u32, u32, u32) = redis::pipe() + .zcount(format!("{}/{}", world_id, 1), filter_timestamp, "+inf") + .zcount(format!("{}/{}", world_id, 2), filter_timestamp, "+inf") + .zcount(format!("{}/{}", world_id, 3), filter_timestamp, "+inf") + .query(&mut con) + .unwrap(); + + let total = tr + vs + nc; + let response = WorldPopulation { world_id: world_id.parse().unwrap(), - total: 0, - factions: Factions { - tr: 0, - nc: 0, - vs: 0, - }, + total, + factions: Factions { tr, nc, vs }, }; response