From b0f8abef33e8558d818edb3b2d84218fd8878eed Mon Sep 17 00:00:00 2001
From: Paul Makles <paulmakles@gmail.com>
Date: Mon, 3 Aug 2020 10:51:14 +0200
Subject: [PATCH] Implement basic caching.

---
 Cargo.lock              | 50 +++++++++++++++++++++++
 Cargo.toml              |  2 +
 src/database/channel.rs | 89 ++++++++++++++++++++++++++++++++++++-----
 src/guards/channel.rs   | 17 +++++++-
 src/main.rs             |  6 ++-
 5 files changed, 150 insertions(+), 14 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index d2f4fff..ded248a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -13,6 +13,14 @@ name = "adler"
 version = "0.2.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
+[[package]]
+name = "ahash"
+version = "0.2.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "const-random 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "ahash"
 version = "0.3.8"
@@ -241,6 +249,24 @@ dependencies = [
  "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
+[[package]]
+name = "const-random"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "const-random-macro 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "proc-macro-hack 0.5.16 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "const-random-macro"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
+ "proc-macro-hack 0.5.16 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "cookie"
 version = "0.11.3"
@@ -554,6 +580,15 @@ dependencies = [
  "tracing 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
+[[package]]
+name = "hashbrown"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "ahash 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)",
+ "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "hashbrown"
 version = "0.7.2"
@@ -827,6 +862,14 @@ dependencies = [
  "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
+[[package]]
+name = "lru"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "hashbrown 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "lru-cache"
 version = "0.1.2"
@@ -1512,6 +1555,8 @@ dependencies = [
  "chrono 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)",
  "dotenv 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "hashbrown 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lru 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "mongodb 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "num_enum 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "once_cell 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2507,6 +2552,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 [metadata]
 "checksum addr2line 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1b6a2d3371669ab3ca9797670853d61402b03d0b4b9ebf33d677dfa720203072"
 "checksum adler 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e"
+"checksum ahash 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "6f33b5018f120946c1dcf279194f238a9f146725593ead1c08fa47ff22b0b5d3"
 "checksum ahash 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "e8fd72866655d1904d6b0997d0b07ba561047d070fbe29de039031c641b61217"
 "checksum aho-corasick 0.7.13 (registry+https://github.com/rust-lang/crates.io-index)" = "043164d8ba5c4c3035fec9bbee8647c0261d788f3474306f93bb65901cae0e86"
 "checksum async-trait 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)" = "a265e3abeffdce30b2e26b7a11b222fe37c6067404001b434101457d0385eb92"
@@ -2537,6 +2583,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 "checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
 "checksum chrono 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)" = "c74d84029116787153e02106bf53e66828452a4b325cc8652b788b5967c0a0b6"
 "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
+"checksum const-random 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "2f1af9ac737b2dd2d577701e59fd09ba34822f6f2ebdb30a7647405d9e55e16a"
+"checksum const-random-macro 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "25e4c606eb459dd29f7c57b2e0879f2b6f14ee130918c2b78ccb58a9624e6c7a"
 "checksum cookie 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "5795cda0897252e34380a27baf884c53aa7ad9990329cdad96d4c5d027015d44"
 "checksum core-foundation 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171"
 "checksum core-foundation-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac"
@@ -2575,6 +2623,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 "checksum gimli 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aaf91faf136cb47367fa430cd46e37a788775e7fa104f8b4bcb3861dc389b724"
 "checksum glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
 "checksum h2 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "993f9e0baeed60001cf565546b0d3dbe6a6ad23f2bd31644a133c641eccf6d53"
+"checksum hashbrown 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8e6073d0ca812575946eb5f35ff68dbe519907b25c42530389ff946dc84c6ead"
 "checksum hashbrown 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "96282e96bfcd3da0d3aa9938bedf1e50df3269b6db08b4876d2da0bb1a0841cf"
 "checksum hashbrown 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "34f595585f103464d8d2f6e9864682d74c1601fed5e07d62b1c9058dba8246fb"
 "checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205"
@@ -2607,6 +2656,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 "checksum linked-hash-map 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a"
 "checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b"
 "checksum log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b"
+"checksum lru 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "35c456c123957de3a220cd03786e0d86aa542a88b46029973b542f426da6ef34"
 "checksum lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c"
 "checksum match_cfg 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4"
 "checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08"
diff --git a/Cargo.toml b/Cargo.toml
index ca6a8e2..2ccdd24 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -27,3 +27,5 @@ hashbrown = "0.7.1"
 serde_json = "1.0.51"
 rocket_cors = "0.5.2"
 bitfield = "0.13.2"
+lru = "0.5.3"
+lazy_static = "1.4.0"
diff --git a/src/database/channel.rs b/src/database/channel.rs
index d74b477..5da3f91 100644
--- a/src/database/channel.rs
+++ b/src/database/channel.rs
@@ -1,33 +1,102 @@
+use crate::database::get_collection;
+
 use serde::{Deserialize, Serialize};
+use std::sync::{Arc, Mutex};
+use lru::LruCache;
+use bson::{doc, from_bson};
 
 #[derive(Serialize, Deserialize, Debug, Clone)]
 pub struct LastMessage {
+    // message id
     id: String,
+    // author's id
     user_id: String,
+    // truncated content with author's name prepended (for GDM / GUILD)
     short_content: String,
 }
 
-#[derive(Serialize, Deserialize, Debug)]
+#[derive(Serialize, Deserialize, Debug, Clone)]
 pub struct Channel {
     #[serde(rename = "_id")]
     pub id: String,
     #[serde(rename = "type")]
     pub channel_type: u8,
 
-    // for Direct Messages
+    // DM: whether the DM is active
     pub active: Option<bool>,
-
-    // for DMs / GDMs
+    // DM + GDM: last message in channel
     pub last_message: Option<LastMessage>,
+    // DM + GDM: recipients for channel
     pub recipients: Option<Vec<String>>,
-
-    // for GDMs
+    // GDM: owner of group
     pub owner: Option<String>,
-
-    // for Guilds
+    // GUILD: channel parent
     pub guild: Option<String>,
-
-    // for Guilds and Group DMs
+    // GUILD + GDM: channel name
     pub name: Option<String>,
+    // GUILD + GDM: channel description
     pub description: Option<String>,
 }
+
+lazy_static! {
+    static ref CACHE: Arc<Mutex<LruCache<String, Channel>>> = Arc::new(Mutex::new(LruCache::new(4_000_000)));
+}
+
+/*pub fn test() {
+    use std::time::Instant;
+
+    let now = Instant::now();
+    let mut cache = CACHE.lock().unwrap();
+    println!("I'm about to write 4 million entries to cache.");
+    for i in 0..4_000_000 {
+        let c = Channel {
+            id: "potato".to_string(),
+            channel_type: 0,
+    
+            active: None,
+            last_message: None,
+            description: None,
+            guild: None,
+            name: None,
+            owner: None,
+            recipients: None
+        };
+
+        cache.put(format!("{}", i), c);
+    }
+
+    println!("It took {} seconds, roughly {}ms per entry.", now.elapsed().as_secs_f64(), now.elapsed().as_millis() as f64 / 1_000_000.0);
+
+    let now = Instant::now();
+    println!("Now I'm going to read every entry and immediately dispose of it.");
+    for i in 0..4_000_000 {
+        cache.get(&format!("{}", i));
+    }
+
+    println!("It took {} seconds, roughly {}ms per entry.", now.elapsed().as_secs_f64(), now.elapsed().as_millis() as f64 / 1_000_000.0);
+}*/
+
+pub fn fetch_channel(id: &str) -> Channel {
+    {
+        let mut cache = CACHE.lock().unwrap();
+        let existing = cache.get(&id.to_string());
+
+        if let Some(channel) = existing {
+            return (*channel).clone();
+        }
+    }
+
+    let col = get_collection("channels");
+    let result = col.find_one(doc! { "_id": id }, None).unwrap();
+
+    if let Some(doc) = result {
+        let channel: Channel = from_bson(bson::Bson::Document(doc)).expect("Failed to unwrap channel.");
+
+        let mut cache = CACHE.lock().unwrap();
+        cache.put(id.to_string(), channel.clone());
+
+        return channel;
+    }
+
+    panic!("Channel does not exist!");
+}
diff --git a/src/guards/channel.rs b/src/guards/channel.rs
index ace3096..171136e 100644
--- a/src/guards/channel.rs
+++ b/src/guards/channel.rs
@@ -27,7 +27,20 @@ pub struct ChannelRef {
 
 impl ChannelRef {
     pub fn from(id: String) -> Option<ChannelRef> {
-        match database::get_collection("channels").find_one(
+        let channel = database::channel::fetch_channel(&id);
+        Some(ChannelRef {
+            id: channel.id,
+            channel_type: channel.channel_type,
+
+            name: channel.name,
+            last_message: channel.last_message,
+            
+            recipients: channel.recipients,
+            guild: channel.guild,
+            owner: channel.owner
+        })
+
+        /*match database::get_collection("channels").find_one(
             doc! { "_id": id },
             FindOneOptions::builder()
                 .projection(doc! {
@@ -48,7 +61,7 @@ impl ChannelRef {
                 None => None,
             },
             Err(_) => None,
-        }
+        }*/
     }
 
     pub fn fetch_data(&self, projection: Document) -> Option<Document> {
diff --git a/src/main.rs b/src/main.rs
index 2b11c3b..2170332 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -6,12 +6,14 @@ extern crate rocket;
 extern crate rocket_contrib;
 #[macro_use]
 extern crate bitfield;
+#[macro_use]
+extern crate lazy_static;
 
+pub mod notifications;
 pub mod database;
-pub mod email;
 pub mod guards;
-pub mod notifications;
 pub mod routes;
+pub mod email;
 pub mod util;
 
 use dotenv;
-- 
GitLab