├── memclt ├── .gitignore ├── Cargo.toml └── src │ └── main.rs ├── memcrs ├── src │ ├── server │ │ ├── mod.rs │ │ └── timer.rs │ ├── memory_store │ │ ├── mod.rs │ │ └── store.rs │ ├── memcache │ │ ├── cli │ │ │ ├── mod.rs │ │ │ └── parser.rs │ │ ├── eviction_policy.rs │ │ ├── mod.rs │ │ ├── builder.rs │ │ ├── random_policy.rs │ │ ├── store.rs │ │ └── store │ │ │ └── storage_tests.rs │ ├── cache │ │ ├── mod.rs │ │ ├── error.rs │ │ └── cache.rs │ ├── mock │ │ ├── mod.rs │ │ ├── value.rs │ │ └── mock_server.rs │ ├── protocol │ │ ├── mod.rs │ │ ├── binary_connection.rs │ │ ├── binary.rs │ │ └── binary_codec │ │ │ └── binary_encoder_tests.rs │ ├── version.rs │ ├── memcache_server │ │ ├── mod.rs │ │ ├── runtime_builder.rs │ │ ├── memc_tcp.rs │ │ └── client_handler.rs │ ├── lib.rs │ └── bin │ │ └── memcrsd.rs ├── fuzz │ ├── .gitignore │ ├── fuzz_targets │ │ └── fuzz_decode_binary.rs │ ├── Cargo.toml │ └── Cargo.lock ├── fuzz.sh ├── coverage.sh ├── Cargo.toml └── benchmarks │ ├── 13_07_2020.md │ ├── 19_02_2020.txt~ │ ├── 19_02_2020.txt │ ├── 06_12_2020.md │ ├── 05_12_2020.md │ ├── 10_08_2020.md │ ├── 23_02_2020.txt │ ├── 07_08_2020.md │ └── 07_12_2020.md ├── .gitignore ├── .vscode ├── settings.json ├── tasks.json └── launch.json ├── Cargo.toml ├── LICENSE.md ├── Code_of_Conduct.md ├── Dockerfile ├── COMM-LICENSE.md ├── .github └── workflows │ └── rust.yml ├── doc └── benchmarks.txt ├── README.md ├── CONTRIBUTING.md └── Cargo.lock /memclt/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /memcrs/src/server/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod timer; -------------------------------------------------------------------------------- /memcrs/src/memory_store/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod store; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | memcrs/target -------------------------------------------------------------------------------- /memcrs/src/memcache/cli/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod parser; 2 | -------------------------------------------------------------------------------- /memcrs/src/cache/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod error; 2 | pub mod cache; 3 | -------------------------------------------------------------------------------- /memcrs/src/mock/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod mock_server; 2 | pub mod value; 3 | -------------------------------------------------------------------------------- /memcrs/fuzz/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | target 3 | corpus 4 | artifacts 5 | coverage -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust.clippy_preference": "on", 3 | "rust.jobs": 8 4 | } -------------------------------------------------------------------------------- /memcrs/src/protocol/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod binary; 2 | pub mod binary_codec; 3 | pub mod binary_connection; 4 | -------------------------------------------------------------------------------- /memcrs/src/version.rs: -------------------------------------------------------------------------------- 1 | use clap::crate_version; 2 | 3 | pub static MEMCRS_VERSION: &str = crate_version!(); 4 | -------------------------------------------------------------------------------- /memcrs/src/memcache/eviction_policy.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug)] 2 | pub enum EvictionPolicy { 3 | None, 4 | Random, 5 | } 6 | -------------------------------------------------------------------------------- /memcrs/src/memcache_server/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod client_handler; 2 | pub mod handler; 3 | pub mod memc_tcp; 4 | pub mod runtime_builder; 5 | -------------------------------------------------------------------------------- /memcrs/src/memcache/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod builder; 2 | pub mod cli; 3 | pub mod eviction_policy; 4 | pub mod random_policy; 5 | pub mod store; 6 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | 3 | 4 | members = [ 5 | "memcrs" 6 | #"memclt" 7 | ] 8 | 9 | [profile.release] 10 | opt-level = 3 11 | debug = false 12 | lto = true 13 | panic = 'unwind' 14 | -------------------------------------------------------------------------------- /memcrs/fuzz.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | export RUST_BACKTRACE=full 4 | #export RUSTFLAGS="-Znew-llvm-pass-manager=no" 5 | cargo +nightly fuzz run -j 8 fuzz_binary_decoder -- -rss_limit_mb=4192 -timeout=60 6 | 7 | -------------------------------------------------------------------------------- /memcrs/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate log; 3 | 4 | extern crate num_derive; 5 | pub mod memcache; 6 | pub mod protocol; 7 | pub mod memcache_server; 8 | pub mod cache; 9 | pub mod server; 10 | pub mod version; 11 | pub mod memory_store; 12 | 13 | #[cfg(test)] 14 | mod mock; 15 | -------------------------------------------------------------------------------- /memclt/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "memclt" 3 | version = "0.1.0" 4 | authors = ["Dariusz Ostolski "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | memcache = "0.16.0" 11 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) Dariusz Ostolski 2 | 3 | Memcrs is an Open Source project licensed under the terms of 4 | the AGPL license. Please see 5 | for license text. 6 | 7 | Memcrs Pro has a commercial-friendly license allowing private forks 8 | and modifications of Memcrs. You can find the commercial license terms in COMM-LICENSE.md. 9 | -------------------------------------------------------------------------------- /memcrs/coverage.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | export CARGO_INCREMENTAL=0 4 | export RUSTFLAGS="-Zprofile -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort" 5 | export RUSTDOCFLAGS="-Cpanic=abort" 6 | cargo clean 7 | cargo +nightly test 8 | grcov ../target/debug/ -s . -t html --llvm --branch --ignore-not-existing -o ../target/debug/coverage/ 9 | -------------------------------------------------------------------------------- /Code_of_Conduct.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | The memc.rs project adheres to the [Rust Code of Conduct](https://www.rust-lang.org/policies/code-of-conduct). This describes the minimum behavior expected from all contributors. 4 | 5 | ## Enforcement 6 | 7 | Instances of violations of the Code of Conduct can be reported by contacting the project team at [dariusz.ostolski@gmail.com](mailto:dariusz.ostolski@gmail.com). 8 | -------------------------------------------------------------------------------- /memcrs/fuzz/fuzz_targets/fuzz_decode_binary.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | use libfuzzer_sys::fuzz_target; 3 | extern crate memcrs; 4 | use bytes::{BytesMut, BufMut}; 5 | use tokio_util::codec::{Decoder}; 6 | 7 | fuzz_target!(|data: &[u8]| { 8 | // fuzzed code goes here 9 | let mut codec = memcrs::protocol::binary_codec::MemcacheBinaryCodec::new(1024); 10 | let mut src = BytesMut::with_capacity(data.len()); 11 | src.put(data); 12 | let _ = codec.decode(&mut src); 13 | }); 14 | -------------------------------------------------------------------------------- /memcrs/src/mock/value.rs: -------------------------------------------------------------------------------- 1 | use crate::cache::cache::ValueType; 2 | use bytes::{BufMut, BytesMut}; 3 | use std::str; 4 | 5 | pub fn from_string(val: &str) -> ValueType { 6 | let mut value = BytesMut::with_capacity(val.as_bytes().len()); 7 | value.put_slice(val.as_bytes()); 8 | value.freeze() 9 | } 10 | 11 | pub fn from_slice(val: &[u8]) -> ValueType { 12 | let mut value = BytesMut::with_capacity(val.len()); 13 | value.put_slice(val); 14 | value.freeze() 15 | } 16 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "type": "cargo", 6 | "command": "build", 7 | "problemMatcher": [ 8 | "$rustc" 9 | ], 10 | "group": { 11 | "kind": "build", 12 | "isDefault": true 13 | }, 14 | "label": "rust: cargo build" 15 | }, 16 | { 17 | "type": "cargo", 18 | "command": "build", 19 | "args": [ 20 | "--release" 21 | ], 22 | "problemMatcher": [ 23 | "$rustc" 24 | ], 25 | "group": "build", 26 | "label": "rust: cargo build release" 27 | } 28 | ] 29 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Run Test Debugger", 9 | "type": "lldb", 10 | "request": "launch", 11 | "program": "${workspaceFolder}/target/debug/memcrsd", 12 | "args": ["-vvv"], 13 | "cwd": "${workspaceFolder}" 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /memcrs/fuzz/Cargo.toml: -------------------------------------------------------------------------------- 1 | 2 | [package] 3 | name = "memcrs-fuzz" 4 | version = "0.0.1" 5 | authors = ["Automatically generated"] 6 | publish = false 7 | edition = "2021" 8 | 9 | [package.metadata] 10 | cargo-fuzz = true 11 | 12 | [dependencies] 13 | libfuzzer-sys = "0.4.6" 14 | bytes = "1.4.0" 15 | tokio-util = { version = "0.7.4", features = ["full"] } 16 | 17 | [dependencies.memcrs] 18 | path = ".." 19 | 20 | # Prevent this from interfering with workspaces 21 | [workspace] 22 | members = ["."] 23 | 24 | [[bin]] 25 | name = "fuzz_binary_decoder" 26 | path = "fuzz_targets/fuzz_decode_binary.rs" 27 | test = false 28 | doc = false 29 | 30 | [profile.release] 31 | debug=true 32 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Create the build container to compile the hello world program 2 | FROM rust:latest as builder 3 | ENV USER root 4 | RUN mkdir /build 5 | COPY . /build 6 | WORKDIR /build 7 | RUN rustup target add x86_64-unknown-linux-musl 8 | RUN apt update && apt install -y musl-tools musl-dev 9 | RUN apt install gcc 10 | RUN cargo build --release --target=x86_64-unknown-linux-musl --features=jemallocator 11 | 12 | # Create the execution container by copying the compiled hello world to it and running it 13 | FROM scratch 14 | COPY --from=builder /build/target/x86_64-unknown-linux-musl/release/memcrsd /memcrsd 15 | ENTRYPOINT [ "/memcrsd", "-c", "50000", "-l", "0.0.0.0", "-v", "-m", "2048", "-I", "10m" ] 16 | 17 | -------------------------------------------------------------------------------- /COMM-LICENSE.md: -------------------------------------------------------------------------------- 1 | END-USER LICENSE AGREEMENT 2 | 3 | ------------------------------------------------------------------------------ 4 | 5 | IMPORTANT: THIS SOFTWARE END-USER LICENSE AGREEMENT ("EULA") IS A LEGAL AGREEMENT (“Agreement”) BETWEEN YOU (THE CUSTOMER, EITHER AS AN INDIVIDUAL OR, IF PURCHASED OR OTHERWISE ACQUIRED BY OR FOR AN ENTITY, AS AN ENTITY) AND Dariusz Ostolski. READ IT CAREFULLY BEFORE COMPLETING THE INSTALLATION PROCESS AND USING Memcrs AND RELATED SOFTWARE COMPONENTS (“SOFTWARE”). IT PROVIDES A LICENSE TO USE THE SOFTWARE AND CONTAINS WARRANTY INFORMATION AND LIABILITY DISCLAIMERS. BY INSTALLING AND USING THE SOFTWARE, YOU ARE CONFIRMING YOUR ACCEPTANCE OF THE SOFTWARE AND AGREEING TO BECOME BOUND BY THE TERMS OF THIS AGREEMENT. 6 | 7 | ------------------------------------------------------------------------------ 8 | 9 | This project licensed under the terms of the AGPL license. Please see 10 | for license text. 11 | 12 | -------------------------------------------------------------------------------- /memcrs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "memcrs" 3 | version = "0.0.1" 4 | authors = ["Dariusz Ostolski "] 5 | edition = "2021" 6 | 7 | 8 | [lib] 9 | name = "memcrs" 10 | path = "src/lib.rs" 11 | 12 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 13 | 14 | [dependencies] 15 | byte-unit = "4.0.19" 16 | bytes = "1.4.0" 17 | clap = { version = "3.2.23", features = ["cargo"] } 18 | core_affinity = "0.8.0" 19 | dashmap = "5.4.0" 20 | futures = "0.3.27" 21 | futures-util = "0.3.27" 22 | log = "0.4.17" 23 | socket2 = { version = "0.4.9", features = ["all"] } 24 | num_cpus = "1.15.0" 25 | num-derive = "0.3.3" 26 | num-traits = "0.2.15" 27 | rand = { version = "0.8.5", features = ["small_rng"] } 28 | serde = "1.0.155" 29 | serde_derive = "1.0.155" 30 | tracing = "0.1.37" 31 | tracing-attributes = "0.1.23" 32 | tracing-log = "0.1.3" 33 | tracing-subscriber = { version = "0.3.16", default-features = false, features = ["std", "fmt"] } 34 | tokio = { version = "1.26.0", features = ["full"] } 35 | tokio-util = { version = "0.7.7", features = ["full"] } 36 | 37 | [target.'cfg(not(target_env = "msvc"))'.dependencies] 38 | jemallocator = { version ="0.5.0", optional = true } -------------------------------------------------------------------------------- /memcrs/src/server/timer.rs: -------------------------------------------------------------------------------- 1 | use log::debug; 2 | use std::sync::atomic::{AtomicU64, Ordering}; 3 | use std::time::Duration; 4 | use tokio::time::{interval_at, Instant}; 5 | 6 | pub trait Timer { 7 | fn timestamp(&self) -> u64; 8 | } 9 | 10 | pub trait SetableTimer { 11 | fn add_second(&self); 12 | } 13 | 14 | #[derive(Default)] 15 | pub struct SystemTimer { 16 | seconds: AtomicU64, 17 | } 18 | 19 | impl SystemTimer { 20 | pub fn new() -> Self { 21 | debug!("Creating system timer"); 22 | SystemTimer { 23 | seconds: AtomicU64::new(0), 24 | } 25 | } 26 | 27 | pub async fn run(&self) { 28 | let start = Instant::now(); 29 | let mut interval = interval_at(start, Duration::from_secs(1)); 30 | loop { 31 | interval.tick().await; 32 | self.add_second(); 33 | debug!("Server tick: {}", self.timestamp()); 34 | } 35 | } 36 | } 37 | 38 | impl Timer for SystemTimer { 39 | fn timestamp(&self) -> u64 { 40 | self.seconds.load(Ordering::Acquire) 41 | } 42 | } 43 | 44 | impl SetableTimer for SystemTimer { 45 | fn add_second(&self) { 46 | self.seconds.fetch_add(1, Ordering::Release); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /memcrs/src/memcache/builder.rs: -------------------------------------------------------------------------------- 1 | use super::eviction_policy::EvictionPolicy; 2 | use super::random_policy::RandomPolicy; 3 | use crate::cache::cache::{Cache}; 4 | use crate::memory_store::store::{MemoryStore}; 5 | use crate::server::timer; 6 | use std::sync::Arc; 7 | 8 | pub struct MemcacheStoreConfig { 9 | policy: EvictionPolicy, 10 | memory_limit: u64, 11 | } 12 | 13 | impl MemcacheStoreConfig { 14 | pub fn new(memory_limit: u64) -> MemcacheStoreConfig { 15 | MemcacheStoreConfig { 16 | policy: EvictionPolicy::None, 17 | memory_limit, 18 | } 19 | } 20 | } 21 | 22 | #[derive(Default)] 23 | pub struct MemcacheStoreBuilder {} 24 | 25 | impl MemcacheStoreBuilder { 26 | pub fn new() -> MemcacheStoreBuilder { 27 | MemcacheStoreBuilder {} 28 | } 29 | 30 | pub fn from_config( 31 | config: MemcacheStoreConfig, 32 | timer: Arc, 33 | ) -> Arc { 34 | let store_engine = Arc::new(MemoryStore::new(timer)); 35 | let store: Arc = match config.policy { 36 | EvictionPolicy::Random => { 37 | Arc::new(RandomPolicy::new(store_engine, config.memory_limit)) 38 | } 39 | EvictionPolicy::None => store_engine, 40 | }; 41 | store 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /memcrs/benchmarks/13_07_2020.md: -------------------------------------------------------------------------------- 1 | memix 2 | RUN #1 100%, 2 secs] 0 threads: 400000 ops, 173288 (avg: 173278) ops/sec, 91.22MB/sec (avg: 91.18MB/sec), 0.23 (avg: 0.23) msec latency 3 | ALL STATS 4 | ========================================================================= 5 | Type Ops/sec Hits/sec Misses/sec Latency KB/sec 6 | ------------------------------------------------------------------------- 7 | Sets 152692.50 --- --- 0.23000 159220.41 8 | Gets 152692.50 152692.50 0.00 0.22900 5334.99 9 | Waits 0.00 --- --- 0.00000 --- 10 | Totals 305385.01 152692.50 0.00 0.23000 164555.40 11 | 12 | 13 | memcached 14 | [RUN #1 100%, 0 secs] 0 threads: 400000 ops, 0 (avg: 491839) ops/sec, 0.00KB/sec (avg: 258.81MB/sec), 0.00 (avg: 0.08) msec latency 15 | ALL STATS 16 | ========================================================================= 17 | Type Ops/sec Hits/sec Misses/sec Latency KB/sec 18 | ------------------------------------------------------------------------- 19 | Sets 112580.85 --- --- 0.08200 117393.91 20 | Gets 112580.85 112580.85 0.00 0.07900 3933.51 21 | Waits 0.00 --- --- 0.00000 --- 22 | Totals 225161.71 112580.85 0.00 0.08000 121327.42 23 | -------------------------------------------------------------------------------- /memcrs/src/cache/error.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, PartialEq, Eq)] 2 | pub enum CacheError { 3 | NotFound = 0x01, 4 | KeyExists = 0x02, 5 | ValueTooLarge = 0x03, 6 | InvalidArguments = 0x04, 7 | ItemNotStored = 0x05, 8 | ArithOnNonNumeric = 0x06, 9 | UnkownCommand = 0x81, 10 | OutOfMemory = 0x82, 11 | NotSupported = 0x83, 12 | InternalError = 0x84, 13 | Busy = 0x85, 14 | TemporaryFailure = 0x86, 15 | } 16 | 17 | impl CacheError { 18 | pub fn to_static_string(&self) -> &'static str { 19 | static NOT_FOUND: &str = "Not found"; 20 | static KEY_EXISTS: &str = "Key exists"; 21 | 22 | match self { 23 | CacheError::NotFound => NOT_FOUND, 24 | CacheError::KeyExists => KEY_EXISTS, 25 | CacheError::ValueTooLarge => "Value too big", 26 | CacheError::InvalidArguments => "Invalid arguments", 27 | CacheError::ItemNotStored => "Item not stored", 28 | CacheError::ArithOnNonNumeric => "Incr/Decr on non numeric value", 29 | CacheError::UnkownCommand => "Invalid command", 30 | CacheError::OutOfMemory => "Out of memory", 31 | CacheError::NotSupported => "Not supported", 32 | CacheError::InternalError => "Internal error", 33 | CacheError::Busy => "Busy", 34 | CacheError::TemporaryFailure => "Temporary failure", 35 | } 36 | } 37 | } 38 | 39 | pub type Result = std::result::Result; 40 | -------------------------------------------------------------------------------- /memcrs/src/mock/mock_server.rs: -------------------------------------------------------------------------------- 1 | use crate::memcache::store::MemcStore; 2 | use crate::memory_store::store::MemoryStore; 3 | use crate::server::timer; 4 | use std::sync::atomic::{AtomicUsize, Ordering}; 5 | use std::sync::Arc; 6 | 7 | pub struct MockSystemTimer { 8 | pub current_time: AtomicUsize, 9 | } 10 | 11 | pub trait SetableTimer: timer::Timer { 12 | fn set(&self, time: u64); 13 | } 14 | 15 | impl MockSystemTimer { 16 | pub fn new() -> Self { 17 | MockSystemTimer { 18 | current_time: AtomicUsize::new(0), 19 | } 20 | } 21 | } 22 | 23 | impl timer::Timer for MockSystemTimer { 24 | fn timestamp(&self) -> u64 { 25 | self.current_time.load(Ordering::Relaxed) as u64 26 | } 27 | } 28 | 29 | impl SetableTimer for MockSystemTimer { 30 | fn set(&self, time: u64) { 31 | self.current_time.store(time as usize, Ordering::Relaxed) 32 | } 33 | } 34 | 35 | pub struct MockServer { 36 | pub timer: Arc, 37 | pub storage: MemcStore, 38 | } 39 | 40 | impl MockServer { 41 | pub fn new() -> Self { 42 | let timer = Arc::new(MockSystemTimer::new()); 43 | let store = Arc::new(MemoryStore::new(timer.clone())); 44 | MockServer { 45 | timer: timer, 46 | storage: MemcStore::new(store), 47 | } 48 | } 49 | } 50 | 51 | pub fn create_server() -> MockServer { 52 | MockServer::new() 53 | } 54 | 55 | pub fn create_storage() -> Arc { 56 | let timer = Arc::new(MockSystemTimer::new()); 57 | Arc::new(MemcStore::new(Arc::new(MemoryStore::new(timer)))) 58 | } 59 | -------------------------------------------------------------------------------- /memcrs/benchmarks/19_02_2020.txt~: -------------------------------------------------------------------------------- 1 | -- memix 2 | 3 | [RUN #1] Preparing benchmark client... 4 | [RUN #1] Launching threads now... 5 | [RUN #1 100%, 33 secs] 0 threads: 10000000 ops, 319659 (avg: 301817) ops/sec, 1.23GB/sec (avg: 1.16GB/sec), 3.13 (avg: 3.31) msec latency 6 | 7 | 10 Threads 8 | 100 Connections per thread 9 | 10000 Requests per client 10 | 11 | 12 | ALL STATS 13 | ========================================================================= 14 | Type Ops/sec Hits/sec Misses/sec Latency KB/sec 15 | ------------------------------------------------------------------------- 16 | Sets 150043.03 --- --- 3.12400 1206758.90 17 | Gets 150043.03 150043.03 0.00 3.49700 5242.42 18 | Waits 0.00 --- --- 0.00000 --- 19 | Totals 300086.07 150043.03 0.00 3.31100 1212001.33 20 | 21 | 22 | -- memcached 23 | 24 | [RUN #1 100%, 29 secs] 0 threads: 9960000 ops, 350354 (avg: 335912) ops/sec, 1.35GB/sec (avg: 1.29GB/sec), 2.84 (avg: 2.89) msec latency 25 | 26 | 10 Threads 27 | 100 Connections per thread 28 | 10000 Requests per client 29 | 30 | 31 | ALL STATS 32 | ========================================================================= 33 | Type Ops/sec Hits/sec Misses/sec Latency KB/sec 34 | ------------------------------------------------------------------------- 35 | Sets 176467.72 --- --- 2.82300 1419286.13 36 | Gets 176467.72 176467.72 0.00 2.95300 6165.69 37 | Waits 0.00 --- --- 0.00000 --- 38 | Totals 352935.45 176467.72 0.00 2.88800 1425451.82 39 | -------------------------------------------------------------------------------- /memcrs/benchmarks/19_02_2020.txt: -------------------------------------------------------------------------------- 1 | -- memix 2 | 3 | [RUN #1] Preparing benchmark client... 4 | [RUN #1] Launching threads now... 5 | [RUN #1 100%, 33 secs] 0 threads: 10000000 ops, 319659 (avg: 301817) ops/sec, 1.23GB/sec (avg: 1.16GB/sec), 3.13 (avg: 3.31) msec latency 6 | 7 | 10 Threads 8 | 100 Connections per thread 9 | 10000 Requests per client 10 | 11 | 12 | ALL STATS 13 | ========================================================================= 14 | Type Ops/sec Hits/sec Misses/sec Latency KB/sec 15 | ------------------------------------------------------------------------- 16 | Sets 150043.03 --- --- 3.12400 1206758.90 17 | Gets 150043.03 150043.03 0.00 3.49700 5242.42 18 | Waits 0.00 --- --- 0.00000 --- 19 | Totals 300086.07 150043.03 0.00 3.31100 1212001.33 20 | 21 | 22 | 23 | 24 | -- memcached 25 | 26 | [RUN #1 100%, 29 secs] 0 threads: 9960000 ops, 350354 (avg: 335912) ops/sec, 1.35GB/sec (avg: 1.29GB/sec), 2.84 (avg: 2.89) msec latency 27 | 28 | 10 Threads 29 | 100 Connections per thread 30 | 10000 Requests per client 31 | 32 | 33 | ALL STATS 34 | ========================================================================= 35 | Type Ops/sec Hits/sec Misses/sec Latency KB/sec 36 | ------------------------------------------------------------------------- 37 | Sets 176467.72 --- --- 2.82300 1419286.13 38 | Gets 176467.72 176467.72 0.00 2.95300 6165.69 39 | Waits 0.00 --- --- 0.00000 --- 40 | Totals 352935.45 176467.72 0.00 2.88800 1425451.82 41 | -------------------------------------------------------------------------------- /memcrs/src/bin/memcrsd.rs: -------------------------------------------------------------------------------- 1 | use log::info; 2 | use std::env; 3 | use std::process; 4 | use std::sync::Arc; 5 | use tracing_log::LogTracer; 6 | extern crate clap; 7 | extern crate memcrs; 8 | 9 | #[cfg(feature = "jemallocator")] 10 | use jemallocator::Jemalloc; 11 | 12 | #[cfg(feature = "jemallocator")] 13 | #[global_allocator] 14 | static GLOBAL: Jemalloc = Jemalloc; 15 | 16 | fn main() { 17 | LogTracer::init().expect("Cannot initialize logger"); 18 | let runtimes = (num_cpus::get_physical()).to_string(); 19 | 20 | let cli_config = match memcrs::memcache::cli::parser::parse(runtimes, env::args().collect()) { 21 | Ok(config) => config, 22 | Err(err) => { 23 | eprint!("{}", err); 24 | process::exit(1); 25 | } 26 | }; 27 | // Vary the output based on how many times the user used the "verbose" flag 28 | // (i.e. 'myprog -v -v -v' or 'myprog -vvv' vs 'myprog -v' 29 | tracing_subscriber::fmt() 30 | .with_max_level(cli_config.log_level) 31 | .init(); 32 | 33 | info!("Listen address: {}", cli_config.listen_address.to_string()); 34 | info!("Listen port: {}", cli_config.port); 35 | info!("Connection limit: {}", cli_config.connection_limit); 36 | info!("Number of threads: {}", cli_config.threads); 37 | info!("Runtime type: {}", cli_config.runtime_type.as_str()); 38 | info!("Max item size: {}", cli_config.item_size_limit.get_bytes()); 39 | info!("Memory limit: {} MB", cli_config.memory_limit_mb); 40 | 41 | let system_timer: Arc = 42 | Arc::new(memcrs::server::timer::SystemTimer::new()); 43 | let parent_runtime = 44 | memcrs::memcache_server::runtime_builder::create_memcrs_server(cli_config, system_timer.clone()); 45 | parent_runtime.block_on(system_timer.run()) 46 | } 47 | -------------------------------------------------------------------------------- /memclt/src/main.rs: -------------------------------------------------------------------------------- 1 | use memcache::MemcacheError; 2 | use std::collections::HashMap; 3 | use std::str; 4 | 5 | fn main() { 6 | let client = memcache::Client::connect( 7 | "memcache://127.0.0.1:11211?timeout=120&tcp_nodelay=true&protocol=binary", 8 | ) 9 | .unwrap(); 10 | client.set("foo", "test", 600).unwrap(); 11 | 12 | let result: HashMap, u32, Option)> = client.gets(&["foo"]).unwrap(); 13 | 14 | let (key, val, cas) = result.get("foo").unwrap(); 15 | 16 | println!( 17 | "Foo: {:?} {} {}", 18 | str::from_utf8(key).unwrap(), 19 | val, 20 | cas.unwrap() 21 | ); 22 | 23 | client.append("foo", "bas").unwrap(); 24 | 25 | client.prepend("foo", "bis").unwrap(); 26 | 27 | let result: HashMap, u32, Option)> = client.gets(&["foo"]).unwrap(); 28 | let (key, val, cas) = result.get("foo").unwrap(); 29 | 30 | println!( 31 | "Foo: {:?} {} {}", 32 | str::from_utf8(key).unwrap(), 33 | val, 34 | cas.unwrap() 35 | ); 36 | client.replace("foo", "3000", 80).unwrap(); 37 | 38 | client.increment("foo", 100).unwrap(); 39 | 40 | client.decrement("foo", 50).unwrap(); 41 | 42 | let result: Result, MemcacheError> = client.get("foo"); 43 | match result { 44 | Ok(val) => match val { 45 | Some(value) => println!("Server returned: {}", value), 46 | None => println!("Server none"), 47 | }, 48 | Err(err) => { 49 | println!("Error returned: {:?}", err); 50 | } 51 | } 52 | 53 | client.delete("foo").unwrap(); 54 | 55 | client.flush_with_delay(100).unwrap(); 56 | 57 | client.flush().unwrap(); 58 | let version = client.version().unwrap(); 59 | println!("Version: {:?}", version); 60 | } 61 | -------------------------------------------------------------------------------- /memcrs/benchmarks/06_12_2020.md: -------------------------------------------------------------------------------- 1 | memcrsd 2 | =================================================== 3 | Writing results to stdout 4 | [RUN #1] Preparing benchmark client... 5 | [RUN #1] Launching threads now... 6 | [RUN #1 100%, 21 secs] 0 threads: 4000000 ops, 177274 (avg: 183668) ops/sec, 1.17GB/sec (avg: 1.21GB/sec), 0.23 (avg: 0.22) msec latency 7 | 8 | 4 Threads 9 | 10 Connections per thread 10 | 100000 Requests per client 11 | 12 | 13 | ALL STATS 14 | ============================================================================================================================ 15 | Type Ops/sec Hits/sec Misses/sec Avg. Latency p50 Latency p99 Latency p99.9 Latency KB/sec 16 | ---------------------------------------------------------------------------------------------------------------------------- 17 | Sets 92448.20 --- --- 0.19882 0.15900 0.51900 2.84700 1276649.83 18 | Gets 92448.20 92448.20 0.00 0.23686 0.20700 0.52700 2.76700 3320.35 19 | Waits 0.00 --- --- --- --- --- --- --- 20 | Totals 184896.40 92448.20 0.00 0.21784 0.19100 0.52700 2.81500 1279970.18 21 | 22 | memcached 23 | =================================================== 24 | Writing results to stdout 25 | [RUN #1] Preparing benchmark client... 26 | [RUN #1] Launching threads now... 27 | [RUN #1 100%, 11 secs] 0 threads: 4000000 ops, 333837 (avg: 341980) ops/sec, 2.20GB/sec (avg: 2.26GB/sec), 0.12 (avg: 0.12) msec latency 28 | 29 | 4 Threads 30 | 10 Connections per thread 31 | 100000 Requests per client 32 | 33 | 34 | ALL STATS 35 | ============================================================================================================================ 36 | Type Ops/sec Hits/sec Misses/sec Avg. Latency p50 Latency p99 Latency p99.9 Latency KB/sec 37 | ---------------------------------------------------------------------------------------------------------------------------- 38 | Sets 162137.08 --- --- 0.07781 0.07900 0.13500 0.16700 2239008.27 39 | Gets 162137.08 162137.08 0.00 0.15652 0.15900 0.23900 0.29500 5823.29 40 | Waits 0.00 --- --- --- --- --- --- --- 41 | Totals 324274.17 162137.08 0.00 0.11717 0.11900 0.22300 0.27100 2244831.56 -------------------------------------------------------------------------------- /memcrs/benchmarks/05_12_2020.md: -------------------------------------------------------------------------------- 1 | Rust: 2 | =================================================== 3 | Writing results to stdout 4 | [RUN #1] Preparing benchmark client... 5 | [RUN #1] Launching threads now... 6 | [RUN #1 100%, 2 secs] 0 threads: 400000 ops, 152885 (avg: 147597) ops/sec, 1.01GB/sec (avg: 997.68MB/sec), 0.26 (avg: 0.27) msec latencyy 7 | 8 | 4 Threads 9 | 10 Connections per thread 10 | 10000 Requests per client 11 | 12 | 13 | ALL STATS 14 | ============================================================================================================================ 15 | Type Ops/sec Hits/sec Misses/sec Avg. Latency p50 Latency p99 Latency p99.9 Latency KB/sec 16 | ---------------------------------------------------------------------------------------------------------------------------- 17 | Sets 73881.87 --- --- 0.25170 0.19900 0.71100 3.88700 1020188.74 18 | Gets 73881.87 73881.87 0.00 0.29037 0.24700 0.75900 3.77500 2581.39 19 | Waits 0.00 --- --- --- --- --- --- --- 20 | Totals 147763.74 73881.87 0.00 0.27104 0.23100 0.72700 3.82300 1022770.13 21 | 22 | Memcached: 23 | =================================================== 24 | Writing results to stdout 25 | [RUN #1] Preparing benchmark client... 26 | [RUN #1] Launching threads now... 27 | [RUN #1 100%, 1 secs] 0 threads: 400000 ops, 247905 (avg: 257773) ops/sec, 1.64GB/sec (avg: 1.70GB/sec), 0.16 (avg: 0.15) msec latency 28 | 29 | 4 Threads 30 | 10 Connections per thread 31 | 10000 Requests per client 32 | 33 | 34 | ALL STATS 35 | ============================================================================================================================ 36 | Type Ops/sec Hits/sec Misses/sec Avg. Latency p50 Latency p99 Latency p99.9 Latency KB/sec 37 | ---------------------------------------------------------------------------------------------------------------------------- 38 | Sets 430706.81 --- --- 0.10562 0.10300 0.27900 0.54300 5947362.01 39 | Gets 430706.81 430706.81 0.00 0.20504 0.20700 0.33500 0.63100 15048.66 40 | Waits 0.00 --- --- --- --- --- --- --- 41 | Totals 861413.62 430706.81 0.00 0.15533 0.15100 0.31900 0.59900 5962410.67 -------------------------------------------------------------------------------- /memcrs/benchmarks/10_08_2020.md: -------------------------------------------------------------------------------- 1 | memcache: 2 | 3 | [RUN #1] Preparing benchmark client... 4 | [RUN #1] Launching threads now... 5 | [RUN #1 100%, 16 secs] 0 threads: 4800000 ops, 305221 (avg: 292602) ops/sec, 30.56MB/sec (avg: 29.20MB/sec), 0.26 (avg: 0.27) msec latency 6 | 7 | 8 Threads 8 | 10 Connections per thread 9 | 60000 Requests per client 10 | 11 | 12 | ALL STATS 13 | ========================================================================= 14 | Type Ops/sec Hits/sec Misses/sec Latency KB/sec 15 | ------------------------------------------------------------------------- 16 | Sets 147525.00 --- --- 0.27100 24870.30 17 | Gets 147525.00 147525.00 0.00 0.27200 5277.14 18 | Waits 0.00 --- --- 0.00000 --- 19 | Totals 295050.00 147525.00 0.00 0.27200 30147.44 20 | 21 | ./memtier_benchmark -s 192.168.1.153 -p 11211 -P memcache_binary -c 10 -t 8 31,44s user 49,82s system 450% cpu 18,030 total 22 | 23 | 24 threads: 24 | =================================================== 25 | [RUN #1] Preparing benchmark client... 26 | [RUN #1] Launching threads now... 27 | [RUN #1 100%, 49 secs] 0 threads: 14400000 ops, 292549 (avg: 288697) ops/sec, 29.29MB/sec (avg: 28.81MB/sec), 0.82 (avg: 0.83) msec latency 28 | 29 | 24 Threads 30 | 10 Connections per thread 31 | 60000 Requests per client 32 | 33 | 34 | ALL STATS 35 | ========================================================================= 36 | Type Ops/sec Hits/sec Misses/sec Latency KB/sec 37 | ------------------------------------------------------------------------- 38 | Sets 147161.38 --- --- 0.83000 24809.00 39 | Gets 147161.38 147161.38 0.00 0.82800 5264.13 40 | Waits 0.00 --- --- 0.00000 --- 41 | Totals 294322.75 147161.38 0.00 0.82900 30073.14 42 | ./memtier_benchmark -s 192.168.1.153 -p 11211 -P memcache_binary -c 10 -t 2 104,36s user 168,94s system 535% cpu 51,074 total 43 | 44 | 45 | memix: 46 | =================================================== 47 | [RUN #1] Preparing benchmark client... 48 | [RUN #1] Launching threads now... 49 | [RUN #1 100%, 16 secs] 0 threads: 4800000 ops, 281945 (avg: 286594) ops/sec, 28.23MB/sec (avg: 28.60MB/sec), 0.28 (avg: 0.28) msec latency 50 | 51 | 8 Threads 52 | 10 Connections per thread 53 | 60000 Requests per client 54 | 55 | 56 | ALL STATS 57 | ========================================================================= 58 | Type Ops/sec Hits/sec Misses/sec Latency KB/sec 59 | ------------------------------------------------------------------------- 60 | Sets 135087.33 --- --- 0.27700 22773.52 61 | Gets 135087.33 135087.33 0.00 0.27700 4832.23 62 | Waits 0.00 --- --- 0.00000 --- 63 | Totals 270174.66 135087.33 0.00 0.27700 27605.75 64 | ./memtier_benchmark -s 192.168.1.153 -p 11211 -P memcache_binary -c 10 -t 8 31,76s user 50,42s system 455% cpu 18,026 total 65 | 66 | 24 threads: 67 | 68 | =================================================== 69 | [RUN #1] Preparing benchmark client... 70 | [RUN #1] Launching threads now... 71 | [RUN #1 100%, 50 secs] 0 threads: 14400000 ops, 285421 (avg: 285748) ops/sec, 28.58MB/sec (avg: 28.51MB/sec), 0.84 (avg: 0.84) msec latency 72 | 73 | 24 Threads 74 | 10 Connections per thread 75 | 60000 Requests per client 76 | 77 | 78 | ALL STATS 79 | ========================================================================= 80 | Type Ops/sec Hits/sec Misses/sec Latency KB/sec 81 | ------------------------------------------------------------------------- 82 | Sets 144504.77 --- --- 0.83800 24361.14 83 | Gets 144504.77 144504.77 0.00 0.83600 5169.10 84 | Waits 0.00 --- --- 0.00000 --- 85 | Totals 289009.54 144504.77 0.00 0.83700 29530.25 86 | ./memtier_benchmark -s 192.168.1.153 -p 11211 -P memcache_binary -c 10 -t 2 108,19s user 170,16s system 545% cpu 51,065 total -------------------------------------------------------------------------------- /memcrs/src/memcache/random_policy.rs: -------------------------------------------------------------------------------- 1 | use crate::cache::error::Result; 2 | use crate::cache::cache::{ 3 | impl_details::CacheImplDetails, Cache, CacheReadOnlyView, KeyType, CacheMetaData, CachePredicate, Record, 4 | RemoveIfResult, SetStatus 5 | }; 6 | use rand::rngs::SmallRng; 7 | use rand::{Rng, SeedableRng}; 8 | use std::sync::atomic; 9 | use std::sync::Arc; 10 | 11 | pub struct RandomPolicy { 12 | store: Arc, 13 | memory_limit: u64, 14 | memory_usage: atomic::AtomicU64, 15 | } 16 | 17 | impl RandomPolicy { 18 | pub fn new(store: Arc, memory_limit: u64) -> RandomPolicy { 19 | RandomPolicy { 20 | store, 21 | memory_limit, 22 | memory_usage: atomic::AtomicU64::new(0), 23 | } 24 | } 25 | 26 | fn incr_mem_usage(&self, value: u64) -> u64 { 27 | let mut usage = self.memory_usage.fetch_add(value, atomic::Ordering::Release); 28 | 29 | let mut small_rng = SmallRng::from_entropy(); 30 | while usage > self.memory_limit { 31 | debug!("Current memory usage: {}", usage); 32 | debug!("Memory limit: {}", self.memory_limit); 33 | 34 | let max = self.store.len(); 35 | if max == 0 { 36 | self.decr_mem_usage(usage); 37 | break; 38 | } 39 | let item = small_rng.gen_range(0..max); 40 | let mut number_of_calls: usize = 0; 41 | let res = self 42 | .store 43 | .remove_if(&mut move |_key: &KeyType, _value: &Record| -> bool { 44 | if number_of_calls != item { 45 | number_of_calls += 1; 46 | return false; 47 | } 48 | number_of_calls += 1; 49 | true 50 | }); 51 | 52 | res.iter().for_each(|record| match record { 53 | Some(val) => { 54 | let len = val.1.len(); 55 | debug!("Evicted: {} bytes from storage", len); 56 | usage = self.decr_mem_usage(len as u64); 57 | } 58 | None => {} 59 | }); 60 | } 61 | usage 62 | } 63 | 64 | fn decr_mem_usage(&self, value: u64) -> u64 { 65 | self.memory_usage.fetch_sub(value, atomic::Ordering::Release) 66 | } 67 | } 68 | 69 | impl CacheImplDetails for RandomPolicy { 70 | // 71 | fn get_by_key(&self, key: &KeyType) -> Result { 72 | self.store.get_by_key(key) 73 | } 74 | 75 | // 76 | fn check_if_expired(&self, key: &KeyType, record: &Record) -> bool { 77 | self.store.check_if_expired(key, record) 78 | } 79 | } 80 | 81 | impl Cache for RandomPolicy { 82 | fn get(&self, key: &KeyType) -> Result { 83 | self.store.get(key) 84 | } 85 | 86 | fn set(&self, key: KeyType, record: Record) -> Result { 87 | let len = record.len() as u64; 88 | self.incr_mem_usage(len); 89 | self.store.set(key, record) 90 | } 91 | 92 | fn delete(&self, key: KeyType, header: CacheMetaData) -> Result { 93 | let result = self.store.delete(key, header); 94 | if let Ok(record) = &result { 95 | self.decr_mem_usage(record.len() as u64); 96 | } 97 | result 98 | } 99 | 100 | // Removes key value and returns as an option 101 | fn remove(&self, key: &KeyType) -> Option<(KeyType, Record)> { 102 | let result = self.store.remove(key); 103 | if let Some(key_value) = &result { 104 | self.decr_mem_usage(key_value.1.len() as u64); 105 | } 106 | result 107 | } 108 | 109 | fn flush(&self, header: CacheMetaData) { 110 | self.store.flush(header) 111 | } 112 | 113 | fn as_read_only(&self) -> Box { 114 | self.store.as_read_only() 115 | } 116 | 117 | fn remove_if(&self, f: &mut CachePredicate) -> RemoveIfResult { 118 | self.store.remove_if(f) 119 | } 120 | 121 | fn len(&self) -> usize { 122 | self.store.len() 123 | } 124 | 125 | fn is_empty(&self) -> bool { 126 | self.store.is_empty() 127 | } 128 | } 129 | 130 | mod tests {} 131 | -------------------------------------------------------------------------------- /memcrs/src/memcache_server/runtime_builder.rs: -------------------------------------------------------------------------------- 1 | extern crate core_affinity; 2 | use crate::memcache; 3 | use crate::memcache_server; 4 | use crate::server; 5 | use crate::{ 6 | memcache::cli::parser::RuntimeType, 7 | cache::{cache::Cache} 8 | }; 9 | use std::net::SocketAddr; 10 | use std::sync::{ 11 | atomic::{AtomicUsize, Ordering}, 12 | Arc, 13 | }; 14 | use tokio::runtime::Builder; 15 | 16 | use crate::memcache::cli::parser::MemcrsArgs; 17 | 18 | fn get_worker_thread_name() -> String { 19 | static ATOMIC_ID: AtomicUsize = AtomicUsize::new(0); 20 | let id = ATOMIC_ID.fetch_add(1, Ordering::SeqCst); 21 | let str = format!("memcrsd-wrk-{}", id); 22 | str 23 | } 24 | 25 | fn create_multi_thread_runtime(worker_threads: usize) -> tokio::runtime::Runtime { 26 | let runtime = Builder::new_multi_thread() 27 | .thread_name_fn(get_worker_thread_name) 28 | .worker_threads(worker_threads) 29 | .enable_all() 30 | .build() 31 | .unwrap(); 32 | runtime 33 | } 34 | 35 | fn create_current_thread_runtime() -> tokio::runtime::Runtime { 36 | let runtime = Builder::new_current_thread() 37 | //.worker_threads(threads as usize) 38 | .thread_name_fn(get_worker_thread_name) 39 | //.max_blocking_threads(2) 40 | .enable_all() 41 | .build() 42 | .unwrap(); 43 | runtime 44 | } 45 | 46 | fn create_current_thread_server( 47 | config: MemcrsArgs, 48 | store: Arc, 49 | ) -> tokio::runtime::Runtime { 50 | let addr = SocketAddr::new(config.listen_address, config.port); 51 | let memc_config = memcache_server::memc_tcp::MemcacheServerConfig::new( 52 | 60, 53 | config.connection_limit, 54 | config.item_size_limit.get_bytes() as u32, 55 | config.backlog_limit, 56 | ); 57 | 58 | let core_ids = core_affinity::get_core_ids().unwrap(); 59 | 60 | 61 | for i in 0..config.threads { 62 | let store_rc = Arc::clone(&store); 63 | let core_ids_clone = core_ids.clone(); 64 | std::thread::spawn(move || { 65 | debug!("Creating runtime {}", i); 66 | let core_id = core_ids_clone[i%core_ids_clone.len()]; 67 | let res = core_affinity::set_for_current(core_id); 68 | let create_runtime = || { 69 | let child_runtime = create_current_thread_runtime(); 70 | let mut tcp_server = memcache_server::memc_tcp::MemcacheTcpServer::new(memc_config, store_rc); 71 | child_runtime.block_on(tcp_server.run(addr)).unwrap() 72 | }; 73 | if res { 74 | debug!("Thread pinned {:?} to core {:?}", std::thread::current().id(), core_id.id); 75 | create_runtime(); 76 | } else { 77 | warn!("Cannot pin thread to core {}", core_id.id); 78 | create_runtime(); 79 | } 80 | }); 81 | } 82 | create_current_thread_runtime() 83 | } 84 | 85 | fn create_threadpool_server( 86 | config: MemcrsArgs, 87 | store: Arc, 88 | ) -> tokio::runtime::Runtime { 89 | let addr = SocketAddr::new(config.listen_address, config.port); 90 | let memc_config = memcache_server::memc_tcp::MemcacheServerConfig::new( 91 | 60, 92 | config.connection_limit, 93 | config.item_size_limit.get_bytes() as u32, 94 | config.backlog_limit, 95 | ); 96 | let runtime = create_multi_thread_runtime(config.threads); 97 | let store_rc = Arc::clone(&store); 98 | let mut tcp_server = memcache_server::memc_tcp::MemcacheTcpServer::new(memc_config, store_rc); 99 | runtime.spawn(async move { tcp_server.run(addr).await }); 100 | runtime 101 | } 102 | 103 | pub fn create_memcrs_server( 104 | config: MemcrsArgs, 105 | system_timer: std::sync::Arc, 106 | ) -> tokio::runtime::Runtime { 107 | let store_config = memcache::builder::MemcacheStoreConfig::new(config.memory_limit); 108 | let memcache_store = 109 | memcache::builder::MemcacheStoreBuilder::from_config(store_config, system_timer); 110 | 111 | match config.runtime_type { 112 | RuntimeType::CurrentThread => create_current_thread_server(config, memcache_store), 113 | RuntimeType::MultiThread => create_threadpool_server(config, memcache_store), 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /memcrs/benchmarks/23_02_2020.txt: -------------------------------------------------------------------------------- 1 | =================================================== 2 | [RUN #1] Preparing benchmark client... 3 | [RUN #1] Launching threads now... 4 | [RUN #1 2%, 0 secs] 10 threads: 219154 ops, 1328850 (avg: 1328850) ops/sec, 5.12GB/sec (avg: 5.12GB/sec), [RUN #1 6%, 1 secs] 10 threads: 587678 ops, 370861 (avg: 507223) ops/sec, 1.43GB/sec (avg: 1.96GB/sec), [RUN #1 9%, 2 secs] 10 threads: 926615 ops, 338808 (avg: 429187) ops/sec, 1.30GB/sec (avg: 1.65GB/sec), [RUN #1 13%, 3 secs] 10 threads: 1269046 ops, 342350 (avg: 401694) ops/sec, 1.32GB/sec (avg: 1.55GB/sec),[RUN #1 16%, 4 secs] 10 threads: 1612267 ops, 343138 (avg: 387613) ops/sec, 1.32GB/sec (avg: 1.49GB/sec),[RUN #1 20%, 5 secs] 10 threads: 1957017 ops, 344668 (avg: 379288) ops/sec, 1.33GB/sec (avg: 1.46GB/sec),[RUN #1 23%, 6 secs] 10 threads: 2300693 ops, 343591 (avg: 373491) ops/sec, 1.32GB/sec (avg: 1.44GB/sec),[RUN #1 26%, 7 secs] 10 threads: 2645520 ops, 344754 (avg: 369477) ops/sec, 1.33GB/sec (avg: 1.42GB/sec),[RUN #1 30%, 8 secs] 10 threads: 2987776 ops, 342170 (avg: 366130) ops/sec, 1.32GB/sec (avg: 1.41GB/sec),[RUN #1 33%, 9 secs] 10 threads: 3330620 ops, 342750 (avg: 363577) ops/sec, 1.32GB/sec (avg: 1.40GB/sec),[RUN #1 37%, 10 secs] 10 threads: 3674899 ops, 344201 (avg: 361670) ops/sec, 1.33GB/sec (avg: 1.39GB/sec),[RUN #1 40%, 11 secs] 10 threads: 4018914 ops, 343937 (avg: 360081) ops/sec, 1.33GB/sec (avg: 1.39GB/sec),[RUN #1 44%, 12 secs] 10 threads: 4361076 ops, 342031 (avg: 358596) ops/sec, 1.32GB/sec (avg: 1.38GB/sec),[RUN #1 47%, 13 secs] 10 threads: 4703611 ops, 342395 (avg: 357364) ops/sec, 1.32GB/sec (avg: 1.38GB/sec),[RUN #1 50%, 14 secs] 10 threads: 5041125 ops, 337433 (avg: 355957) ops/sec, 1.30GB/sec (avg: 1.37GB/sec),[RUN #1 54%, 15 secs] 10 threads: 5379526 ops, 338317 (avg: 354793) ops/sec, 1.30GB/sec (avg: 1.37GB/sec),[RUN #1 57%, 16 secs] 10 threads: 5710713 ops, 331108 (avg: 353327) ops/sec, 1.28GB/sec (avg: 1.36GB/sec),[RUN #1 60%, 17 secs] 10 threads: 6049230 ops, 338429 (avg: 352459) ops/sec, 1.30GB/sec (avg: 1.36GB/sec),[RUN #1 64%, 18 secs] 10 threads: 6382554 ops, 333232 (avg: 351400) ops/sec, 1.28GB/sec (avg: 1.35GB/sec),[RUN #1 67%, 19 secs] 10 threads: 6717837 ops, 335203 (avg: 350555) ops/sec, 1.29GB/sec (avg: 1.35GB/sec),[RUN #1 71%, 20 secs] 10 threads: 7055629 ops, 337719 (avg: 349918) ops/sec, 1.30GB/sec (avg: 1.35GB/sec),[RUN #1 74%, 21 secs] 10 threads: 7390905 ops, 335192 (avg: 349222) ops/sec, 1.29GB/sec (avg: 1.35GB/sec),[RUN #1 77%, 22 secs] 10 threads: 7723855 ops, 332869 (avg: 348484) ops/sec, 1.28GB/sec (avg: 1.34GB/sec),[RUN #1 81%, 23 secs] 10 threads: 8061584 ops, 337644 (avg: 348016) ops/sec, 1.30GB/sec (avg: 1.34GB/sec),[RUN #1 84%, 24 secs] 10 threads: 8403748 ops, 350687 (avg: 348124) ops/sec, 1.35GB/sec (avg: 1.34GB/sec),[RUN #1 88%, 25 secs] 10 threads: 8756374 ops, 376988 (avg: 349201) ops/sec, 1.45GB/sec (avg: 1.35GB/sec),[RUN #1 91%, 25 secs] 9 threads: 9115154 ops, 376988 (avg: 351275) ops/sec, 1.45GB/sec (avg: 1.35GB/sec),[RUN #1 95%, 26 secs] 9 threads: 9483048 ops, 376988 (avg: 354057) ops/sec, 1.45GB/sec (avg: 1.36GB/sec),[RUN #1 99%, 27 secs] 8 threads: 9866809 ops, 376988 (avg: 358247) ops/sec, 1.45GB/sec (avg: 1.38GB/sec),[RUN #1 100%, 27 secs] 0 threads: 10000000 ops, 376988 (avg: 361578) ops/sec, 1.45GB/sec (avg: 1.39GB/sec), 2.65 (avg: 2.76) msec latency 5 | 6 | 10 Threads 7 | 100 Connections per thread 8 | 10000 Requests per client 9 | 10 | 11 | ALL STATS 12 | ========================================================================= 13 | Type Ops/sec Hits/sec Misses/sec Latency KB/sec 14 | ------------------------------------------------------------------------- 15 | Sets 184988.93 --- --- 2.67700 1487820.04 16 | Gets 184988.93 184988.93 0.00 2.85100 6463.41 17 | Waits 0.00 --- --- 0.00000 --- 18 | Totals 369977.85 184988.93 0.00 2.76400 1494283.45 19 | 20 | 10 Threads 21 | 100 Connections per thread 22 | 10000 Requests per client 23 | 24 | 25 | ALL STATS 26 | ========================================================================= 27 | Type Ops/sec Hits/sec Misses/sec Latency KB/sec 28 | ------------------------------------------------------------------------- 29 | Sets 156832.59 --- --- 3.10400 1261365.65 30 | Gets 156832.59 156832.59 0.00 3.47200 5479.65 31 | Waits 0.00 --- --- 0.00000 --- 32 | Totals 313665.19 156832.59 0.00 3.28800 1266845.30 33 | -------------------------------------------------------------------------------- /memcrs/src/memcache_server/memc_tcp.rs: -------------------------------------------------------------------------------- 1 | use socket2::{Domain, SockAddr, Socket, Type}; 2 | use std::net::ToSocketAddrs; 3 | use std::sync::Arc; 4 | 5 | use tokio::io; 6 | use tokio::net::TcpListener; 7 | use tokio::sync::Semaphore; 8 | 9 | use tracing::{debug, error}; 10 | 11 | //use tracing_attributes::instrument; 12 | 13 | use super::client_handler; 14 | use crate::memcache::store as storage; 15 | use crate::cache::cache::Cache; 16 | 17 | #[derive(Clone, Copy)] 18 | pub struct MemcacheServerConfig { 19 | timeout_secs: u32, 20 | connection_limit: u32, 21 | item_memory_limit: u32, 22 | listen_backlog: u32, 23 | } 24 | 25 | impl MemcacheServerConfig { 26 | pub fn new( 27 | timeout_secs: u32, 28 | connection_limit: u32, 29 | item_memory_limit: u32, 30 | listen_backlog: u32, 31 | ) -> Self { 32 | MemcacheServerConfig { 33 | timeout_secs, 34 | connection_limit, 35 | item_memory_limit, 36 | listen_backlog, 37 | } 38 | } 39 | } 40 | #[derive(Clone)] 41 | pub struct MemcacheTcpServer { 42 | storage: Arc, 43 | limit_connections: Arc, 44 | config: MemcacheServerConfig, 45 | } 46 | 47 | impl MemcacheTcpServer { 48 | pub fn new( 49 | config: MemcacheServerConfig, 50 | store: Arc, 51 | ) -> MemcacheTcpServer { 52 | MemcacheTcpServer { 53 | storage: Arc::new(storage::MemcStore::new(store)), 54 | limit_connections: Arc::new(Semaphore::new(config.connection_limit as usize)), 55 | config, 56 | } 57 | } 58 | 59 | pub async fn run(&mut self, addr: A) -> io::Result<()> { 60 | let listener = self.get_tcp_listener(addr)?; 61 | loop { 62 | tokio::select! { 63 | connection = listener.accept() => { 64 | match connection { 65 | Ok((socket, addr)) => { 66 | let peer_addr = addr; 67 | socket.set_nodelay(true)?; 68 | socket.set_linger(None)?; 69 | let mut client = client_handler::Client::new( 70 | Arc::clone(&self.storage), 71 | socket, 72 | peer_addr, 73 | self.get_client_config(), 74 | Arc::clone(&self.limit_connections) 75 | ); 76 | 77 | self.limit_connections.acquire().await.unwrap().forget(); 78 | // Like with other small servers, we'll `spawn` this client to ensure it 79 | // runs concurrently with all other clients. The `move` keyword is used 80 | // here to move ownership of our store handle into the async closure. 81 | tokio::spawn(async move { client.handle().await }); 82 | }, 83 | Err(err) => { 84 | error!("Accept error: {}", err); 85 | } 86 | } 87 | } 88 | } 89 | } 90 | } 91 | 92 | fn get_tcp_listener( 93 | &mut self, 94 | addr: A, 95 | ) -> Result { 96 | let socket = Socket::new(Domain::IPV4, Type::STREAM, None)?; 97 | socket.set_reuse_address(true)?; 98 | socket.set_reuse_port(true)?; 99 | socket.set_nonblocking(true)?; 100 | let addrs_iter = addr.to_socket_addrs()?; 101 | for socket_addr in addrs_iter { 102 | debug!("Binding to addr: {:?}", socket_addr); 103 | let sock_addr = SockAddr::from(socket_addr); 104 | let res = socket.bind(&sock_addr); 105 | if let Err(err) = res { 106 | error!("Can't bind to: {:?}, err {:?}", sock_addr, err); 107 | return Err(err); 108 | } 109 | } 110 | 111 | if let Err(err) = socket.listen(self.config.listen_backlog as i32) { 112 | error!("Listen error: {:?}", err); 113 | return Err(err); 114 | } 115 | 116 | let std_listener: std::net::TcpListener = socket.into(); 117 | TcpListener::from_std(std_listener) 118 | } 119 | 120 | fn get_client_config(&self) -> client_handler::ClientConfig { 121 | client_handler::ClientConfig { 122 | item_memory_limit: self.config.item_memory_limit, 123 | rx_timeout_secs: self.config.timeout_secs, 124 | _wx_timeout_secs: self.config.timeout_secs, 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /memcrs/src/cache/cache.rs: -------------------------------------------------------------------------------- 1 | use super::error::{CacheError, Result}; 2 | use bytes::Bytes; 3 | 4 | /// Cache key type 5 | pub type KeyType = Bytes; 6 | 7 | /// Cache value associated with a key 8 | pub type ValueType = Bytes; 9 | 10 | /// Meta data stored with cache value 11 | #[derive(Clone, Debug)] 12 | pub struct CacheMetaData { 13 | pub(crate) timestamp: u64, 14 | pub(crate) cas: u64, 15 | pub(crate) flags: u32, 16 | pub(crate) time_to_live: u32, 17 | } 18 | 19 | impl CacheMetaData { 20 | pub fn new(cas: u64, flags: u32, time_to_live: u32) -> CacheMetaData { 21 | CacheMetaData { 22 | timestamp: 0, 23 | cas, 24 | flags, 25 | time_to_live, 26 | } 27 | } 28 | 29 | pub fn get_expiration(&self) -> u32 { 30 | self.time_to_live 31 | } 32 | 33 | pub const fn len(&self) -> usize { 34 | std::mem::size_of::() 35 | } 36 | 37 | pub const fn is_empty(&self) -> bool { 38 | self.len() == 0 39 | } 40 | } 41 | 42 | /// Value and meta data stored in cache 43 | #[derive(Clone, Debug)] 44 | pub struct Record { 45 | pub(crate) header: CacheMetaData, 46 | pub(crate) value: ValueType, 47 | } 48 | 49 | impl Record { 50 | pub fn new(value: ValueType, cas: u64, flags: u32, expiration: u32) -> Record { 51 | let header = CacheMetaData::new(cas, flags, expiration); 52 | Record { header, value } 53 | } 54 | 55 | pub fn len(&self) -> usize { 56 | self.header.len() + self.value.len() 57 | } 58 | 59 | pub fn is_empty(&self) -> bool { 60 | self.len() == 0 61 | } 62 | } 63 | 64 | impl PartialEq for Record { 65 | fn eq(&self, other: &Self) -> bool { 66 | self.value == other.value 67 | } 68 | } 69 | 70 | /// Result of set operation on cache 71 | /// cas indicates version stored in cache 72 | #[derive(Debug)] 73 | pub struct SetStatus { 74 | pub cas: u64, 75 | } 76 | 77 | /// Read only view over a store 78 | pub trait CacheReadOnlyView<'a> { 79 | fn len(&self) -> usize; 80 | fn is_empty(&self) -> bool; 81 | fn keys(&'a self) -> Box + 'a>; 82 | } 83 | 84 | // Not a part of Store public API 85 | pub mod impl_details { 86 | use super::*; 87 | pub trait CacheImplDetails { 88 | // 89 | fn get_by_key(&self, key: &KeyType) -> Result; 90 | 91 | // 92 | fn check_if_expired(&self, key: &KeyType, record: &Record) -> bool; 93 | } 94 | } 95 | 96 | pub type RemoveIfResult = Vec>; 97 | pub type CachePredicate = dyn FnMut(&KeyType, &Record) -> bool; 98 | 99 | 100 | // An abstraction over a generic store key <=> value store 101 | pub trait Cache: impl_details::CacheImplDetails { 102 | /// Returns a value associated with a key 103 | fn get(&self, key: &KeyType) -> Result { 104 | let result = self.get_by_key(key); 105 | match result { 106 | Ok(record) => { 107 | if self.check_if_expired(key, &record) { 108 | return Err(CacheError::NotFound); 109 | } 110 | Ok(record) 111 | } 112 | Err(err) => Err(err), 113 | } 114 | } 115 | 116 | /// Sets value that will be associated with a store. 117 | /// If value already exists in a store CAS field is compared 118 | /// and depending on CAS value comparison value is set or rejected. 119 | /// 120 | /// - if CAS is equal to 0 value is always set 121 | /// - if CAS is not equal value is not set and there is an error 122 | /// returned with status KeyExists 123 | fn set(&self, key: KeyType, record: Record) -> Result; 124 | 125 | /// Removes a value associated with a key a returns it to a caller if CAS 126 | /// value comparison is successful or header.CAS is equal to 0: 127 | /// 128 | /// - if header.CAS != to stored record CAS KeyExists is returned 129 | /// - if key is not found NotFound is returned 130 | fn delete(&self, key: KeyType, header: CacheMetaData) -> Result; 131 | 132 | /// Removes all values from a store 133 | /// 134 | /// - if header.ttl is set to 0 values are removed immediately, 135 | /// - if header.ttl>0 values are removed from a store after 136 | /// ttl expiration 137 | fn flush(&self, header: CacheMetaData); 138 | 139 | /// Number of key value pairs stored in store 140 | fn len(&self) -> usize; 141 | 142 | fn is_empty(&self) -> bool; 143 | 144 | /// Returns a read-only view over a stroe 145 | fn as_read_only(&self) -> Box; 146 | 147 | /// Removes key-value pairs from a store for which 148 | /// f predicate returns true 149 | fn remove_if(&self, f: &mut CachePredicate) -> RemoveIfResult; 150 | 151 | /// Removes key value and returns as an option 152 | fn remove(&self, key: &KeyType) -> Option<(KeyType, Record)>; 153 | } 154 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | 6 | env: 7 | CARGO_TERM_COLOR: always 8 | 9 | defaults: 10 | run: 11 | # necessary for windows 12 | shell: bash 13 | 14 | jobs: 15 | test: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Cargo cache 20 | uses: actions/cache@v2 21 | with: 22 | path: | 23 | ~/.cargo/registry 24 | ./target 25 | key: test-cargo-registry 26 | - name: List 27 | run: find ./ 28 | - name: Run tests 29 | run: cargo test --verbose 30 | 31 | build: 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | # a list of all the targets 36 | include: 37 | - TARGET: x86_64-unknown-linux-gnu # tested in a debian container on a mac 38 | OS: ubuntu-latest 39 | - TARGET: x86_64-unknown-linux-musl # test in an alpine container on a mac 40 | OS: ubuntu-latest 41 | - TARGET: aarch64-unknown-linux-gnu # tested on aws t4g.nano 42 | OS: ubuntu-latest 43 | - TARGET: aarch64-unknown-linux-musl # tested on aws t4g.nano in alpine container 44 | OS: ubuntu-latest 45 | - TARGET: armv7-unknown-linux-gnueabihf # raspberry pi 2-3-4, not tested 46 | OS: ubuntu-latest 47 | - TARGET: armv7-unknown-linux-musleabihf # raspberry pi 2-3-4, not tested 48 | OS: ubuntu-latest 49 | - TARGET: arm-unknown-linux-gnueabihf # raspberry pi 0-1, not tested 50 | OS: ubuntu-latest 51 | - TARGET: arm-unknown-linux-musleabihf # raspberry pi 0-1, not tested 52 | OS: ubuntu-latest 53 | - TARGET: x86_64-apple-darwin # tested on a mac, is not properly signed so there are security warnings 54 | OS: macos-latest 55 | needs: test 56 | runs-on: ${{ matrix.OS }} 57 | env: 58 | NAME: memcrsd 59 | TARGET: ${{ matrix.TARGET }} 60 | OS: ${{ matrix.OS }} 61 | steps: 62 | - uses: actions/checkout@v2 63 | - name: Cargo cache 64 | uses: actions/cache@v2 65 | with: 66 | path: | 67 | ~/.cargo/registry 68 | ./target 69 | key: build-cargo-registry-${{matrix.TARGET}} 70 | - name: List 71 | run: find ./ 72 | - name: Install and configure dependencies 73 | run: | 74 | # dependencies are only needed on ubuntu as that's the only place where 75 | # we make cross-compilation 76 | if [[ $OS =~ ^ubuntu.*$ ]]; then 77 | sudo apt-get install -qq crossbuild-essential-arm64 crossbuild-essential-armhf 78 | fi 79 | 80 | # some additional configuration for cross-compilation on linux 81 | cat >>~/.cargo/config < Self { 20 | MemcacheBinaryConnection { 21 | stream: socket, 22 | codec: MemcacheBinaryCodec::new(item_size_limit), 23 | buffer: BytesMut::with_capacity(4096), 24 | } 25 | } 26 | 27 | pub async fn read_frame(&mut self) -> Result, io::Error> { 28 | let _extras_length: u32 = 8; 29 | loop { 30 | // Attempt to parse a frame from the buffered data. If enough data 31 | // has been buffered, the frame is returned. 32 | if let Some(frame) = self.codec.decode(&mut self.buffer)? { 33 | match frame { 34 | BinaryRequest::ItemTooLarge(request) => { 35 | debug!( 36 | "Body len {:?} buffer len {:?}", 37 | request.header.body_length, 38 | self.buffer.len() 39 | ); 40 | let skip = (request.header.body_length) - (self.buffer.len() as u32); 41 | if skip >= self.buffer.len() as u32 { 42 | self.buffer.clear(); 43 | } else { 44 | self.buffer = self.buffer.split_off(skip as usize); 45 | } 46 | self.skip_bytes(skip).await?; 47 | return Ok(Some(BinaryRequest::ItemTooLarge(request))); 48 | } 49 | _ => { 50 | return Ok(Some(frame)); 51 | } 52 | } 53 | } 54 | 55 | // There is not enough buffered data to read a frame. Attempt to 56 | // read more data from the socket. 57 | // 58 | // On success, the number of bytes is returned. `0` indicates "end 59 | // of stream". 60 | if 0 == self.stream.read_buf(&mut self.buffer).await? { 61 | // The remote closed the connection. For this to be a clean 62 | // shutdown, there should be no data in the read buffer. If 63 | // there is, this means that the peer closed the socket while 64 | // sending a frame. 65 | if self.buffer.is_empty() { 66 | return Ok(None); 67 | } else { 68 | return Err(Error::new( 69 | ErrorKind::ConnectionReset, 70 | "Connection reset by peer", 71 | )); 72 | } 73 | } 74 | } 75 | } 76 | 77 | pub async fn skip_bytes(&mut self, bytes: u32) -> io::Result<()> { 78 | let buffer_size = 64 * 1024; 79 | let mut buffer = BytesMut::with_capacity(cmp::min(bytes as usize, buffer_size)); 80 | let mut bytes_read: usize; 81 | let mut bytes_counter: usize = 0; 82 | debug!("Skip bytes {:?}", bytes); 83 | if bytes == 0 { 84 | return Ok(()); 85 | } 86 | 87 | loop { 88 | bytes_read = self.stream.read_buf(&mut buffer).await?; 89 | 90 | // The remote closed the connection. For this to be a clean 91 | // shutdown, there should be no data in the read buffer. If 92 | // there is, this means that the peer closed the socket while 93 | // sending a frame. 94 | if bytes_read == 0 { 95 | debug!("Bytes read {:?}", bytes_read); 96 | if buffer.is_empty() { 97 | return Ok(()); 98 | } else { 99 | return Err(Error::new( 100 | ErrorKind::ConnectionReset, 101 | "Connection reset by peer", 102 | )); 103 | } 104 | } 105 | 106 | bytes_counter += bytes_read; 107 | let difference = bytes as usize - bytes_counter; 108 | debug!( 109 | "Bytes read: {:?} {:?} {:?}", 110 | bytes_read, bytes_counter, difference 111 | ); 112 | 113 | if bytes_counter == bytes as usize { 114 | return Ok(()); 115 | } 116 | 117 | if difference < buffer_size { 118 | buffer = BytesMut::with_capacity(difference); 119 | } else { 120 | buffer.clear(); 121 | } 122 | 123 | if bytes_counter > bytes as usize { 124 | panic!("Read too much bytes socket corrupted"); 125 | } 126 | } 127 | } 128 | 129 | pub async fn write(&mut self, msg: &BinaryResponse) -> io::Result<()> { 130 | let message = self.codec.encode_message(msg); 131 | self.write_data_to_stream(message).await?; 132 | Ok(()) 133 | } 134 | 135 | async fn write_data_to_stream(&mut self, msg: ResponseMessage) -> io::Result<()> { 136 | self.stream.write_all(&msg.data[..]).await?; 137 | Ok(()) 138 | } 139 | 140 | pub async fn shutdown(&mut self) -> io::Result<()> { 141 | self.stream.shutdown().await?; 142 | Ok(()) 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /memcrs/src/memory_store/store.rs: -------------------------------------------------------------------------------- 1 | use crate::cache::error::{CacheError, Result}; 2 | use crate::server::timer; 3 | use crate::cache::cache::{KeyType, Record, CacheReadOnlyView, impl_details, Cache, SetStatus, CacheMetaData, CachePredicate, RemoveIfResult}; 4 | use dashmap::mapref::multiple::RefMulti; 5 | use dashmap::{DashMap, ReadOnlyView}; 6 | use std::sync::atomic::{AtomicU64, Ordering}; 7 | use std::sync::Arc; 8 | 9 | type Storage = DashMap; 10 | pub struct MemoryStore { 11 | memory: Storage, 12 | timer: Arc, 13 | cas_id: AtomicU64, 14 | } 15 | 16 | type StorageReadOnlyView = ReadOnlyView; 17 | 18 | impl<'a> CacheReadOnlyView<'a> for StorageReadOnlyView { 19 | fn len(&self) -> usize { 20 | StorageReadOnlyView::len(self) 21 | } 22 | 23 | fn is_empty(&self) -> bool { 24 | StorageReadOnlyView::is_empty(self) 25 | } 26 | 27 | fn keys(&'a self) -> Box + 'a> { 28 | let keys = self.keys(); 29 | Box::new(keys) 30 | } 31 | } 32 | 33 | impl MemoryStore { 34 | pub fn new(timer: Arc) -> MemoryStore { 35 | MemoryStore { 36 | memory: DashMap::new(), 37 | timer, 38 | cas_id: AtomicU64::new(1), 39 | } 40 | } 41 | 42 | fn get_cas_id(&self) -> u64 { 43 | self.cas_id.fetch_add(1, Ordering::Release) 44 | } 45 | } 46 | 47 | impl impl_details::CacheImplDetails for MemoryStore { 48 | fn get_by_key(&self, key: &KeyType) -> Result { 49 | match self.memory.get(key) { 50 | Some(record) => Ok(record.clone()), 51 | None => Err(CacheError::NotFound), 52 | } 53 | } 54 | 55 | fn check_if_expired(&self, key: &KeyType, record: &Record) -> bool { 56 | let current_time = self.timer.timestamp(); 57 | 58 | if record.header.time_to_live == 0 { 59 | return false; 60 | } 61 | 62 | if record.header.timestamp + (record.header.time_to_live as u64) > current_time { 63 | return false; 64 | } 65 | match self.remove(key) { 66 | Some(_) => true, 67 | None => true, 68 | } 69 | } 70 | } 71 | 72 | impl Cache for MemoryStore { 73 | // Removes key value and returns as an option 74 | fn remove(&self, key: &KeyType) -> Option<(KeyType, Record)> { 75 | self.memory.remove(key) 76 | } 77 | 78 | fn set(&self, key: KeyType, mut record: Record) -> Result { 79 | //trace!("Set: {:?}", &record.header); 80 | if record.header.cas > 0 { 81 | match self.memory.get_mut(&key) { 82 | Some(mut key_value) => { 83 | if key_value.header.cas != record.header.cas { 84 | Err(CacheError::KeyExists) 85 | } else { 86 | record.header.cas += 1; 87 | record.header.timestamp = self.timer.timestamp(); 88 | let cas = record.header.cas; 89 | *key_value = record; 90 | Ok(SetStatus { cas }) 91 | } 92 | } 93 | None => { 94 | record.header.cas += 1; 95 | record.header.timestamp = self.timer.timestamp(); 96 | let cas = record.header.cas; 97 | self.memory.insert(key, record); 98 | Ok(SetStatus { cas }) 99 | } 100 | } 101 | } else { 102 | let cas = self.get_cas_id(); 103 | record.header.cas = cas; 104 | record.header.timestamp = self.timer.timestamp(); 105 | self.memory.insert(key, record); 106 | Ok(SetStatus { cas }) 107 | } 108 | } 109 | 110 | fn delete(&self, key: KeyType, header: CacheMetaData) -> Result { 111 | let mut cas_match: Option = None; 112 | match self.memory.remove_if(&key, |_key, record| -> bool { 113 | let result = header.cas == 0 || record.header.cas == header.cas; 114 | cas_match = Some(result); 115 | result 116 | }) { 117 | Some(key_value) => Ok(key_value.1), 118 | None => match cas_match { 119 | Some(_value) => Err(CacheError::KeyExists), 120 | None => Err(CacheError::NotFound), 121 | }, 122 | } 123 | } 124 | 125 | fn flush(&self, header: CacheMetaData) { 126 | if header.time_to_live > 0 { 127 | self.memory.alter_all(|_key, mut value| { 128 | value.header.time_to_live = header.time_to_live; 129 | value 130 | }); 131 | } else { 132 | self.memory.clear(); 133 | } 134 | } 135 | 136 | fn as_read_only(&self) -> Box { 137 | let storage_clone = self.memory.clone(); 138 | Box::new(storage_clone.into_read_only()) 139 | } 140 | 141 | fn remove_if(&self, f: &mut CachePredicate) -> RemoveIfResult { 142 | let items: Vec = self 143 | .memory 144 | .iter() 145 | .filter(|record: &RefMulti| f(record.key(), record.value())) 146 | .map(|record: RefMulti| record.key().clone()) 147 | .collect(); 148 | 149 | let result: Vec> = 150 | items.iter().map(|key: &KeyType| self.remove(key)).collect(); 151 | result 152 | } 153 | 154 | fn len(&self) -> usize { 155 | self.memory.len() 156 | } 157 | 158 | fn is_empty(&self) -> bool { 159 | self.memory.is_empty() 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /memcrs/src/memcache_server/client_handler.rs: -------------------------------------------------------------------------------- 1 | use std::net::SocketAddr; 2 | use std::sync::Arc; 3 | use std::time::Duration; 4 | use tokio::io; 5 | use tokio::net::TcpStream; 6 | use tokio::sync::Semaphore; 7 | use tokio::time::timeout; 8 | use tracing::{debug, error}; 9 | 10 | //use tracing_attributes::instrument; 11 | 12 | use super::handler; 13 | use crate::memcache::store as storage; 14 | use crate::protocol::binary_codec::{BinaryRequest, BinaryResponse}; 15 | use crate::protocol::binary_connection::MemcacheBinaryConnection; 16 | 17 | pub struct ClientConfig { 18 | pub(crate) item_memory_limit: u32, 19 | pub(crate) rx_timeout_secs: u32, 20 | pub(crate) _wx_timeout_secs: u32, 21 | } 22 | pub struct Client { 23 | stream: MemcacheBinaryConnection, 24 | addr: SocketAddr, 25 | config: ClientConfig, 26 | handler: handler::BinaryHandler, 27 | /// Max connection semaphore. 28 | /// 29 | /// When the handler is dropped, a permit is returned to this semaphore. If 30 | /// the listener is waiting for connections to close, it will be notified of 31 | /// the newly available permit and resume accepting connections. 32 | limit_connections: Arc, 33 | } 34 | 35 | impl Client { 36 | pub fn new( 37 | store: Arc, 38 | socket: TcpStream, 39 | addr: SocketAddr, 40 | config: ClientConfig, 41 | limit_connections: Arc, 42 | ) -> Self { 43 | Client { 44 | stream: MemcacheBinaryConnection::new(socket, config.item_memory_limit), 45 | addr, 46 | config, 47 | handler: handler::BinaryHandler::new(store), 48 | limit_connections, 49 | } 50 | } 51 | 52 | pub async fn handle(&mut self) { 53 | debug!("New client connected: {}", self.addr); 54 | 55 | // Here for every packet we get back from the `Framed` decoder, 56 | // we parse the request, and if it's valid we generate a response 57 | // based on the values in the storage. 58 | loop { 59 | match timeout( 60 | Duration::from_secs(self.config.rx_timeout_secs as u64), 61 | self.stream.read_frame(), 62 | ) 63 | .await 64 | { 65 | Ok(req_or_none) => { 66 | let client_close = self.handle_frame(req_or_none).await; 67 | if client_close { 68 | return; 69 | } 70 | } 71 | Err(err) => { 72 | debug!( 73 | "Timeout {}s elapsed, disconecting client: {}, error: {}", 74 | self.config.rx_timeout_secs, self.addr, err 75 | ); 76 | return; 77 | } 78 | } 79 | } 80 | } 81 | 82 | async fn handle_frame(&mut self, req: Result, io::Error>) -> bool { 83 | match req { 84 | Ok(re) => { 85 | match re { 86 | Some(request) => self.handle_request(request).await, 87 | None => { 88 | // The connection will be closed at this point as `lines.next()` has returned `None`. 89 | debug!("Connection closed: {}", self.addr); 90 | true 91 | } 92 | } 93 | } 94 | Err(err) => { 95 | error!("Error when reading frame; error = {:?}", err); 96 | true 97 | } 98 | } 99 | } 100 | 101 | /// Handles single memcached binary request 102 | /// Returns true if we should leave client receive loop 103 | async fn handle_request(&mut self, request: BinaryRequest) -> bool { 104 | debug!("Got request {:?}", request.get_header()); 105 | 106 | if let BinaryRequest::QuitQuietly(_req) = request { 107 | debug!("Closing client socket quit quietly"); 108 | if let Err(_e) = self.stream.shutdown().await.map_err(log_error) {} 109 | return true; 110 | } 111 | 112 | let resp = self.handler.handle_request(request); 113 | match resp { 114 | Some(response) => { 115 | let mut socket_close = false; 116 | if let BinaryResponse::Quit(_resp) = &response { 117 | socket_close = true; 118 | } 119 | 120 | debug!("Sending response {:?}", response); 121 | if let Err(e) = self.stream.write(&response).await { 122 | error!("error on sending response; error = {:?}", e); 123 | return true; 124 | } 125 | 126 | if socket_close { 127 | debug!("Closing client socket quit command"); 128 | if let Err(_e) = self.stream.shutdown().await.map_err(log_error) {} 129 | return true; 130 | } 131 | false 132 | } 133 | None => false, 134 | } 135 | } 136 | } 137 | 138 | impl Drop for Client { 139 | fn drop(&mut self) { 140 | // Add a permit back to the semaphore. 141 | // 142 | // Doing so unblocks the listener if the max number of 143 | // connections has been reached. 144 | // 145 | // This is done in a `Drop` implementation in order to guarantee that 146 | // the permit is added even if the task handling the connection panics. 147 | // If `add_permit` was called at the end of the `run` function and some 148 | // bug causes a panic. The permit would never be returned to the 149 | // semaphore. 150 | self.limit_connections.add_permits(1); 151 | } 152 | } 153 | 154 | fn log_error(e: io::Error) { 155 | // in most cases its not an error 156 | // client may just drop connection i.e. like 157 | // php client does 158 | if e.kind() == io::ErrorKind::NotConnected { 159 | info!("Error: {}", e); 160 | } else { 161 | error!("Error: {}", e); 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /memcrs/src/memcache/store.rs: -------------------------------------------------------------------------------- 1 | use bytes::{Bytes, BytesMut}; 2 | 3 | use crate::cache::error::{CacheError, Result}; 4 | use crate::cache::cache::{ 5 | Cache, KeyType as CacheKeyType, CacheMetaData as CacheMeta, Record as CacheRecord, SetStatus as CacheSetStatus, 6 | }; 7 | 8 | 9 | use std::str; 10 | use std::sync::Arc; 11 | 12 | pub type Record = CacheRecord; 13 | pub type Meta = CacheMeta; 14 | pub type SetStatus = CacheSetStatus; 15 | pub type KeyType = CacheKeyType; 16 | 17 | #[derive(Clone)] 18 | pub struct DeltaParam { 19 | pub(crate) delta: u64, 20 | pub(crate) value: u64, 21 | } 22 | pub type IncrementParam = DeltaParam; 23 | pub type DecrementParam = IncrementParam; 24 | 25 | pub type DeltaResultValueType = u64; 26 | #[derive(Debug)] 27 | pub struct DeltaResult { 28 | pub cas: u64, 29 | pub value: DeltaResultValueType, 30 | } 31 | /** 32 | * Implements Memcache commands based 33 | * on Key Value Store 34 | */ 35 | pub struct MemcStore { 36 | store: Arc, 37 | } 38 | 39 | impl MemcStore { 40 | pub fn new(store: Arc) -> MemcStore { 41 | MemcStore { store } 42 | } 43 | 44 | pub fn set(&self, key: KeyType, record: Record) -> Result { 45 | self.store.set(key, record) 46 | } 47 | 48 | pub fn get(&self, key: &KeyType) -> Result { 49 | self.store.get(key) 50 | } 51 | 52 | // fn touch_record(&self, _record: &mut Record) { 53 | // let _timer = self.timer.secs(); 54 | // } 55 | 56 | pub fn add(&self, key: KeyType, record: Record) -> Result { 57 | match self.get(&key) { 58 | Ok(_record) => Err(CacheError::KeyExists), 59 | Err(_err) => self.set(key, record), 60 | } 61 | } 62 | 63 | pub fn replace(&self, key: KeyType, record: Record) -> Result { 64 | match self.get(&key) { 65 | Ok(_record) => self.set(key, record), 66 | Err(_err) => Err(CacheError::NotFound), 67 | } 68 | } 69 | 70 | pub fn append(&self, key: KeyType, new_record: Record) -> Result { 71 | match self.get(&key) { 72 | Ok(mut record) => { 73 | record.header.cas = new_record.header.cas; 74 | let mut value = 75 | BytesMut::with_capacity(record.value.len() + new_record.value.len()); 76 | value.extend_from_slice(&record.value); 77 | value.extend_from_slice(&new_record.value); 78 | record.value = value.freeze(); 79 | self.set(key, record) 80 | } 81 | Err(_err) => Err(CacheError::NotFound), 82 | } 83 | } 84 | 85 | pub fn prepend(&self, key: KeyType, new_record: Record) -> Result { 86 | match self.get(&key) { 87 | Ok(mut record) => { 88 | let mut value = 89 | BytesMut::with_capacity(record.value.len() + new_record.value.len()); 90 | value.extend_from_slice(&new_record.value); 91 | value.extend_from_slice(&record.value); 92 | record.value = value.freeze(); 93 | record.header.cas = new_record.header.cas; 94 | self.set(key, record) 95 | } 96 | Err(_err) => Err(CacheError::NotFound), 97 | } 98 | } 99 | 100 | pub fn increment( 101 | &self, 102 | header: Meta, 103 | key: KeyType, 104 | increment: IncrementParam, 105 | ) -> Result { 106 | self.add_delta(header, key, increment, true) 107 | } 108 | 109 | pub fn decrement( 110 | &self, 111 | header: Meta, 112 | key: KeyType, 113 | decrement: DecrementParam, 114 | ) -> Result { 115 | self.add_delta(header, key, decrement, false) 116 | } 117 | 118 | fn add_delta( 119 | &self, 120 | header: Meta, 121 | key: KeyType, 122 | delta: DeltaParam, 123 | increment: bool, 124 | ) -> Result { 125 | match self.get(&key) { 126 | Ok(mut record) => { 127 | str::from_utf8(&record.value) 128 | .map(|value: &str| { 129 | value 130 | .parse::() 131 | .map_err(|_err| CacheError::ArithOnNonNumeric) 132 | }) 133 | .map_err(|_err| CacheError::ArithOnNonNumeric) 134 | .and_then(|value: std::result::Result| { 135 | //flatten result 136 | value 137 | }) 138 | .map(|mut value: u64| { 139 | if increment { 140 | value += delta.delta; 141 | } else if delta.delta > value { 142 | value = 0; 143 | } else { 144 | value -= delta.delta; 145 | } 146 | record.value = Bytes::from(value.to_string()); 147 | record.header = header; 148 | self.set(key, record).map(|result| DeltaResult { 149 | cas: result.cas, 150 | value, 151 | }) 152 | }) 153 | .and_then(|result: std::result::Result| { 154 | //flatten result 155 | result 156 | }) 157 | } 158 | Err(_err) => { 159 | if header.get_expiration() != 0xffffffff { 160 | let record = Record::new( 161 | Bytes::from(delta.value.to_string()), 162 | 0, 163 | 0, 164 | header.get_expiration(), 165 | ); 166 | return self.set(key, record).map(|result| DeltaResult { 167 | cas: result.cas, 168 | value: delta.value, 169 | }); 170 | } 171 | Err(CacheError::NotFound) 172 | } 173 | } 174 | } 175 | 176 | pub fn delete(&self, key: KeyType, header: Meta) -> Result { 177 | self.store.delete(key, header) 178 | } 179 | 180 | pub fn flush(&self, header: Meta) { 181 | self.store.flush(header) 182 | } 183 | } 184 | 185 | #[cfg(test)] 186 | mod storage_tests; 187 | -------------------------------------------------------------------------------- /memcrs/src/protocol/binary.rs: -------------------------------------------------------------------------------- 1 | use bytes::Bytes; 2 | use num_derive::{FromPrimitive, ToPrimitive}; 3 | use serde_derive::{Deserialize, Serialize}; 4 | 5 | #[derive(FromPrimitive, ToPrimitive, Debug)] 6 | #[repr(u8)] 7 | pub enum Magic { 8 | Request = 0x80, 9 | Response = 0x81, 10 | } 11 | 12 | #[derive(FromPrimitive, ToPrimitive)] 13 | #[repr(u16)] 14 | pub enum ResponseStatus { 15 | Success = 0x00, 16 | KeyNotExists = 0x01, 17 | KeyExists = 0x02, 18 | TooBig = 0x03, 19 | InvalidArguments = 0x04, 20 | NotStored = 0x05, 21 | NonNumericValue = 0x06, 22 | AuthenticationError = 0x20, 23 | AuthenticationContinue = 0x21, 24 | UnkownError = 0x81, 25 | NotEnoughMemory = 0x82, 26 | } 27 | 28 | #[derive(FromPrimitive, ToPrimitive)] 29 | #[repr(u8)] 30 | pub enum DataTypes { 31 | RawBytes = 0x00, 32 | } 33 | 34 | #[derive(FromPrimitive, ToPrimitive, Copy, Clone)] 35 | #[repr(u8)] 36 | pub enum Command { 37 | Get = 0x00, 38 | Set = 0x01, 39 | Add = 0x02, 40 | Replace = 0x03, 41 | Delete = 0x04, 42 | Increment = 0x05, 43 | Decrement = 0x06, 44 | Quit = 0x07, 45 | Flush = 0x08, 46 | GetQuiet = 0x09, 47 | Noop = 0x0a, 48 | Version = 0x0b, 49 | GetKey = 0x0c, 50 | GetKeyQuiet = 0x0d, 51 | Append = 0x0e, 52 | Prepend = 0x0f, 53 | Stat = 0x10, 54 | SetQuiet = 0x11, 55 | AddQuiet = 0x12, 56 | ReplaceQuiet = 0x13, 57 | DeleteQuiet = 0x14, 58 | IncrementQuiet = 0x15, 59 | DecrementQuiet = 0x16, 60 | QuitQuiet = 0x17, 61 | FlushQuiet = 0x18, 62 | AppendQuiet = 0x19, 63 | PrependQuiet = 0x1a, 64 | Touch = 0x1c, 65 | GetAndTouch = 0x1d, 66 | GetAndTouchQuiet = 0x1e, 67 | 68 | SaslListMechs = 0x20, 69 | SaslAuth = 0x21, 70 | SaslStep = 0x22, 71 | 72 | GetAndTouchKey = 0x23, 73 | GetAndTouchKeyQuiet = 0x24, 74 | 75 | OpCodeMax = 0x25, 76 | } 77 | 78 | #[derive(Serialize, Deserialize, Debug, Copy, Clone, Default)] 79 | pub struct RequestHeader { 80 | pub(crate) magic: u8, 81 | pub(crate) opcode: u8, 82 | pub(crate) key_length: u16, 83 | pub(crate) extras_length: u8, 84 | pub(crate) data_type: u8, 85 | pub(crate) vbucket_id: u16, 86 | pub(crate) body_length: u32, 87 | pub(crate) opaque: u32, 88 | pub(crate) cas: u64, 89 | } 90 | 91 | #[derive(Serialize, Deserialize, Debug, Copy, Clone, Default)] 92 | pub struct ResponseHeader { 93 | pub magic: u8, 94 | pub opcode: u8, 95 | pub key_length: u16, 96 | pub extras_length: u8, 97 | pub data_type: u8, 98 | pub status: u16, 99 | pub body_length: u32, 100 | pub opaque: u32, 101 | pub cas: u64, 102 | } 103 | 104 | impl ResponseHeader { 105 | pub fn new(cmd: u8, opaque: u32) -> Self { 106 | ResponseHeader { 107 | magic: Magic::Response as u8, 108 | opcode: cmd, 109 | opaque, 110 | ..ResponseHeader::default() 111 | } 112 | } 113 | } 114 | 115 | #[derive(Serialize, Deserialize, Debug)] 116 | pub struct Request { 117 | pub(crate) header: RequestHeader, 118 | } 119 | 120 | #[derive(Serialize, Deserialize, Debug)] 121 | pub struct Response { 122 | pub header: ResponseHeader, 123 | } 124 | 125 | pub type NoopRequest = Request; 126 | pub type NoopResponse = Response; 127 | 128 | pub type VersionRequest = Request; 129 | #[derive(Serialize, Deserialize, Debug)] 130 | pub struct VersionResponse { 131 | pub header: ResponseHeader, 132 | pub version: String, 133 | } 134 | 135 | #[derive(Serialize, Deserialize, Debug)] 136 | pub struct ErrorResponse { 137 | pub header: ResponseHeader, 138 | pub error: &'static str, 139 | } 140 | 141 | #[derive(Debug)] 142 | pub struct GetRequest { 143 | pub(crate) header: RequestHeader, 144 | pub(crate) key: Bytes, 145 | } 146 | 147 | pub type GetQuietRequest = GetRequest; 148 | pub type GetKeyRequest = GetRequest; 149 | pub type GetKeyQuietRequest = GetRequest; 150 | 151 | #[derive(Debug)] 152 | pub struct GetResponse { 153 | pub(crate) header: ResponseHeader, 154 | pub(crate) flags: u32, 155 | pub(crate) key: Bytes, 156 | pub(crate) value: Bytes, 157 | } 158 | 159 | pub type DeleteRequest = GetRequest; 160 | pub type DeleteResponse = Response; 161 | 162 | pub type GetQuietlyResponse = GetResponse; 163 | pub type GetKeyResponse = GetResponse; 164 | pub type GetKeyQuietlyResponse = GetResponse; 165 | 166 | #[derive(Clone, Debug)] 167 | pub struct SetRequest { 168 | pub(crate) header: RequestHeader, 169 | pub(crate) flags: u32, 170 | pub(crate) expiration: u32, 171 | pub(crate) key: Bytes, 172 | pub(crate) value: Bytes, 173 | } 174 | 175 | pub type AddRequest = SetRequest; 176 | pub type ReplaceRequest = SetRequest; 177 | 178 | #[derive(Debug)] 179 | pub struct AppendRequest { 180 | pub(crate) header: RequestHeader, 181 | pub(crate) key: Bytes, 182 | pub(crate) value: Bytes, 183 | } 184 | 185 | pub type PrependRequest = AppendRequest; 186 | pub type AppendResponse = Response; 187 | pub type PrependResponse = Response; 188 | 189 | pub type SetResponse = Response; 190 | pub type AddResponse = Response; 191 | pub type ReplaceResponse = Response; 192 | 193 | #[derive(Debug)] 194 | pub struct IncrementRequest { 195 | pub(crate) header: RequestHeader, 196 | pub(crate) delta: u64, 197 | pub(crate) initial: u64, 198 | pub(crate) expiration: u32, 199 | pub(crate) key: Bytes, 200 | } 201 | #[derive(Serialize, Deserialize, Debug)] 202 | pub struct IncrementResponse { 203 | pub(crate) header: ResponseHeader, 204 | pub(crate) value: u64, 205 | } 206 | 207 | pub type DecrementRequest = IncrementRequest; 208 | pub type DecrementResponse = IncrementResponse; 209 | 210 | #[derive(Serialize, Deserialize, Debug)] 211 | pub struct TouchRequest { 212 | pub(crate) expiration: u32, 213 | } 214 | 215 | pub type TouchResponse = Response; 216 | 217 | #[derive(Serialize, Deserialize, Debug)] 218 | pub struct FlushRequest { 219 | pub(crate) header: RequestHeader, 220 | pub(crate) expiration: u32, 221 | } 222 | pub type FlushResponse = Response; 223 | 224 | pub type QuitRequest = Request; 225 | pub type QuitResponse = Response; 226 | 227 | pub type StatsRequest = Request; 228 | #[derive(Debug)] 229 | pub struct StatsResponse { 230 | pub(crate) header: ResponseHeader, 231 | } 232 | 233 | // pub struct StatsResponseRecord { 234 | // pub(crate) header: ResponseHeader, 235 | // pub(crate) key: Vec, 236 | // pub(crate) value: Bytes, 237 | // } 238 | // #[derive(Debug)] 239 | // pub struct StatsResponse { 240 | // pub(crate) records: Vec, 241 | // } 242 | 243 | /* TODO Get And Touch (GAT) */ 244 | -------------------------------------------------------------------------------- /memcrs/src/memcache/cli/parser.rs: -------------------------------------------------------------------------------- 1 | use crate::version; 2 | use byte_unit::{Byte, ByteUnit}; 3 | use clap::{command, crate_authors, value_parser, Arg}; 4 | use std::net::IpAddr; 5 | use tracing; 6 | 7 | pub enum RuntimeType { 8 | CurrentThread, 9 | MultiThread, 10 | } 11 | 12 | impl RuntimeType { 13 | pub fn as_str(&self) -> &'static str { 14 | match self { 15 | RuntimeType::CurrentThread => "Work handled withing current thread runtime", 16 | RuntimeType::MultiThread => "Work stealing threadpool runtime", 17 | } 18 | } 19 | } 20 | 21 | pub struct MemcrsArgs { 22 | pub port: u16, 23 | pub connection_limit: u32, 24 | pub backlog_limit: u32, 25 | pub memory_limit_mb: u64, 26 | pub item_size_limit: Byte, 27 | pub memory_limit: u64, 28 | pub threads: usize, 29 | pub log_level: tracing::Level, 30 | pub listen_address: IpAddr, 31 | pub runtime_type: RuntimeType, 32 | } 33 | 34 | const DEFAULT_PORT: u16 = 11211; 35 | const DEFAULT_ADDRESS: &str = "127.0.0.1"; 36 | const CONNECTION_LIMIT: u32 = 1024; 37 | const LISTEN_BACKLOG: u32 = 1024; 38 | const MEMORY_LIMIT: u64 = 64; 39 | const MAX_ITEM_SIZE: &str = "1m"; 40 | const NUMBER_OF_THREADS: usize = 4; 41 | const RUNTIME_TYPE: &str = "current"; 42 | 43 | impl MemcrsArgs { 44 | fn from_args(threads: String, args: Vec) -> Result { 45 | let number_of_threads: usize = threads.parse().unwrap_or(NUMBER_OF_THREADS); 46 | let matches = cli_args(&threads).get_matches_from(args); 47 | 48 | let port: u16 = *matches.get_one::("port").unwrap_or(&DEFAULT_PORT); 49 | 50 | let connection_limit: u32 = *matches 51 | .get_one::("connection-limit") 52 | .unwrap_or(&CONNECTION_LIMIT); 53 | 54 | let backlog_limit: u32 = *matches 55 | .get_one::("listen-backlog") 56 | .unwrap_or(&LISTEN_BACKLOG); 57 | 58 | let memory_limit_mb: u64 = *matches 59 | .get_one::("memory-limit") 60 | .unwrap_or(&MEMORY_LIMIT); 61 | 62 | let memory_limit_res = Byte::from_unit(memory_limit_mb as f64, ByteUnit::MiB).unwrap(); 63 | let memory_limit: u64 = memory_limit_res.get_bytes() as u64; 64 | 65 | let item_size_limit_str: String = matches 66 | .get_one::("max-item-size") 67 | .unwrap_or(&String::from(MAX_ITEM_SIZE)) 68 | .clone(); 69 | 70 | let item_size_limit_res = Byte::from_str(item_size_limit_str).unwrap(); 71 | let item_size_limit_max = Byte::from_unit(1000f64, ByteUnit::MiB).unwrap(); 72 | 73 | if item_size_limit_res.get_bytes() > item_size_limit_max.get_bytes() { 74 | return Err(format!( 75 | "Max item size cannot be greater than: {}", 76 | item_size_limit_max.get_appropriate_unit(false) 77 | )); 78 | } 79 | 80 | let threads: usize = *matches 81 | .get_one::("threads") 82 | .unwrap_or(&number_of_threads); 83 | 84 | let listen_address = match matches 85 | .get_one::("listen") 86 | .unwrap_or(&String::from(DEFAULT_ADDRESS)) 87 | .parse::() 88 | { 89 | Ok(ip_addr) => ip_addr, 90 | Err(err) => return Err(format!("Invalid ip address: {}", err)), 91 | }; 92 | 93 | let runtime_type = match matches 94 | .get_one::("runtime-type") 95 | .unwrap_or(&String::from(RUNTIME_TYPE)) 96 | .as_str() 97 | { 98 | "current" => RuntimeType::CurrentThread, 99 | "multi" => RuntimeType::MultiThread, 100 | _ => unreachable!(), 101 | }; 102 | 103 | // Vary the output based on how many times the user used the "verbose" flag 104 | // (i.e. 'myprog -v -v -v' or 'myprog -vvv' vs 'myprog -v' 105 | let log_level = match matches.get_count("v") { 106 | 0 => tracing::Level::ERROR, 107 | 1 => tracing::Level::WARN, 108 | 2 => tracing::Level::INFO, 109 | 3 => tracing::Level::DEBUG, 110 | _ => tracing::Level::TRACE, 111 | }; 112 | 113 | Ok(MemcrsArgs { 114 | port, 115 | connection_limit, 116 | backlog_limit, 117 | memory_limit_mb, 118 | item_size_limit: item_size_limit_res, 119 | memory_limit, 120 | threads, 121 | log_level, 122 | listen_address, 123 | runtime_type, 124 | }) 125 | } 126 | } 127 | 128 | fn cli_args(threads: & str) -> clap::Command { 129 | command!() 130 | .version(version::MEMCRS_VERSION) 131 | .author(crate_authors!("\n")) 132 | .about("memcrsd - memcached compatible server implementation in Rust") 133 | .arg( 134 | Arg::new("port") 135 | .short('p') 136 | .long("port") 137 | .default_value("11211") 138 | .value_parser(value_parser!(u16)) 139 | .help("TCP port to listen on") 140 | .takes_value(true), 141 | ) 142 | .arg( 143 | Arg::new("listen") 144 | .short('l') 145 | .long("listen") 146 | .default_value("127.0.0.1") 147 | .help("interface to listen on") 148 | .takes_value(true), 149 | ) 150 | .arg( 151 | Arg::new("connection-limit") 152 | .short('c') 153 | .long("connection-limit") 154 | .value_parser(value_parser!(u32)) 155 | .default_value("1024") 156 | .help("max simultaneous connections") 157 | .takes_value(true), 158 | ) 159 | .arg( 160 | Arg::new("listen-backlog") 161 | .short('b') 162 | .long("listen-backlog") 163 | .value_parser(value_parser!(u32)) 164 | .default_value("1024") 165 | .help("set the backlog queue limit") 166 | .takes_value(true), 167 | ) 168 | .arg( 169 | Arg::new("v") 170 | .short('v') 171 | .action(clap::ArgAction::Count) 172 | .help("Sets the level of verbosity"), 173 | ) 174 | .arg( 175 | Arg::new("memory-limit") 176 | .short('m') 177 | .long("memory-limit") 178 | .value_parser(value_parser!(u64)) 179 | .default_value("64") 180 | .help("item memory in megabytes") 181 | .takes_value(true), 182 | ) 183 | .arg( 184 | Arg::new("max-item-size") 185 | .short('I') 186 | .long("max-item-size") 187 | .default_value("1m") 188 | .help("adjusts max item size (min: 1k, max: 1024m)") 189 | .takes_value(true), 190 | ) 191 | .arg( 192 | Arg::new("threads") 193 | .short('t') 194 | .long("threads") 195 | .value_parser(value_parser!(usize)) 196 | .default_value(threads) 197 | .help("number of threads to use") 198 | .takes_value(true), 199 | ) 200 | .arg( 201 | Arg::new("runtime-type") 202 | .short('r') 203 | .long("runtime-type") 204 | .default_value("current") 205 | .value_parser(["current", "multi"]) 206 | .help("runtime type to use") 207 | .takes_value(true), 208 | ) 209 | } 210 | 211 | pub fn parse(runtimes: String, args: Vec) -> Result { 212 | MemcrsArgs::from_args(runtimes, args) 213 | } 214 | 215 | #[cfg(test)] 216 | mod tests { 217 | use crate::memcache::cli::parser::cli_args; 218 | #[test] 219 | fn verify_cli() { 220 | cli_args(&"8".to_string()).debug_assert(); 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # memcrsd memcached server implementation in Rust 2 | 3 | memcrsd is a key value store implementation in Rust. It is compatible with binary protocol of memcached server. 4 | 5 | ## Supported features and compatibility 6 | 7 | To check compatibility with memcached server implementation memcrsd project 8 | is using [memcapable](http://docs.libmemcached.org/bin/memcapable.html) tool from [libmemcached library](https://libmemcached.org/libMemcached.html) 9 | 10 | Here is a capability status for memcrsd: 11 | 12 | ```sh 13 | ./memcapable -h 127.0.0.1 -b -p 11211 14 | Hostname was provided.127.0.0.1 15 | binary noop [pass] 16 | binary quit [pass] 17 | binary quitq [pass] 18 | binary set [pass] 19 | binary setq [pass] 20 | binary flush [pass] 21 | binary flushq [pass] 22 | binary add [pass] 23 | binary addq [pass] 24 | binary replace [pass] 25 | binary replaceq [pass] 26 | binary delete [pass] 27 | binary deleteq [pass] 28 | binary get [pass] 29 | binary getq [pass] 30 | binary getk [pass] 31 | binary getkq [pass] 32 | binary incr [pass] 33 | binary incrq [pass] 34 | binary decr [pass] 35 | binary decrq [pass] 36 | binary version [pass] 37 | binary append [pass] 38 | binary appendq [pass] 39 | binary prepend [pass] 40 | binary prependq [pass] 41 | binary stat [pass] 42 | All tests passed 43 | ``` 44 | 45 | ## Bug reports 46 | 47 | Feel free to use the issue tracker on github. 48 | 49 | **If you are reporting a security bug** please contact a maintainer privately. 50 | We follow responsible disclosure: we handle reports privately, prepare a 51 | patch, allow notifications to vendor lists. Then we push a fix release and your 52 | bug can be posted publicly with credit in our release notes and commit 53 | history. 54 | 55 | ## Website 56 | 57 | * [https://memc.rs/](https://memc.rs/) 58 | 59 | ## Docker image 60 | 61 | Docker image is available at docker hub: [https://hub.docker.com/r/memcrs/memc-rs](https://hub.docker.com/r/memcrs/memc-rs) 62 | 63 | ### Building docker image 64 | 65 | Docker image contains only one binary, so image is pretty small(~8MB). To be able to build docker image additional memory needs to be granted to container that builds final image. Building docker image is divided in 2 stages. In stage one rust:latest image is used to compile static binary and the second stage contains just copies built binary into to final image. 66 | 67 | To build docker image memcrsd sources have to be cloned and `docker build -m 512m .` command executed: 68 | 69 | ```sh 70 | git clone git@github.com:memc-rs/memc-rs.git memc-rs 71 | cd memc-rs 72 | docker build -m 4096m . 73 | ``` 74 | 75 | ### Publishing docker image 76 | 77 | 78 | ```sh 79 | git checkout memcrsd-0.0.1b 80 | docker pull rust 81 | docker build -m 4096m -t memcrsd . 82 | docker images 83 | # tag docker image 84 | docker tag 769dba683c8b memcrs/memc-rs:0.0.1b 85 | docker tag memcrs/memc-rs:0.0.1b memcrs/memc-rs:latest 86 | docker push memcrs/memc-rs:latest 87 | docker push memcrs/memc-rs:0.0.1b 88 | ``` 89 | 90 | ### Getting docker image from docker hub 91 | 92 | To get latest version of memcrsd run following command: 93 | 94 | ```sh 95 | docker image pull memcrs/memc-rs:latest 96 | ``` 97 | 98 | If you want specific version please take a look at available tags: [https://hub.docker.com/r/memcrs/memc-rs/tags](https://hub.docker.com/r/memcrs/memc-rs/tags) 99 | 100 | ### Runnig docker image 101 | 102 | ```sh 103 | docker run -p 127.0.0.1:11211:11211/tcp -d memcrs/memc-rs 104 | ``` 105 | 106 | ## Testing 107 | 108 | memcrsd project is tested using different types of tests: 109 | 110 | * unit testing, 111 | * fuzzy testing, 112 | * end-2-end tests 113 | 114 | ### Fuzzy testing 115 | 116 | At the moment decoding network packets is fuzzy tested. 117 | 118 | ```sh 119 | cargo install -f cargo-fuzz 120 | cargo +nightly fuzz build 121 | cargo +nightly fuzz run -j 8 fuzz_binary_decoder -- -rss_limit_mb=4192 -timeout=60 122 | cargo +nightly fuzz coverage fuzz_binary_decoder 123 | ``` 124 | 125 | Workaround to see fuzzing test results: 126 | 127 | ```sh 128 | ~/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/bin/llvm-cov show fuzz/target/x86_64-unknown-linux-gnu/release/fuzz_binary_decoder --format=html -Xdemangler=rustfilt --ignore-filename-regex="\.cargo" -instr-profile=fuzz/coverage/fuzz_binary_decoder/coverage.profdata > index.html 129 | ``` 130 | 131 | ### Generating test coverage reporting 132 | 133 | To generate test coverage there is a convenient shell script that compiles and executed unit tests: 134 | 135 | ```sh 136 | cd memcrs 137 | ./coverage.sh 138 | firefox ../target/debug/coverage/index.html 139 | ``` 140 | 141 | To be able to produce coverage reports `grcov` tool needs to be installed: 142 | 143 | ```sh 144 | cargo install grcov 145 | ``` 146 | 147 | The plan in the future is to have coverage ~90%. 148 | 149 | ### Integration testing 150 | 151 | For end-to-end integration testing at the moment memcrsd project is using memcapable tool from libmemcache 152 | library. In the future memclt binary will be used to perform end-to-end tests. 153 | 154 | ## Measuring performance 155 | 156 | Measuring performance can be tricky, thats why to measure performance memcrsd 157 | project is using industry standard benchmarking tool for measuring performance 158 | of memcached server which is memtier_benchmark. 159 | This tool can be used to generate various traffic patterns. It provides a robust 160 | set of customization and reporting capabilities all wrapped into a convenient and 161 | easy-to-use command-line interface. 162 | More information about memtier benchmark tool can be found on [RedisLabs blog.](https://redislabs.com/blog/memtier_benchmark-a-high-throughput-benchmarking-tool-for-redis-memcached/) 163 | 164 | ### Memtier benchmark installation 165 | 166 | Memtier benchmark is available on github, it needs to be cloned and compiled: 167 | 168 | ```sh 169 | git clone https://github.com/RedisLabs/memtier_benchmark.git 170 | autoreconf -ivf 171 | ./configure 172 | make 173 | make install 174 | ``` 175 | 176 | ### Generating flamegraph 177 | 178 | To be able to sample and resolve Kernel functions: 179 | 180 | * we need to expose kernel addresses([kptr_restrict](https://sysctl-explorer.net/kernel/kptr_restrict/)) 181 | * grant unprivileged users access to performance events in kernel([perf_event_paranoid](https://sysctl-explorer.net/kernel/perf_event_paranoid/)) 182 | 183 | ```sh 184 | sudo sh -c " echo 0 > /proc/sys/kernel/kptr_restrict" 185 | sudo sh -c " echo -1 >> /proc/sys/kernel/perf_event_paranoid" 186 | ``` 187 | 188 | Generating flamegraphs: 189 | 190 | ```sh 191 | sudo apt install -y linux-tools-common linux-tools-generic 192 | cargo install flamegraph 193 | cd ~/projects/memix 194 | cargo flamegraph 195 | cargo flamegraph --bin memcrsd 196 | ``` 197 | 198 | ### Attaching perf 199 | 200 | By default release profile is built with debug symbols, see Cargo.toml: 201 | 202 | ```toml 203 | [profile.release] 204 | opt-level = 3 205 | debug=true 206 | ``` 207 | 208 | On Ubuntu install required packages: 209 | 210 | ```sh 211 | sudo apt install -y linux-tools-common linux-tools-generic 212 | ``` 213 | 214 | We can start a server: 215 | 216 | ```sh 217 | ./target/release/memcrsd -v & perf record -F 99 -p `pgrep memcrsd` 218 | ``` 219 | 220 | * First we run the memcrsd server and we send it to the background using the ampersand (&) symbol. 221 | * Next to it, so it executes immediately, we run perf that receives the process identifier (PID) courtesy of pgrep memcrsd. 222 | * The pgrep command returns the PID of a process by name. 223 | 224 | After benchmarks are executed reports can be displayed using perf report: 225 | 226 | ```sh 227 | perf report 228 | ``` 229 | -------------------------------------------------------------------------------- /memcrs/src/protocol/binary_codec/binary_encoder_tests.rs: -------------------------------------------------------------------------------- 1 | #[allow(unused)] 2 | use super::*; 3 | 4 | #[cfg(test)] 5 | mod tests { 6 | use super::*; 7 | use crate::mock::value::from_string; 8 | use crate::cache::error; 9 | 10 | fn create_response_header( 11 | cmd: binary::Command, 12 | opaque: u32, 13 | cas: u64, 14 | ) -> binary::ResponseHeader { 15 | let mut response_header = binary::ResponseHeader::new(cmd as u8, opaque); 16 | response_header.cas = cas; 17 | response_header 18 | } 19 | 20 | fn encode_packet(src: BinaryResponse) -> Result { 21 | let mut encoder = MemcacheBinaryCodec::new(1024); 22 | let mut buf = BytesMut::with_capacity(128); 23 | match encoder.encode(src, &mut buf) { 24 | Ok(_) => Ok(buf), 25 | Err(err) => Err(err), 26 | } 27 | } 28 | 29 | fn test_encode(expected_result: &[u8], response: BinaryResponse) { 30 | let encode_result = encode_packet(response); 31 | match encode_result { 32 | Ok(buf) => { 33 | assert_eq!(&buf[..], expected_result); 34 | } 35 | Err(_) => unreachable!(), 36 | } 37 | } 38 | 39 | #[test] 40 | fn encode_set_response() { 41 | let header = create_response_header(binary::Command::Set, 0xDEAD_BEEF, 0x4FE6C1); 42 | let response = BinaryResponse::Set(binary::SetResponse { header }); 43 | let expected_result: [u8; 24] = [ 44 | 0x81, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDE, 0xAD, 45 | 0xBE, 0xEF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4f, 0xe6, 0xc1, 46 | ]; 47 | 48 | test_encode(&expected_result, response); 49 | } 50 | 51 | #[test] 52 | fn encode_replace_response() { 53 | let header = create_response_header(binary::Command::Replace, 0, 4); 54 | let response = BinaryResponse::Replace(binary::ReplaceResponse { header }); 55 | let expected_result: [u8; 24] = [ 56 | 0x81, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 57 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 58 | ]; 59 | test_encode(&expected_result, response); 60 | } 61 | 62 | #[test] 63 | fn encode_add_response() { 64 | let header = create_response_header(binary::Command::Add, 0, 4); 65 | let response = BinaryResponse::Add(binary::AddResponse { header }); 66 | let expected_result: [u8; 24] = [ 67 | 0x81, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 68 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 69 | ]; 70 | test_encode(&expected_result, response); 71 | } 72 | #[test] 73 | fn encode_append_response() { 74 | let header = create_response_header(binary::Command::Append, 0, 2); 75 | let response = BinaryResponse::Append(binary::AppendResponse { header }); 76 | let expected_result: [u8; 24] = [ 77 | 0x81, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 78 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 79 | ]; 80 | test_encode(&expected_result, response); 81 | } 82 | 83 | #[test] 84 | fn encode_prepend_response() { 85 | let header = create_response_header(binary::Command::Prepend, 0, 3); 86 | let response = BinaryResponse::Prepend(binary::PrependResponse { header }); 87 | let expected_result: [u8; 24] = [ 88 | 0x81, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 89 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 90 | ]; 91 | test_encode(&expected_result, response); 92 | } 93 | 94 | #[test] 95 | fn encode_get_key_quiet_response() { 96 | let expected_result = [ 97 | 0x81, 0x0d, 0x00, 0x03, // key len 98 | 0x04, // extras len 99 | 0x00, 0x00, 0x00, // status 100 | 0x00, 0x00, 0x00, 0x0b, // total body: 11 101 | 0x00, 0x00, 0x00, 0x00, // opaque: 0 102 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, // cas: 1 103 | 0x00, 0x00, 0x00, 0x00, // flags: 104 | 0x66, 0x6f, 0x6f, // key: foo 105 | 0x74, 0x65, 0x73, 0x74, // value: test 106 | ]; 107 | let mut header = create_response_header(binary::Command::GetKeyQuiet, 0, 1); 108 | header.key_length = "foo".len() as u16; 109 | header.extras_length = 4; 110 | header.body_length = "foo".len() as u32 + "test".len() as u32 + header.extras_length as u32; 111 | let response = BinaryResponse::GetKeyQuietly(binary::GetKeyQuietlyResponse { 112 | header, 113 | flags: 0, 114 | key: Bytes::from("foo"), 115 | value: from_string("test"), 116 | }); 117 | let encode_result = encode_packet(response); 118 | match encode_result { 119 | Ok(buf) => { 120 | assert_eq!(&buf[..], expected_result); 121 | } 122 | Err(_) => unreachable!(), 123 | } 124 | } 125 | 126 | #[test] 127 | fn encode_get_response() { 128 | let expected_result = [ 129 | 0x81, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 130 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 131 | 0x33, 0x30, 0x35, 0x30, 132 | ]; 133 | let mut header = create_response_header(binary::Command::Get, 0, 13); 134 | header.key_length = 0; 135 | header.extras_length = 4; 136 | header.body_length = "3050".len() as u32 + header.extras_length as u32; 137 | let response = BinaryResponse::Get(binary::GetResponse { 138 | header, 139 | flags: 0, 140 | key: Bytes::new(), 141 | value: from_string("3050"), 142 | }); 143 | let encode_result = encode_packet(response); 144 | match encode_result { 145 | Ok(buf) => { 146 | assert_eq!(&buf[..], expected_result); 147 | } 148 | Err(_) => unreachable!(), 149 | } 150 | } 151 | 152 | #[test] 153 | fn encode_noop_response() { 154 | let header = create_response_header(binary::Command::Noop, 0, 0); 155 | let expected_result: [u8; 24] = [ 156 | 0x81, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 157 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 158 | ]; 159 | let response = BinaryResponse::Noop(binary::NoopResponse { header }); 160 | test_encode(&expected_result, response); 161 | } 162 | 163 | #[test] 164 | fn encode_delete_response() { 165 | let header = create_response_header(binary::Command::Delete, 0, 0); 166 | let expected_result: [u8; 24] = [ 167 | 0x81, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 168 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 169 | ]; 170 | let response = BinaryResponse::Delete(binary::DeleteResponse { header }); 171 | test_encode(&expected_result, response); 172 | } 173 | 174 | #[test] 175 | fn encode_flush_response() { 176 | let expected_result: [u8; 24] = [ 177 | 0x81, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 178 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 179 | ]; 180 | 181 | let header = create_response_header(binary::Command::Flush, 0, 0); 182 | let response = BinaryResponse::Flush(binary::FlushResponse { header }); 183 | test_encode(&expected_result, response); 184 | } 185 | 186 | #[test] 187 | fn encode_increment_response() { 188 | let expected_result: [u8; 32] = [ 189 | 0x81, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 190 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, // cas: 5 191 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x1c, // value: 3100 192 | ]; 193 | let mut header = create_response_header(binary::Command::Increment, 0, 5); 194 | header.body_length = 8; 195 | let response = BinaryResponse::Increment(binary::IncrementResponse { 196 | header, 197 | value: 3100, 198 | }); 199 | test_encode(&expected_result, response); 200 | } 201 | 202 | #[test] 203 | fn encode_decrement_response() { 204 | let expected_result: [u8; 32] = [ 205 | 0x81, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 206 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 207 | 0x00, 0x00, 0x0b, 0xea, 208 | ]; 209 | let mut header = create_response_header(binary::Command::Decrement, 0, 6); 210 | header.body_length = 8; 211 | let response = BinaryResponse::Decrement(binary::DecrementResponse { 212 | header, 213 | value: 3050, 214 | }); 215 | test_encode(&expected_result, response); 216 | } 217 | 218 | #[test] 219 | fn encode_version_response() { 220 | let expected_result: [u8; 29] = [ 221 | 0x81, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 222 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x2e, 0x36, 0x2e, 223 | 0x32, 224 | ]; 225 | let mut header = create_response_header(binary::Command::Version, 0, 0); 226 | header.body_length = "1.6.2".len() as u32; 227 | let response = BinaryResponse::Version(binary::VersionResponse { 228 | header, 229 | version: String::from("1.6.2"), 230 | }); 231 | test_encode(&expected_result, response); 232 | } 233 | 234 | #[test] 235 | fn encode_error_response() { 236 | let expected_result: [u8; 33] = [ 237 | 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 238 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x6f, 0x74, 0x20, 239 | 0x66, 0x6f, 0x75, 0x6e, 0x64, 240 | ]; 241 | let mut header = create_response_header(binary::Command::Get, 0, 0); 242 | header.body_length = "Not found".len() as u32; 243 | let err = error::CacheError::NotFound; 244 | header.status = error::CacheError::NotFound as u16; 245 | let response = BinaryResponse::Error(binary::ErrorResponse { 246 | header, 247 | error: err.to_static_string(), 248 | }); 249 | test_encode(&expected_result, response); 250 | } 251 | } 252 | -------------------------------------------------------------------------------- /memcrs/benchmarks/07_12_2020.md: -------------------------------------------------------------------------------- 1 | ./memtier_benchmark -s 192.168.1.153 -p 11211 -P memcache_binary --hide-histogram --show-config -c 10 -t 4 --ratio=1:1 -d 14096 --key-pattern S:S -n 100000 -R --randomize 2 | 3 | memcrsd 4 | 5 | [RUN #1] Preparing benchmark client... 6 | [RUN #1] Launching threads now... 7 | [RUN #1 100%, 272 secs] 0 threads: 4000000 ops, 37681 (avg: 14664) ops/sec, 254.80MB/sec (avg: 99.14MB/sec), 1.06 (avg: 2.73) msec latency 8 | 9 | 4 Threads 10 | 10 Connections per thread 11 | 100000 Requests per client 12 | 13 | 14 | ALL STATS 15 | ============================================================================================================================ 16 | Type Ops/sec Hits/sec Misses/sec Avg. Latency p50 Latency p99 Latency p99.9 Latency KB/sec 17 | ---------------------------------------------------------------------------------------------------------------------------- 18 | Sets 7349.63 --- --- 2.94005 3.00700 5.47100 6.30300 101493.62 19 | Gets 7349.63 7349.63 0.00 2.51215 2.43100 5.56700 7.39100 263.97 20 | Waits 0.00 --- --- --- --- --- --- --- 21 | Totals 14699.26 7349.63 0.00 2.72610 2.73500 5.50300 7.00700 101757.59 22 | 23 | 24 | memcached 25 | 26 | Writing results to stdout 27 | [RUN #1] Preparing benchmark client... 28 | [RUN #1] Launching threads now... 29 | [RUN #1 100%, 264 secs] 0 threads: 4000000 ops, 14408 (avg: 15126) ops/sec, 97.45MB/sec (avg: 102.26MB/sec), 2.78 (avg: 2.64) msec latency 30 | 31 | 4 Threads 32 | 10 Connections per thread 33 | 100000 Requests per client 34 | 35 | 36 | ALL STATS 37 | ============================================================================================================================ 38 | Type Ops/sec Hits/sec Misses/sec Avg. Latency p50 Latency p99 Latency p99.9 Latency KB/sec 39 | ---------------------------------------------------------------------------------------------------------------------------- 40 | Sets 7516.06 --- --- 2.91393 3.00700 5.43900 8.25500 103791.90 41 | Gets 7516.06 7516.06 0.00 2.37204 2.15900 6.39900 9.98300 269.95 42 | Waits 0.00 --- --- --- --- --- --- --- 43 | Totals 15032.12 7516.06 0.00 2.64298 2.59100 5.82300 9.66300 104061.85 44 | 45 | 46 | 47 | ./memtier_benchmark -s 192.168.1.153 -p 11211 -P memcache_binary --hide-histogram --show-config -c 20 -t 8 --ratio=1:10 -d 14096 --key-pattern S:S -n 100000 -R --randomize 48 | 49 | memcrs(1st) 50 | =================================================== 51 | Writing results to stdout 52 | [RUN #1] Preparing benchmark client... 53 | [RUN #1] Launching threads now... 54 | [RUN #1 100%, 195 secs] 0 threads: 16000000 ops, 417269 (avg: 81937) ops/sec, 524.43MB/sec (avg: 103.07MB/sec), 0.38 (avg: 1.95) msec latency 55 | 56 | 8 Threads 57 | 20 Connections per thread 58 | 100000 Requests per client 59 | 60 | 61 | ALL STATS 62 | ============================================================================================================================ 63 | Type Ops/sec Hits/sec Misses/sec Avg. Latency p50 Latency p99 Latency p99.9 Latency KB/sec 64 | ---------------------------------------------------------------------------------------------------------------------------- 65 | Sets 7553.19 --- --- 8.48973 8.89500 16.12700 18.04700 104298.07 66 | Gets 75531.09 3.29 75527.80 1.29763 0.87900 6.46300 14.65500 2720.14 67 | Waits 0.00 --- --- --- --- --- --- --- 68 | Totals 83084.29 3.29 75527.80 1.95146 0.91900 14.27100 16.19100 107018.20 69 | 70 | 71 | memcrs(2nd ./memtier_benchmark -s 192.168.1.153 -p 11211 -P memcache_binary --hide-histogram --show-config -c 20 -t 8 --ratio=1:10 -d 14096 --key-pattern S:S -n 100000 -R --randomize) 72 | =================================================== 73 | Writing results to stdout 74 | [RUN #1] Preparing benchmark client... 75 | [RUN #1] Launching threads now... 76 | [RUN #1 100%, 344 secs] 0 threads: 16000000 ops, 499230 (avg: 46443) ops/sec, 627.80MB/sec (avg: 58.42MB/sec), 0.32 (avg: 3.44) msec latency 77 | 78 | 8 Threads 79 | 20 Connections per thread 80 | 100000 Requests per client 81 | 82 | 83 | ALL STATS 84 | ============================================================================================================================ 85 | Type Ops/sec Hits/sec Misses/sec Avg. Latency p50 Latency p99 Latency p99.9 Latency KB/sec 86 | ---------------------------------------------------------------------------------------------------------------------------- 87 | Sets 4226.98 --- --- 5.68679 5.59900 12.22300 16.51100 58368.09 88 | Gets 42269.30 4226.98 38042.32 3.21926 1.17500 19.83900 39.67900 1522.26 89 | Waits 0.00 --- --- --- --- --- --- --- 90 | Totals 46496.27 4226.98 38042.32 3.44358 1.26300 19.71100 39.16700 59890.36 91 | 92 | 93 | memcrs(3rd ./memtier_benchmark -s 192.168.1.153 -p 11211 -P memcache_binary --hide-histogram --show-config -c 20 -t 8 --ratio=1:10 -d 14096 --key-pattern S:S -n 100000 -R --randomize) 94 | =================================================== 95 | Writing results to stdout 96 | [RUN #1] Preparing benchmark client... 97 | [RUN #1] Launching threads now... 98 | [RUN #1 100%, 334 secs] 0 threads: 16000000 ops, 393590 (avg: 47849) ops/sec, 494.53MB/sec (avg: 60.19MB/sec), 0.41 (avg: 3.34) msec latency 99 | 100 | 8 Threads 101 | 20 Connections per thread 102 | 100000 Requests per client 103 | 104 | 105 | ALL STATS 106 | ============================================================================================================================ 107 | Type Ops/sec Hits/sec Misses/sec Avg. Latency p50 Latency p99 Latency p99.9 Latency KB/sec 108 | ---------------------------------------------------------------------------------------------------------------------------- 109 | Sets 4391.72 --- --- 5.32701 5.18300 12.28700 17.53500 60642.99 110 | Gets 43916.74 4391.72 39525.02 3.14376 1.13500 18.94300 40.70300 1581.59 111 | Waits 0.00 --- --- --- --- --- --- --- 112 | Totals 48308.46 4391.72 39525.02 3.34224 1.19900 18.81500 40.44700 62224.58 113 | 114 | 115 | 116 | 117 | ---------------------------- 118 | 119 | memcached (1st /memtier_benchmark -s 192.168.1.153 -p 11211 -P memcache_binary --hide-histogram --show-config -c 20 -t 8 --ratio=1:10 -d 14096 --key-pattern S:S -n 100000 -R --randomize) 120 | =================================================== 121 | Writing results to stdout 122 | [RUN #1] Preparing benchmark client... 123 | [RUN #1] Launching threads now... 124 | [RUN #1 100%, 194 secs] 0 threads: 16000000 ops, 215619 (avg: 82305) ops/sec, 271.25MB/sec (avg: 103.53MB/sec), 0.74 (avg: 1.94) msec latency 125 | 126 | 8 Threads 127 | 20 Connections per thread 128 | 100000 Requests per client 129 | 130 | 131 | ALL STATS 132 | ============================================================================================================================ 133 | Type Ops/sec Hits/sec Misses/sec Avg. Latency p50 Latency p99 Latency p99.9 Latency KB/sec 134 | ---------------------------------------------------------------------------------------------------------------------------- 135 | Sets 7583.53 --- --- 7.46831 7.64700 16.06300 17.27900 104716.95 136 | Gets 75834.44 3.55 75830.89 1.39019 0.98300 6.39900 13.24700 2731.06 137 | Waits 0.00 --- --- --- --- --- --- --- 138 | Totals 83417.97 3.55 75830.89 1.94275 1.03100 12.28700 16.12700 107448.01 139 | 140 | 141 | memcached (2nd /memtier_benchmark -s 192.168.1.153 -p 11211 -P memcache_binary --hide-histogram --show-config -c 20 -t 8 --ratio=1:10 -d 14096 --key-pattern S:S -n 100000 -R --randomize) 142 | =================================================== 143 | Writing results to stdout 144 | [RUN #1] Preparing benchmark client... 145 | [RUN #1] Launching threads now... 146 | [RUN #1 100%, 337 secs] 0 threads: 16000000 ops, 356550 (avg: 47388) ops/sec, 448.13MB/sec (avg: 59.61MB/sec), 0.45 (avg: 3.37) msec latency 147 | 148 | 8 Threads 149 | 20 Connections per thread 150 | 100000 Requests per client 151 | 152 | 153 | ALL STATS 154 | ============================================================================================================================ 155 | Type Ops/sec Hits/sec Misses/sec Avg. Latency p50 Latency p99 Latency p99.9 Latency KB/sec 156 | ---------------------------------------------------------------------------------------------------------------------------- 157 | Sets 4338.37 --- --- 5.46580 5.40700 11.83900 17.53500 59906.34 158 | Gets 43383.27 4338.37 39044.90 3.16592 1.15100 19.07100 40.95900 1562.38 159 | Waits 0.00 --- --- --- --- --- --- --- 160 | Totals 47721.65 4338.37 39044.90 3.37500 1.23100 19.07100 39.67900 61468.72 161 | 162 | 163 | memcached (3rd /memtier_benchmark -s 192.168.1.153 -p 11211 -P memcache_binary --hide-histogram --show-config -c 20 -t 8 --ratio=1:10 -d 14096 --key-pattern S:S -n 100000 -R --randomize) 164 | =================================================== 165 | Writing results to stdout 166 | [RUN #1] Preparing benchmark client... 167 | [RUN #1] Launching threads now... 168 | [RUN #1 100%, 322 secs] 0 threads: 16000000 ops, 323900 (avg: 49583) ops/sec, 407.31MB/sec (avg: 62.37MB/sec), 0.49 (avg: 3.22) msec latency 169 | 170 | 8 Threads 171 | 20 Connections per thread 172 | 100000 Requests per client 173 | 174 | 175 | ALL STATS 176 | ============================================================================================================================ 177 | Type Ops/sec Hits/sec Misses/sec Avg. Latency p50 Latency p99 Latency p99.9 Latency KB/sec 178 | ---------------------------------------------------------------------------------------------------------------------------- 179 | Sets 4570.42 --- --- 4.82451 4.70300 11.77500 18.17500 63110.46 180 | Gets 45703.65 4570.42 41133.24 3.06528 1.08700 32.76700 38.91100 1645.95 181 | Waits 0.00 --- --- --- --- --- --- --- 182 | Totals 50274.07 4570.42 41133.24 3.22521 1.13500 32.38300 38.65500 64756.41 183 | -------------------------------------------------------------------------------- /memcrs/src/memcache/store/storage_tests.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use crate::mock::mock_server::{create_server, SetableTimer}; 3 | use crate::mock::value::{from_slice, from_string}; 4 | use bytes::{BufMut, BytesMut}; 5 | 6 | #[test] 7 | fn if_not_defined_cas_should_be_1() { 8 | let server = create_server(); 9 | 10 | let key = Bytes::from("key"); 11 | let record = Record::new(from_string("Test data"), 0, 0, 0); 12 | let result = server.storage.set(key.clone(), record.clone()); 13 | assert!(result.is_ok()); 14 | let found = server.storage.get(&key); 15 | assert!(found.is_ok()); 16 | match found { 17 | Ok(r) => { 18 | assert_eq!(r, record); 19 | assert_eq!(r.header.cas, 1) 20 | } 21 | Err(_er) => unreachable!(), 22 | } 23 | } 24 | 25 | #[test] 26 | fn if_cas_defined_it_should_be_returned() { 27 | let storage = create_server().storage; 28 | let cas: u64 = 0xDEAD_BEEF; 29 | let key = Bytes::from("key"); 30 | let record = Record::new(from_string("test data"), cas, 0, 0); 31 | info!("Record {:?}", &record.header); 32 | let result = storage.set(key.clone(), record.clone()); 33 | assert!(result.is_ok()); 34 | let found = storage.get(&key); 35 | assert!(found.is_ok()); 36 | match found { 37 | Ok(r) => { 38 | assert_eq!(r, record); 39 | assert_eq!(r.header.cas, cas + 1) 40 | } 41 | Err(_er) => unreachable!(), 42 | } 43 | } 44 | 45 | #[test] 46 | fn insert_should_fail_on_cas_mismatch() { 47 | let storage = create_server().storage; 48 | let cas: u64 = 0xDEAD_BEEF; 49 | let key = Bytes::from("key"); 50 | let mut record = Record::new(from_string("test data"), cas, 0, 0); 51 | let result = storage.set(key.clone(), record.clone()); 52 | assert!(result.is_ok()); 53 | record.header.cas = 1; 54 | let result = storage.set(key, record); 55 | match result { 56 | Ok(_) => unreachable!(), 57 | Err(err) => assert_eq!(err, CacheError::KeyExists), 58 | } 59 | } 60 | 61 | #[test] 62 | fn record_should_expire_in_given_time() { 63 | let server = create_server(); 64 | let cas: u64 = 0xDEAD_BEEF; 65 | let key = Bytes::from("key"); 66 | let record = Record::new(from_string("test data"), cas, 0, 123); 67 | let result = server.storage.set(key.clone(), record); 68 | assert!(result.is_ok()); 69 | println!("{:?}", result); 70 | let found = server.storage.get(&key); 71 | assert!(found.is_ok()); 72 | 73 | server.timer.set(123); 74 | let found = server.storage.get(&key); 75 | assert!(found.is_err()); 76 | match found { 77 | Ok(_r) => unreachable!(), 78 | Err(err) => assert_eq!(err, CacheError::NotFound), 79 | } 80 | } 81 | 82 | #[test] 83 | fn delete_record() { 84 | let server = create_server(); 85 | let key = Bytes::from("key"); 86 | let record = Record::new(from_string("test data"), 0, 0, 0); 87 | let result = server.storage.set(key.clone(), record); 88 | assert!(result.is_ok()); 89 | let found = server.storage.get(&key); 90 | assert!(found.is_ok()); 91 | let header = Meta::new(0, 0, 0); 92 | let deleted = server.storage.delete(key.clone(), header); 93 | match deleted { 94 | Ok(_) => match server.storage.get(&key) { 95 | Ok(_) => unreachable!(), 96 | Err(err) => assert_eq!(err, CacheError::NotFound), 97 | }, 98 | Err(_err) => unreachable!(), 99 | } 100 | } 101 | 102 | #[test] 103 | fn delete_should_return_not_exists() { 104 | let server = create_server(); 105 | let key = Bytes::from("key"); 106 | let record = Record::new(from_string("test data"), 0, 0, 0); 107 | let result = server.storage.set(key.clone(), record); 108 | assert!(result.is_ok()); 109 | let found = server.storage.get(&key); 110 | assert!(found.is_ok()); 111 | let header = Meta::new(0, 0, 0); 112 | let deleted = server.storage.delete(Bytes::from("bad key"), header); 113 | match deleted { 114 | Ok(_) => unreachable!(), 115 | Err(err) => assert_eq!(err, CacheError::NotFound), 116 | } 117 | } 118 | 119 | #[test] 120 | fn delete_if_cas_doesnt_match_should_not_delete() { 121 | let server = create_server(); 122 | let key = Bytes::from("key"); 123 | let record = Record::new(from_string("test data"), 1, 0, 0); 124 | let result = server.storage.set(key.clone(), record); 125 | assert!(result.is_ok()); 126 | let found = server.storage.get(&key); 127 | assert!(found.is_ok()); 128 | let header = Meta::new(6, 0, 0); 129 | let deleted = server.storage.delete(Bytes::from("key"), header); 130 | match deleted { 131 | Ok(_) => unreachable!(), 132 | Err(err) => assert_eq!(err, CacheError::KeyExists), 133 | } 134 | } 135 | 136 | #[test] 137 | fn delete_if_cas_match_should_succeed() { 138 | let server = create_server(); 139 | let key = Bytes::from("key"); 140 | let record = Record::new(from_string("test data"), 5, 0, 0); 141 | let result = server.storage.set(key.clone(), record); 142 | assert!(result.is_ok()); 143 | let found = server.storage.get(&key); 144 | assert!(found.is_ok()); 145 | let header = Meta::new(found.unwrap().header.cas, 0, 0); 146 | let deleted = server.storage.delete(Bytes::from("key"), header); 147 | assert!(deleted.is_ok()); 148 | } 149 | 150 | #[test] 151 | fn flush_should_remove_all_elements_in_cache() { 152 | let server = create_server(); 153 | for key_suffix in 1..10 { 154 | let mut key_str = BytesMut::from("key"); 155 | key_str.reserve(8); 156 | key_str.put_slice(&key_suffix.to_string().as_bytes()); 157 | let key = key_str.freeze(); 158 | let record = Record::new(from_string("test data"), 0, 0, 5); 159 | let result = server.storage.set(key.clone(), record); 160 | assert!(result.is_ok()); 161 | } 162 | 163 | server.storage.flush(Meta::new(0, 0, 3)); 164 | server.timer.set(10); 165 | 166 | for key_suffix in 1..10 { 167 | let mut key_str = BytesMut::from("key"); 168 | key_str.reserve(8); 169 | key_str.put_slice(&key_suffix.to_string().as_bytes()); 170 | let result = server.storage.get(&key_str.freeze()); 171 | match result { 172 | Ok(_) => unreachable!(), 173 | Err(err) => assert_eq!(err, CacheError::NotFound), 174 | } 175 | } 176 | } 177 | 178 | #[test] 179 | fn add_should_succeed_if_not_already_stored() { 180 | let server = create_server(); 181 | let key = Bytes::from("key"); 182 | let record = Record::new(from_string("test data"), 5, 0, 0); 183 | let result = server.storage.add(key, record); 184 | assert!(result.is_ok()); 185 | } 186 | 187 | #[test] 188 | fn add_should_fail_if_already_stored() { 189 | let server = create_server(); 190 | let key = Bytes::from("key"); 191 | let record = Record::new(from_string("test data"), 5, 0, 0); 192 | let result = server.storage.set(key.clone(), record.clone()); 193 | assert!(result.is_ok()); 194 | let add_result = server.storage.add(key, record); 195 | match add_result { 196 | Ok(_) => unreachable!(), 197 | Err(err) => assert_eq!(err, CacheError::KeyExists), 198 | } 199 | } 200 | 201 | #[test] 202 | fn replace_should_fail_if_not_stored() { 203 | let server = create_server(); 204 | let key = Bytes::from("key"); 205 | let record = Record::new(from_string("test data"), 5, 0, 0); 206 | let result = server.storage.replace(key, record); 207 | match result { 208 | Ok(_) => unreachable!(), 209 | Err(err) => assert_eq!(err, CacheError::NotFound), 210 | } 211 | } 212 | 213 | #[test] 214 | fn replace_should_succeed_if_stored() { 215 | let server = create_server(); 216 | let key = Bytes::from("key"); 217 | let record = Record::new(from_string("test data"), 0, 0, 0); 218 | let result = server.storage.set(key.clone(), record); 219 | assert!(result.is_ok()); 220 | match result { 221 | Ok(status) => { 222 | let new_record = Record::new(from_string("New record"), status.cas, 0, 0); 223 | let replace_result = server.storage.replace(key, new_record); 224 | assert!(replace_result.is_ok()); 225 | } 226 | Err(_) => unreachable!(), 227 | } 228 | } 229 | 230 | #[test] 231 | fn append_should_fail_if_not_exist() { 232 | let server = create_server(); 233 | let key = Bytes::from("key"); 234 | let record = Record::new(from_string("test data"), 0, 0, 0); 235 | let result = server.storage.append(key, record); 236 | 237 | match result { 238 | Ok(_) => unreachable!(), 239 | Err(err) => assert_eq!(err, CacheError::NotFound), 240 | } 241 | } 242 | 243 | #[test] 244 | fn prepend_should_fail_if_not_exist() { 245 | let server = create_server(); 246 | let key = Bytes::from("key"); 247 | let record = Record::new(from_string("test data"), 0, 0, 0); 248 | let result = server.storage.prepend(key, record); 249 | 250 | match result { 251 | Ok(_) => unreachable!(), 252 | Err(err) => assert_eq!(err, CacheError::NotFound), 253 | } 254 | } 255 | 256 | #[test] 257 | fn append_should_add_at_the_end() { 258 | let server = create_server(); 259 | let key = Bytes::from("key"); 260 | let record = Record::new(from_string("Foo"), 0, 0, 0); 261 | let result = server.storage.set(key.clone(), record); 262 | 263 | match result { 264 | Ok(status) => { 265 | let append_data = Record::new(from_string("bar"), status.cas, 0, 0); 266 | let append_result = server.storage.append(key.clone(), append_data); 267 | assert!(append_result.is_ok()); 268 | let get_result = server.storage.get(&key); 269 | match get_result { 270 | Ok(record) => { 271 | let value = from_string("Foobar"); 272 | assert_eq!(record.value[..], value[..]); 273 | } 274 | Err(_) => unreachable!(), 275 | } 276 | } 277 | Err(_) => unreachable!(), 278 | } 279 | } 280 | 281 | #[test] 282 | fn prepend_should_add_at_the_begining() { 283 | let server = create_server(); 284 | let key = Bytes::from("key"); 285 | let record = Record::new(from_string("Foo"), 0, 0, 0); 286 | let result = server.storage.set(key.clone(), record); 287 | 288 | match result { 289 | Ok(status) => { 290 | let append_data = Record::new(from_string("bar"), status.cas, 0, 0); 291 | let append_result = server.storage.prepend(key.clone(), append_data); 292 | assert!(append_result.is_ok()); 293 | let get_result = server.storage.get(&key); 294 | match get_result { 295 | Ok(record) => { 296 | let value = from_string("barFoo"); 297 | assert_eq!(record.value[..], value[..]); 298 | } 299 | Err(_) => unreachable!(), 300 | } 301 | } 302 | Err(_) => unreachable!(), 303 | } 304 | } 305 | 306 | #[test] 307 | fn increment_if_counter_doesnt_exists_it_should_created() { 308 | const COUNTER_INITIAL_VALUE: u64 = 5; 309 | let server = create_server(); 310 | let key = Bytes::from("counter1"); 311 | let counter = IncrementParam { 312 | delta: 0, 313 | value: COUNTER_INITIAL_VALUE, 314 | }; 315 | let header = Meta::new(0, 0, 0); 316 | let result = server.storage.increment(header, key, counter); 317 | match result { 318 | Ok(delta_result) => { 319 | assert_eq!(COUNTER_INITIAL_VALUE, delta_result.value); 320 | } 321 | Err(_) => { 322 | unreachable!(); 323 | } 324 | } 325 | } 326 | 327 | #[test] 328 | fn increment_if_expire_equals_ffffffff_counter_should_not_be_created() { 329 | let server = create_server(); 330 | let key = Bytes::from("counter1"); 331 | let counter = IncrementParam { delta: 0, value: 0 }; 332 | let header = Meta::new(0, 0, 0xffffffff); 333 | let result = server.storage.increment(header, key, counter); 334 | match result { 335 | Ok(_) => { 336 | unreachable!(); 337 | } 338 | Err(err) => { 339 | assert_eq!(err, CacheError::NotFound); 340 | } 341 | } 342 | } 343 | 344 | #[test] 345 | fn increment_value_should_be_incremented() { 346 | const DELTA: u64 = 6; 347 | const EXPECTED_RESULT: u64 = 5 + DELTA; 348 | let server = create_server(); 349 | let key = Bytes::from("counter1"); 350 | let record = Record::new(from_string("5"), 0, 0, 0); 351 | let result = server.storage.set(key.clone(), record); 352 | assert!(result.is_ok()); 353 | let cas = result.unwrap().cas; 354 | 355 | let counter = IncrementParam { 356 | delta: DELTA, 357 | value: 0, 358 | }; 359 | let header = Meta::new(0, 0, 0); 360 | let result = server.storage.increment(header, key, counter); 361 | match result { 362 | Ok(counter_value) => { 363 | assert_eq!(counter_value.value, EXPECTED_RESULT); 364 | assert_eq!(counter_value.cas, cas + 1); 365 | } 366 | Err(_) => { 367 | unreachable!(); 368 | } 369 | } 370 | } 371 | 372 | #[test] 373 | fn increment_if_value_is_not_number_it_should_be_error() { 374 | const DELTA: u64 = 5; 375 | let server = create_server(); 376 | let key = Bytes::from("counter1"); 377 | let record = Record::new(from_string("asdas5"), 0, 0, 0); 378 | let result = server.storage.set(key.clone(), record); 379 | assert!(result.is_ok()); 380 | 381 | let counter = IncrementParam { 382 | delta: DELTA, 383 | value: 0, 384 | }; 385 | let header = Meta::new(0, 0, 0); 386 | let result = server.storage.increment(header, key, counter); 387 | match result { 388 | Ok(_) => { 389 | unreachable!(); 390 | } 391 | Err(err) => { 392 | assert_eq!(err, CacheError::ArithOnNonNumeric); 393 | } 394 | } 395 | } 396 | 397 | #[test] 398 | fn increment_if_value_cannot_be_parsed_it_should_be_error() { 399 | const DELTA: u64 = 5; 400 | let server = create_server(); 401 | let key = Bytes::from("counter1"); 402 | let record = Record::new(from_slice(&[0xc3, 0x28]), 0, 0, 0); 403 | let result = server.storage.set(key.clone(), record); 404 | assert!(result.is_ok()); 405 | 406 | let counter = IncrementParam { 407 | delta: DELTA, 408 | value: 0, 409 | }; 410 | let header = Meta::new(0, 0, 0); 411 | let result = server.storage.increment(header, key, counter); 412 | match result { 413 | Ok(_) => { 414 | unreachable!(); 415 | } 416 | Err(err) => { 417 | assert_eq!(err, CacheError::ArithOnNonNumeric); 418 | } 419 | } 420 | } 421 | 422 | #[test] 423 | fn decrement_should_not_result_in_negative_value() { 424 | const DELTA: u64 = 1; 425 | let server = create_server(); 426 | let key = Bytes::from("counter1"); 427 | let record = Record::new(from_string("0"), 0, 0, 0); 428 | let result = server.storage.set(key.clone(), record); 429 | assert!(result.is_ok()); 430 | let cas = result.unwrap().cas; 431 | 432 | let counter = IncrementParam { 433 | delta: DELTA, 434 | value: 0, 435 | }; 436 | let header = Meta::new(0, 0, 0); 437 | let result = server.storage.decrement(header, key, counter); 438 | match result { 439 | Ok(counter_value) => { 440 | assert_eq!(counter_value.value, 0); 441 | assert_eq!(counter_value.cas, cas + 1); 442 | } 443 | Err(_) => { 444 | unreachable!(); 445 | } 446 | } 447 | } 448 | 449 | #[test] 450 | fn decrement_value_should_be_decremented() { 451 | const DELTA: u64 = 1; 452 | const EXPECTED_RESULT: u64 = 4; 453 | let server = create_server(); 454 | let key = Bytes::from("counter1"); 455 | let record = Record::new(from_string("5"), 0, 0, 0); 456 | let result = server.storage.set(key.clone(), record); 457 | assert!(result.is_ok()); 458 | let cas = result.unwrap().cas; 459 | 460 | let counter = IncrementParam { 461 | delta: DELTA, 462 | value: 0, 463 | }; 464 | let header = Meta::new(0, 0, 0); 465 | let result = server.storage.decrement(header, key, counter); 466 | match result { 467 | Ok(counter_value) => { 468 | assert_eq!(counter_value.value, EXPECTED_RESULT); 469 | assert_eq!(counter_value.cas, cas + 1); 470 | } 471 | Err(_) => { 472 | unreachable!(); 473 | } 474 | } 475 | } 476 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Memcrs 2 | 3 | :balloon: Thanks for your help improving the project! We are so happy to have 4 | you! 5 | 6 | There are opportunities to contribute to Memcrs at any level. It doesn't matter if 7 | you are just getting started with Rust or are the most weathered expert, we can 8 | use your help. 9 | 10 | **No contribution is too small and all contributions are valued.** 11 | 12 | This guide will help you get started. **Do not let this guide intimidate you**. 13 | It should be considered a map to help you navigate the process. 14 | 15 | ## Conduct 16 | 17 | The Memcrs project adheres to the [Rust Code of Conduct][coc]. This describes 18 | the _minimum_ behavior expected from all contributors. 19 | 20 | [coc]: https://github.com/rust-lang/rust/blob/master/CODE_OF_CONDUCT.md 21 | 22 | ## Contributing in Issues 23 | 24 | For any issue, there are fundamentally three ways an individual can contribute: 25 | 26 | 1. By opening the issue for discussion: For instance, if you believe that you 27 | have discovered a bug in Memcrs, creating a new issue in [the Memcrs 28 | issue tracker][issue] is the way to report it. 29 | 30 | 2. By helping to triage the issue: This can be done by providing 31 | supporting details (a test case that demonstrates a bug), providing 32 | suggestions on how to address the issue, or ensuring that the issue is tagged 33 | correctly. 34 | 35 | 3. By helping to resolve the issue: Typically this is done either in the form of 36 | demonstrating that the issue reported is not a problem after all, or more 37 | often, by opening a Pull Request that changes some bit of something in 38 | Memcrs in a concrete and reviewable manner. 39 | 40 | [issue]: https://github.com/Memcrs/issues 41 | 42 | **Anybody can participate in any stage of contribution**. We urge you to 43 | participate in the discussion around bugs and participate in reviewing PRs. 44 | 45 | ### Asking for General Help 46 | 47 | If you have reviewed existing documentation and still have questions or are 48 | having problems, you can [open a discussion] asking for help. 49 | 50 | In exchange for receiving help, we ask that you contribute back a documentation 51 | PR that helps others avoid the problems that you encountered. 52 | 53 | [open a discussion]: https://github.com/Memcrs/discussions/new 54 | 55 | ### Submitting a Bug Report 56 | 57 | When opening a new issue in the Memcrs issue tracker, you will be presented 58 | with a basic template that should be filled in. If you believe that you have 59 | uncovered a bug, please fill out this form, following the template to the best 60 | of your ability. Do not worry if you cannot answer every detail, just fill in 61 | what you can. 62 | 63 | The two most important pieces of information we need in order to properly 64 | evaluate the report is a description of the behavior you are seeing and a simple 65 | test case we can use to recreate the problem on our own. If we cannot recreate 66 | the issue, it becomes impossible for us to fix. 67 | 68 | 69 | See [How to create a Minimal, Complete, and Verifiable example][mcve]. 70 | 71 | [mcve]: https://stackoverflow.com/help/mcve 72 | 73 | ### Triaging a Bug Report 74 | 75 | Once an issue has been opened, it is not uncommon for there to be discussion 76 | around it. Some contributors may have differing opinions about the issue, 77 | including whether the behavior being seen is a bug or a feature. This discussion 78 | is part of the process and should be kept focused, helpful, and professional. 79 | 80 | Short, clipped responses—that provide neither additional context nor supporting 81 | detail—are not helpful or professional. To many, such responses are simply 82 | annoying and unfriendly. 83 | 84 | Contributors are encouraged to help one another make forward progress as much as 85 | possible, empowering one another to solve issues collaboratively. If you choose 86 | to comment on an issue that you feel either is not a problem that needs to be 87 | fixed, or if you encounter information in an issue that you feel is incorrect, 88 | explain why you feel that way with additional supporting context, and be willing 89 | to be convinced that you may be wrong. By doing so, we can often reach the 90 | correct outcome much faster. 91 | 92 | ### Resolving a Bug Report 93 | 94 | In the majority of cases, issues are resolved by opening a Pull Request. The 95 | process for opening and reviewing a Pull Request is similar to that of opening 96 | and triaging issues, but carries with it a necessary review and approval 97 | workflow that ensures that the proposed changes meet the minimal quality and 98 | functional guidelines of the Memcrs project. 99 | 100 | ## Pull Requests 101 | 102 | Pull Requests are the way concrete changes are made to the code, documentation, 103 | and dependencies in the Memcrs repository. 104 | 105 | Even tiny pull requests (e.g., one character pull request fixing a typo in API 106 | documentation) are greatly appreciated. Before making a large change, it is 107 | usually a good idea to first open an issue describing the change to solicit 108 | feedback and guidance. This will increase the likelihood of the PR getting 109 | merged. 110 | 111 | ### Tests 112 | 113 | If the change being proposed alters code (as opposed to only documentation for 114 | example), it is either adding new functionality to Memcrs or it is fixing 115 | existing, broken functionality. In both of these cases, the pull request should 116 | include one or more tests to ensure that Memcrs does not regress in the future. 117 | There are three ways to write tests: integration tests, documentation tests and 118 | unit tests. 119 | 120 | #### Integration tests 121 | 122 | Integration tests go to the memclt crate. 123 | 124 | The best strategy for writing a new integration test is to look at existing 125 | integration tests in the crate and follow the style. 126 | 127 | #### Documentation tests 128 | 129 | Ideally, every API has at least one [documentation test] that demonstrates how to 130 | use the API. Documentation tests are run with `cargo test --doc`. This ensures 131 | that the example is correct and provides additional test coverage. 132 | 133 | The trick to documentation tests is striking a balance between being succinct 134 | for a reader to understand and actually testing the API. 135 | 136 | The type level example for `tokio_timer::Timeout` provides a good example of a 137 | documentation test: 138 | 139 | ``` 140 | /// // import the `timeout` function, usually this is done 141 | /// // with `use tokio::prelude::*` 142 | /// use tokio::prelude::FutureExt; 143 | /// use futures::Stream; 144 | /// use futures::sync::mpsc; 145 | /// use std::time::Duration; 146 | /// 147 | /// # fn main() { 148 | /// let (tx, rx) = mpsc::unbounded(); 149 | /// # tx.unbounded_send(()).unwrap(); 150 | /// # drop(tx); 151 | /// 152 | /// let process = rx.for_each(|item| { 153 | /// // do something with `item` 154 | /// # drop(item); 155 | /// # Ok(()) 156 | /// }); 157 | /// 158 | /// # tokio::runtime::current_thread::block_on_all( 159 | /// // Wrap the future with a `Timeout` set to expire in 10 milliseconds. 160 | /// process.timeout(Duration::from_millis(10)) 161 | /// # ).unwrap(); 162 | /// # } 163 | ``` 164 | 165 | Given that this is a *type* level documentation test and the primary way users 166 | of `tokio` will create an instance of `Timeout` is by using 167 | `FutureExt::timeout`, this is how the documentation test is structured. 168 | 169 | Lines that start with `/// #` are removed when the documentation is generated. 170 | They are only there to get the test to run. The `block_on_all` function is the 171 | easiest way to execute a future from a test. 172 | 173 | If this were a documentation test for the `Timeout::new` function, then the 174 | example would explicitly use `Timeout::new`. For example: 175 | 176 | ``` 177 | /// use tokio::timer::Timeout; 178 | /// use futures::Future; 179 | /// use futures::sync::oneshot; 180 | /// use std::time::Duration; 181 | /// 182 | /// # fn main() { 183 | /// let (tx, rx) = oneshot::channel(); 184 | /// # tx.send(()).unwrap(); 185 | /// 186 | /// # tokio::runtime::current_thread::block_on_all( 187 | /// // Wrap the future with a `Timeout` set to expire in 10 milliseconds. 188 | /// Timeout::new(rx, Duration::from_millis(10)) 189 | /// # ).unwrap(); 190 | /// # } 191 | ``` 192 | 193 | ### Commits 194 | 195 | It is a recommended best practice to keep your changes as logically grouped as 196 | possible within individual commits. There is no limit to the number of commits 197 | any single Pull Request may have, and many contributors find it easier to review 198 | changes that are split across multiple commits. 199 | 200 | That said, if you have a number of commits that are "checkpoints" and don't 201 | represent a single logical change, please squash those together. 202 | 203 | Note that multiple commits often get squashed when they are landed (see the 204 | notes about [commit squashing](#commit-squashing)). 205 | 206 | #### Commit message guidelines 207 | 208 | A good commit message should describe what changed and why. 209 | 210 | 1. The first line should: 211 | 212 | * contain a short description of the change (preferably 50 characters or less, 213 | and no more than 72 characters) 214 | * be entirely in lowercase with the exception of proper nouns, acronyms, and 215 | the words that refer to code, like function/variable names 216 | 217 | 2. Keep the second line blank. 218 | 3. Wrap all other lines at 72 columns (except for long URLs). 219 | 4. If your patch fixes an open issue, you can add a reference to it at the end 220 | of the log. Use the `Fixes: #` prefix and the issue number. For other 221 | references use `Refs: #`. `Refs` may include multiple issues, separated by a 222 | comma. 223 | 224 | Examples: 225 | 226 | - `Fixes: #1337` 227 | - `Refs: #1234` 228 | 229 | Sample complete commit message: 230 | 231 | ```txt 232 | explain the commit in one line 233 | 234 | Body of commit message is a few lines of text, explaining things 235 | in more detail, possibly giving some background about the issue 236 | being fixed, etc. 237 | 238 | The body of the commit message can be several paragraphs, and 239 | please do proper word-wrap and keep columns shorter than about 240 | 72 characters or so. That way, `git log` will show things 241 | nicely even when it is indented. 242 | 243 | Fixes: #1337 244 | Refs: #453, #154 245 | ``` 246 | 247 | ### Opening the Pull Request 248 | 249 | From within GitHub, opening a new Pull Request will present you with a 250 | [template] that should be filled out. Please try to do your best at filling out 251 | the details, but feel free to skip parts if you're not sure what to put. 252 | 253 | [template]: .github/PULL_REQUEST_TEMPLATE.md 254 | 255 | ### Discuss and update 256 | 257 | You will probably get feedback or requests for changes to your Pull Request. 258 | This is a big part of the submission process so don't be discouraged! Some 259 | contributors may sign off on the Pull Request right away, others may have 260 | more detailed comments or feedback. This is a necessary part of the process 261 | in order to evaluate whether the changes are correct and necessary. 262 | 263 | **Any community member can review a PR and you might get conflicting feedback**. 264 | Keep an eye out for comments from code owners to provide guidance on conflicting 265 | feedback. 266 | 267 | **Once the PR is open, do not rebase the commits**. See [Commit Squashing](#commit-squashing) for 268 | more details. 269 | 270 | ### Commit Squashing 271 | 272 | In most cases, **do not squash commits that you add to your Pull Request during 273 | the review process**. When the commits in your Pull Request land, they may be 274 | squashed into one commit per logical change. Metadata will be added to the 275 | commit message (including links to the Pull Request, links to relevant issues, 276 | and the names of the reviewers). The commit history of your Pull Request, 277 | however, will stay intact on the Pull Request page. 278 | 279 | ## Reviewing Pull Requests 280 | 281 | **Any Memcrs community member is welcome to review any pull request**. 282 | 283 | All Memcrs contributors who choose to review and provide feedback on Pull 284 | Requests have a responsibility to both the project and the individual making the 285 | contribution. Reviews and feedback must be helpful, insightful, and geared 286 | towards improving the contribution as opposed to simply blocking it. If there 287 | are reasons why you feel the PR should not land, explain what those are. Do not 288 | expect to be able to block a Pull Request from advancing simply because you say 289 | "No" without giving an explanation. Be open to having your mind changed. Be open 290 | to working with the contributor to make the Pull Request better. 291 | 292 | Reviews that are dismissive or disrespectful of the contributor or any other 293 | reviewers are strictly counter to the Code of Conduct. 294 | 295 | When reviewing a Pull Request, the primary goals are for the codebase to improve 296 | and for the person submitting the request to succeed. **Even if a Pull Request 297 | does not land, the submitters should come away from the experience feeling like 298 | their effort was not wasted or unappreciated**. Every Pull Request from a new 299 | contributor is an opportunity to grow the community. 300 | 301 | ### Review a bit at a time 302 | 303 | Do not overwhelm new contributors. 304 | 305 | It is tempting to micro-optimize and make everything about relative performance, 306 | perfect grammar, or exact style matches. Do not succumb to that temptation. 307 | 308 | Focus first on the most significant aspects of the change: 309 | 310 | 1. Does this change make sense for Memcrs? 311 | 2. Does this change make Memcrs better, even if only incrementally? 312 | 3. Are there clear bugs or larger scale issues that need attending to? 313 | 4. Is the commit message readable and correct? If it contains a breaking change 314 | is it clear enough? 315 | 316 | Note that only **incremental** improvement is needed to land a PR. This means 317 | that the PR does not need to be perfect, only better than the status quo. Follow 318 | up PRs may be opened to continue iterating. 319 | 320 | When changes are necessary, *request* them, do not *demand* them, and **do not 321 | assume that the submitter already knows how to add a test or run a benchmark**. 322 | 323 | Specific performance optimization techniques, coding styles and conventions 324 | change over time. The first impression you give to a new contributor never does. 325 | 326 | Nits (requests for small changes that are not essential) are fine, but try to 327 | avoid stalling the Pull Request. Most nits can typically be fixed by the Memcrs 328 | Collaborator landing the Pull Request but they can also be an opportunity for 329 | the contributor to learn a bit more about the project. 330 | 331 | It is always good to clearly indicate nits when you comment: e.g. 332 | `Nit: change foo() to bar(). But this is not blocking.` 333 | 334 | If your comments were addressed but were not folded automatically after new 335 | commits or if they proved to be mistaken, please, [hide them][hiding-a-comment] 336 | with the appropriate reason to keep the conversation flow concise and relevant. 337 | 338 | ### Be aware of the person behind the code 339 | 340 | Be aware that *how* you communicate requests and reviews in your feedback can 341 | have a significant impact on the success of the Pull Request. Yes, we may land 342 | a particular change that makes Memcrs better, but the individual might just not 343 | want to have anything to do with Memcrs ever again. The goal is not just having 344 | good code. 345 | 346 | ### Abandoned or Stalled Pull Requests 347 | 348 | If a Pull Request appears to be abandoned or stalled, it is polite to first 349 | check with the contributor to see if they intend to continue the work before 350 | checking if they would mind if you took it over (especially if it just has nits 351 | left). When doing so, it is courteous to give the original contributor credit 352 | for the work they started (either by preserving their name and email address in 353 | the commit log, or by using an `Author: ` meta-data tag in the commit. 354 | 355 | _Adapted from the [Node.js contributing guide][node]_. 356 | 357 | [node]: https://github.com/nodejs/node/blob/master/CONTRIBUTING.md 358 | [hiding-a-comment]: https://help.github.com/articles/managing-disruptive-comments/#hiding-a-comment 359 | [documentation test]: https://doc.rust-lang.org/rustdoc/documentation-tests.html 360 | 361 | ## Keeping track of issues and PRs 362 | 363 | The Memcrs GitHub repository has a lot of issues and PRs to keep track of. This 364 | section explains the meaning of various labels, as well as our [GitHub 365 | project][project]. The section is primarily targeted at maintainers. Most 366 | contributors aren't able to set these labels. 367 | 368 | ### Category 369 | 370 | - **C-bug** This is a bug-report. Bug-fix PRs use `C-enhancement` instead. 371 | - **C-enhancement** This is a PR that adds a new features. 372 | - **C-maintenance** This is an issue or PR about stuff such as documentation, 373 | GitHub Actions or code quality. 374 | - **C-feature-request** This is a feature request. Implementations of feature 375 | requests use `C-enhancement` instead. 376 | - **C-feature-accepted** If you submit a PR for this feature request, we wont 377 | close it with the reason "we don't want this". Issues with this label should 378 | also have the `C-feature-request` label. 379 | - **C-musing** Stuff like tracking issues or roadmaps. "musings about a better 380 | world" 381 | - **C-proposal** A proposal of some kind, and a request for comments. 382 | - **C-question** A user question. Large overlap with GitHub discussions. 383 | - **C-request** A non-feature request, e.g. "please add deprecation notices to 384 | `-alpha.*` versions of crates" 385 | 386 | ### Calls for participation 387 | 388 | - **E-help-wanted** Stuff where we want help. Often seen together with `C-bug` 389 | or `C-feature-accepted`. 390 | - **E-easy** This is easy, ranging from quick documentation fixes to stuff you 391 | can do after reading the tutorial on our website. 392 | - **E-medium** This is not `E-easy` or `E-hard`. 393 | - **E-hard** This either involves very tricky code, is something we don't know 394 | how to solve, or is difficult for some other reason. 395 | - **E-needs-mvce** This bug is missing a minimal complete and verifiable 396 | example. 397 | 398 | The "E-" prefix is the same as used in the Rust compiler repository. Some 399 | issues are missing a difficulty rating, but feel free to ask on our Discord 400 | server if you want to know how difficult an issue likely is. 401 | 402 | ### Topic 403 | 404 | Some extra information. 405 | 406 | - **M-docs** This is about documentation. 407 | - **M-performance** This is about performance. 408 | 409 | Any label not listed here is not in active use. 410 | 411 | ## LTS guarantees 412 | 413 | memc.rs ≥ 1.0.0 comes with LTS guarantees: 414 | 415 | * A minimum of 5 years of maintenance. 416 | * A minimum of 3 years before a hypothetical 2.0 release. 417 | 418 | The goal of these guarantees is to provide stability to the ecosystem. 419 | 420 | ## Mininum Supported Rust Version (MSRV) 421 | 422 | * All Memcrs ≥ 1.0.0 releases will support at least a 6-month old Rust 423 | compiler release. 424 | * The MSRV will only be increased on 1.x releases. 425 | 426 | ## Versioning Policy 427 | 428 | With Memcrs ≥1.0.0: 429 | 430 | * Patch (1.\_.x) releases _should only_ contain bug fixes or documentation 431 | changes. Besides this, these releases should not substantially change 432 | runtime behavior. 433 | * Minor (1.x) releases may contain new functionality, MSRV increases (see 434 | above), minor dependency updates, deprecations, and larger internal 435 | implementation changes. 436 | 437 | This is as defined by [Semantic Versioning 2.0](https://semver.org/). 438 | -------------------------------------------------------------------------------- /memcrs/fuzz/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "ahash" 7 | version = "0.7.6" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" 10 | dependencies = [ 11 | "getrandom", 12 | "once_cell", 13 | "version_check", 14 | ] 15 | 16 | [[package]] 17 | name = "arbitrary" 18 | version = "1.2.0" 19 | source = "registry+https://github.com/rust-lang/crates.io-index" 20 | checksum = "29d47fbf90d5149a107494b15a7dc8d69b351be2db3bb9691740e88ec17fd880" 21 | 22 | [[package]] 23 | name = "atty" 24 | version = "0.2.14" 25 | source = "registry+https://github.com/rust-lang/crates.io-index" 26 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 27 | dependencies = [ 28 | "hermit-abi 0.1.19", 29 | "libc", 30 | "winapi", 31 | ] 32 | 33 | [[package]] 34 | name = "autocfg" 35 | version = "1.1.0" 36 | source = "registry+https://github.com/rust-lang/crates.io-index" 37 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 38 | 39 | [[package]] 40 | name = "bitflags" 41 | version = "1.3.2" 42 | source = "registry+https://github.com/rust-lang/crates.io-index" 43 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 44 | 45 | [[package]] 46 | name = "byte-unit" 47 | version = "4.0.18" 48 | source = "registry+https://github.com/rust-lang/crates.io-index" 49 | checksum = "3348673602e04848647fffaa8e9a861e7b5d5cae6570727b41bde0f722514484" 50 | dependencies = [ 51 | "serde", 52 | "utf8-width", 53 | ] 54 | 55 | [[package]] 56 | name = "bytes" 57 | version = "1.4.0" 58 | source = "registry+https://github.com/rust-lang/crates.io-index" 59 | checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" 60 | 61 | [[package]] 62 | name = "cc" 63 | version = "1.0.76" 64 | source = "registry+https://github.com/rust-lang/crates.io-index" 65 | checksum = "76a284da2e6fe2092f2353e51713435363112dfd60030e22add80be333fb928f" 66 | dependencies = [ 67 | "jobserver", 68 | ] 69 | 70 | [[package]] 71 | name = "cfg-if" 72 | version = "1.0.0" 73 | source = "registry+https://github.com/rust-lang/crates.io-index" 74 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 75 | 76 | [[package]] 77 | name = "clap" 78 | version = "3.2.23" 79 | source = "registry+https://github.com/rust-lang/crates.io-index" 80 | checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5" 81 | dependencies = [ 82 | "atty", 83 | "bitflags", 84 | "clap_lex", 85 | "indexmap", 86 | "once_cell", 87 | "strsim", 88 | "termcolor", 89 | "textwrap", 90 | ] 91 | 92 | [[package]] 93 | name = "clap_lex" 94 | version = "0.2.4" 95 | source = "registry+https://github.com/rust-lang/crates.io-index" 96 | checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" 97 | dependencies = [ 98 | "os_str_bytes", 99 | ] 100 | 101 | [[package]] 102 | name = "dashmap" 103 | version = "5.4.0" 104 | source = "registry+https://github.com/rust-lang/crates.io-index" 105 | checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc" 106 | dependencies = [ 107 | "cfg-if", 108 | "hashbrown", 109 | "lock_api", 110 | "once_cell", 111 | "parking_lot_core", 112 | ] 113 | 114 | [[package]] 115 | name = "futures" 116 | version = "0.3.26" 117 | source = "registry+https://github.com/rust-lang/crates.io-index" 118 | checksum = "13e2792b0ff0340399d58445b88fd9770e3489eff258a4cbc1523418f12abf84" 119 | dependencies = [ 120 | "futures-channel", 121 | "futures-core", 122 | "futures-executor", 123 | "futures-io", 124 | "futures-sink", 125 | "futures-task", 126 | "futures-util", 127 | ] 128 | 129 | [[package]] 130 | name = "futures-channel" 131 | version = "0.3.26" 132 | source = "registry+https://github.com/rust-lang/crates.io-index" 133 | checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5" 134 | dependencies = [ 135 | "futures-core", 136 | "futures-sink", 137 | ] 138 | 139 | [[package]] 140 | name = "futures-core" 141 | version = "0.3.26" 142 | source = "registry+https://github.com/rust-lang/crates.io-index" 143 | checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608" 144 | 145 | [[package]] 146 | name = "futures-executor" 147 | version = "0.3.26" 148 | source = "registry+https://github.com/rust-lang/crates.io-index" 149 | checksum = "e8de0a35a6ab97ec8869e32a2473f4b1324459e14c29275d14b10cb1fd19b50e" 150 | dependencies = [ 151 | "futures-core", 152 | "futures-task", 153 | "futures-util", 154 | ] 155 | 156 | [[package]] 157 | name = "futures-io" 158 | version = "0.3.26" 159 | source = "registry+https://github.com/rust-lang/crates.io-index" 160 | checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531" 161 | 162 | [[package]] 163 | name = "futures-macro" 164 | version = "0.3.26" 165 | source = "registry+https://github.com/rust-lang/crates.io-index" 166 | checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70" 167 | dependencies = [ 168 | "proc-macro2", 169 | "quote", 170 | "syn", 171 | ] 172 | 173 | [[package]] 174 | name = "futures-sink" 175 | version = "0.3.26" 176 | source = "registry+https://github.com/rust-lang/crates.io-index" 177 | checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364" 178 | 179 | [[package]] 180 | name = "futures-task" 181 | version = "0.3.26" 182 | source = "registry+https://github.com/rust-lang/crates.io-index" 183 | checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366" 184 | 185 | [[package]] 186 | name = "futures-util" 187 | version = "0.3.26" 188 | source = "registry+https://github.com/rust-lang/crates.io-index" 189 | checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1" 190 | dependencies = [ 191 | "futures-channel", 192 | "futures-core", 193 | "futures-io", 194 | "futures-macro", 195 | "futures-sink", 196 | "futures-task", 197 | "memchr", 198 | "pin-project-lite", 199 | "pin-utils", 200 | "slab", 201 | ] 202 | 203 | [[package]] 204 | name = "getrandom" 205 | version = "0.2.8" 206 | source = "registry+https://github.com/rust-lang/crates.io-index" 207 | checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" 208 | dependencies = [ 209 | "cfg-if", 210 | "libc", 211 | "wasi", 212 | ] 213 | 214 | [[package]] 215 | name = "hashbrown" 216 | version = "0.12.3" 217 | source = "registry+https://github.com/rust-lang/crates.io-index" 218 | checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" 219 | dependencies = [ 220 | "ahash", 221 | ] 222 | 223 | [[package]] 224 | name = "hermit-abi" 225 | version = "0.1.19" 226 | source = "registry+https://github.com/rust-lang/crates.io-index" 227 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 228 | dependencies = [ 229 | "libc", 230 | ] 231 | 232 | [[package]] 233 | name = "hermit-abi" 234 | version = "0.2.6" 235 | source = "registry+https://github.com/rust-lang/crates.io-index" 236 | checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" 237 | dependencies = [ 238 | "libc", 239 | ] 240 | 241 | [[package]] 242 | name = "indexmap" 243 | version = "1.9.1" 244 | source = "registry+https://github.com/rust-lang/crates.io-index" 245 | checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" 246 | dependencies = [ 247 | "autocfg", 248 | "hashbrown", 249 | ] 250 | 251 | [[package]] 252 | name = "jobserver" 253 | version = "0.1.25" 254 | source = "registry+https://github.com/rust-lang/crates.io-index" 255 | checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b" 256 | dependencies = [ 257 | "libc", 258 | ] 259 | 260 | [[package]] 261 | name = "lazy_static" 262 | version = "1.4.0" 263 | source = "registry+https://github.com/rust-lang/crates.io-index" 264 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 265 | 266 | [[package]] 267 | name = "libc" 268 | version = "0.2.137" 269 | source = "registry+https://github.com/rust-lang/crates.io-index" 270 | checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" 271 | 272 | [[package]] 273 | name = "libfuzzer-sys" 274 | version = "0.4.6" 275 | source = "registry+https://github.com/rust-lang/crates.io-index" 276 | checksum = "beb09950ae85a0a94b27676cccf37da5ff13f27076aa1adbc6545dd0d0e1bd4e" 277 | dependencies = [ 278 | "arbitrary", 279 | "cc", 280 | "once_cell", 281 | ] 282 | 283 | [[package]] 284 | name = "lock_api" 285 | version = "0.4.9" 286 | source = "registry+https://github.com/rust-lang/crates.io-index" 287 | checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" 288 | dependencies = [ 289 | "autocfg", 290 | "scopeguard", 291 | ] 292 | 293 | [[package]] 294 | name = "log" 295 | version = "0.4.17" 296 | source = "registry+https://github.com/rust-lang/crates.io-index" 297 | checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" 298 | dependencies = [ 299 | "cfg-if", 300 | ] 301 | 302 | [[package]] 303 | name = "memchr" 304 | version = "2.5.0" 305 | source = "registry+https://github.com/rust-lang/crates.io-index" 306 | checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" 307 | 308 | [[package]] 309 | name = "memcrs" 310 | version = "0.0.1" 311 | dependencies = [ 312 | "byte-unit", 313 | "bytes", 314 | "clap", 315 | "dashmap", 316 | "futures", 317 | "futures-util", 318 | "log", 319 | "num-derive", 320 | "num-traits", 321 | "num_cpus", 322 | "rand", 323 | "serde", 324 | "serde_derive", 325 | "socket2", 326 | "tokio", 327 | "tokio-util", 328 | "tracing", 329 | "tracing-attributes", 330 | "tracing-log", 331 | "tracing-subscriber", 332 | ] 333 | 334 | [[package]] 335 | name = "memcrs-fuzz" 336 | version = "0.0.1" 337 | dependencies = [ 338 | "bytes", 339 | "libfuzzer-sys", 340 | "memcrs", 341 | "tokio-util", 342 | ] 343 | 344 | [[package]] 345 | name = "mio" 346 | version = "0.8.5" 347 | source = "registry+https://github.com/rust-lang/crates.io-index" 348 | checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" 349 | dependencies = [ 350 | "libc", 351 | "log", 352 | "wasi", 353 | "windows-sys", 354 | ] 355 | 356 | [[package]] 357 | name = "num-derive" 358 | version = "0.3.3" 359 | source = "registry+https://github.com/rust-lang/crates.io-index" 360 | checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" 361 | dependencies = [ 362 | "proc-macro2", 363 | "quote", 364 | "syn", 365 | ] 366 | 367 | [[package]] 368 | name = "num-traits" 369 | version = "0.2.15" 370 | source = "registry+https://github.com/rust-lang/crates.io-index" 371 | checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" 372 | dependencies = [ 373 | "autocfg", 374 | ] 375 | 376 | [[package]] 377 | name = "num_cpus" 378 | version = "1.15.0" 379 | source = "registry+https://github.com/rust-lang/crates.io-index" 380 | checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" 381 | dependencies = [ 382 | "hermit-abi 0.2.6", 383 | "libc", 384 | ] 385 | 386 | [[package]] 387 | name = "once_cell" 388 | version = "1.16.0" 389 | source = "registry+https://github.com/rust-lang/crates.io-index" 390 | checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" 391 | 392 | [[package]] 393 | name = "os_str_bytes" 394 | version = "6.4.0" 395 | source = "registry+https://github.com/rust-lang/crates.io-index" 396 | checksum = "7b5bf27447411e9ee3ff51186bf7a08e16c341efdde93f4d823e8844429bed7e" 397 | 398 | [[package]] 399 | name = "parking_lot" 400 | version = "0.12.1" 401 | source = "registry+https://github.com/rust-lang/crates.io-index" 402 | checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" 403 | dependencies = [ 404 | "lock_api", 405 | "parking_lot_core", 406 | ] 407 | 408 | [[package]] 409 | name = "parking_lot_core" 410 | version = "0.9.4" 411 | source = "registry+https://github.com/rust-lang/crates.io-index" 412 | checksum = "4dc9e0dc2adc1c69d09143aff38d3d30c5c3f0df0dad82e6d25547af174ebec0" 413 | dependencies = [ 414 | "cfg-if", 415 | "libc", 416 | "redox_syscall", 417 | "smallvec", 418 | "windows-sys", 419 | ] 420 | 421 | [[package]] 422 | name = "pin-project-lite" 423 | version = "0.2.9" 424 | source = "registry+https://github.com/rust-lang/crates.io-index" 425 | checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" 426 | 427 | [[package]] 428 | name = "pin-utils" 429 | version = "0.1.0" 430 | source = "registry+https://github.com/rust-lang/crates.io-index" 431 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 432 | 433 | [[package]] 434 | name = "ppv-lite86" 435 | version = "0.2.17" 436 | source = "registry+https://github.com/rust-lang/crates.io-index" 437 | checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" 438 | 439 | [[package]] 440 | name = "proc-macro2" 441 | version = "1.0.47" 442 | source = "registry+https://github.com/rust-lang/crates.io-index" 443 | checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" 444 | dependencies = [ 445 | "unicode-ident", 446 | ] 447 | 448 | [[package]] 449 | name = "quote" 450 | version = "1.0.21" 451 | source = "registry+https://github.com/rust-lang/crates.io-index" 452 | checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" 453 | dependencies = [ 454 | "proc-macro2", 455 | ] 456 | 457 | [[package]] 458 | name = "rand" 459 | version = "0.8.5" 460 | source = "registry+https://github.com/rust-lang/crates.io-index" 461 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 462 | dependencies = [ 463 | "libc", 464 | "rand_chacha", 465 | "rand_core", 466 | ] 467 | 468 | [[package]] 469 | name = "rand_chacha" 470 | version = "0.3.1" 471 | source = "registry+https://github.com/rust-lang/crates.io-index" 472 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 473 | dependencies = [ 474 | "ppv-lite86", 475 | "rand_core", 476 | ] 477 | 478 | [[package]] 479 | name = "rand_core" 480 | version = "0.6.4" 481 | source = "registry+https://github.com/rust-lang/crates.io-index" 482 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 483 | dependencies = [ 484 | "getrandom", 485 | ] 486 | 487 | [[package]] 488 | name = "redox_syscall" 489 | version = "0.2.16" 490 | source = "registry+https://github.com/rust-lang/crates.io-index" 491 | checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" 492 | dependencies = [ 493 | "bitflags", 494 | ] 495 | 496 | [[package]] 497 | name = "scopeguard" 498 | version = "1.1.0" 499 | source = "registry+https://github.com/rust-lang/crates.io-index" 500 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 501 | 502 | [[package]] 503 | name = "serde" 504 | version = "1.0.152" 505 | source = "registry+https://github.com/rust-lang/crates.io-index" 506 | checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" 507 | 508 | [[package]] 509 | name = "serde_derive" 510 | version = "1.0.152" 511 | source = "registry+https://github.com/rust-lang/crates.io-index" 512 | checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" 513 | dependencies = [ 514 | "proc-macro2", 515 | "quote", 516 | "syn", 517 | ] 518 | 519 | [[package]] 520 | name = "sharded-slab" 521 | version = "0.1.4" 522 | source = "registry+https://github.com/rust-lang/crates.io-index" 523 | checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" 524 | dependencies = [ 525 | "lazy_static", 526 | ] 527 | 528 | [[package]] 529 | name = "signal-hook-registry" 530 | version = "1.4.0" 531 | source = "registry+https://github.com/rust-lang/crates.io-index" 532 | checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" 533 | dependencies = [ 534 | "libc", 535 | ] 536 | 537 | [[package]] 538 | name = "slab" 539 | version = "0.4.7" 540 | source = "registry+https://github.com/rust-lang/crates.io-index" 541 | checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" 542 | dependencies = [ 543 | "autocfg", 544 | ] 545 | 546 | [[package]] 547 | name = "smallvec" 548 | version = "1.10.0" 549 | source = "registry+https://github.com/rust-lang/crates.io-index" 550 | checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" 551 | 552 | [[package]] 553 | name = "socket2" 554 | version = "0.4.7" 555 | source = "registry+https://github.com/rust-lang/crates.io-index" 556 | checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" 557 | dependencies = [ 558 | "libc", 559 | "winapi", 560 | ] 561 | 562 | [[package]] 563 | name = "strsim" 564 | version = "0.10.0" 565 | source = "registry+https://github.com/rust-lang/crates.io-index" 566 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" 567 | 568 | [[package]] 569 | name = "syn" 570 | version = "1.0.107" 571 | source = "registry+https://github.com/rust-lang/crates.io-index" 572 | checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" 573 | dependencies = [ 574 | "proc-macro2", 575 | "quote", 576 | "unicode-ident", 577 | ] 578 | 579 | [[package]] 580 | name = "termcolor" 581 | version = "1.1.3" 582 | source = "registry+https://github.com/rust-lang/crates.io-index" 583 | checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" 584 | dependencies = [ 585 | "winapi-util", 586 | ] 587 | 588 | [[package]] 589 | name = "textwrap" 590 | version = "0.16.0" 591 | source = "registry+https://github.com/rust-lang/crates.io-index" 592 | checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" 593 | 594 | [[package]] 595 | name = "thread_local" 596 | version = "1.1.4" 597 | source = "registry+https://github.com/rust-lang/crates.io-index" 598 | checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" 599 | dependencies = [ 600 | "once_cell", 601 | ] 602 | 603 | [[package]] 604 | name = "tokio" 605 | version = "1.25.0" 606 | source = "registry+https://github.com/rust-lang/crates.io-index" 607 | checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af" 608 | dependencies = [ 609 | "autocfg", 610 | "bytes", 611 | "libc", 612 | "memchr", 613 | "mio", 614 | "num_cpus", 615 | "parking_lot", 616 | "pin-project-lite", 617 | "signal-hook-registry", 618 | "socket2", 619 | "tokio-macros", 620 | "windows-sys", 621 | ] 622 | 623 | [[package]] 624 | name = "tokio-macros" 625 | version = "1.8.0" 626 | source = "registry+https://github.com/rust-lang/crates.io-index" 627 | checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" 628 | dependencies = [ 629 | "proc-macro2", 630 | "quote", 631 | "syn", 632 | ] 633 | 634 | [[package]] 635 | name = "tokio-util" 636 | version = "0.7.4" 637 | source = "registry+https://github.com/rust-lang/crates.io-index" 638 | checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" 639 | dependencies = [ 640 | "bytes", 641 | "futures-core", 642 | "futures-io", 643 | "futures-sink", 644 | "futures-util", 645 | "hashbrown", 646 | "pin-project-lite", 647 | "slab", 648 | "tokio", 649 | "tracing", 650 | ] 651 | 652 | [[package]] 653 | name = "tracing" 654 | version = "0.1.37" 655 | source = "registry+https://github.com/rust-lang/crates.io-index" 656 | checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" 657 | dependencies = [ 658 | "cfg-if", 659 | "pin-project-lite", 660 | "tracing-attributes", 661 | "tracing-core", 662 | ] 663 | 664 | [[package]] 665 | name = "tracing-attributes" 666 | version = "0.1.23" 667 | source = "registry+https://github.com/rust-lang/crates.io-index" 668 | checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" 669 | dependencies = [ 670 | "proc-macro2", 671 | "quote", 672 | "syn", 673 | ] 674 | 675 | [[package]] 676 | name = "tracing-core" 677 | version = "0.1.30" 678 | source = "registry+https://github.com/rust-lang/crates.io-index" 679 | checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" 680 | dependencies = [ 681 | "once_cell", 682 | "valuable", 683 | ] 684 | 685 | [[package]] 686 | name = "tracing-log" 687 | version = "0.1.3" 688 | source = "registry+https://github.com/rust-lang/crates.io-index" 689 | checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" 690 | dependencies = [ 691 | "lazy_static", 692 | "log", 693 | "tracing-core", 694 | ] 695 | 696 | [[package]] 697 | name = "tracing-subscriber" 698 | version = "0.3.16" 699 | source = "registry+https://github.com/rust-lang/crates.io-index" 700 | checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70" 701 | dependencies = [ 702 | "sharded-slab", 703 | "thread_local", 704 | "tracing-core", 705 | ] 706 | 707 | [[package]] 708 | name = "unicode-ident" 709 | version = "1.0.5" 710 | source = "registry+https://github.com/rust-lang/crates.io-index" 711 | checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" 712 | 713 | [[package]] 714 | name = "utf8-width" 715 | version = "0.1.6" 716 | source = "registry+https://github.com/rust-lang/crates.io-index" 717 | checksum = "5190c9442dcdaf0ddd50f37420417d219ae5261bbf5db120d0f9bab996c9cba1" 718 | 719 | [[package]] 720 | name = "valuable" 721 | version = "0.1.0" 722 | source = "registry+https://github.com/rust-lang/crates.io-index" 723 | checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" 724 | 725 | [[package]] 726 | name = "version_check" 727 | version = "0.9.4" 728 | source = "registry+https://github.com/rust-lang/crates.io-index" 729 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 730 | 731 | [[package]] 732 | name = "wasi" 733 | version = "0.11.0+wasi-snapshot-preview1" 734 | source = "registry+https://github.com/rust-lang/crates.io-index" 735 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 736 | 737 | [[package]] 738 | name = "winapi" 739 | version = "0.3.9" 740 | source = "registry+https://github.com/rust-lang/crates.io-index" 741 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 742 | dependencies = [ 743 | "winapi-i686-pc-windows-gnu", 744 | "winapi-x86_64-pc-windows-gnu", 745 | ] 746 | 747 | [[package]] 748 | name = "winapi-i686-pc-windows-gnu" 749 | version = "0.4.0" 750 | source = "registry+https://github.com/rust-lang/crates.io-index" 751 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 752 | 753 | [[package]] 754 | name = "winapi-util" 755 | version = "0.1.5" 756 | source = "registry+https://github.com/rust-lang/crates.io-index" 757 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 758 | dependencies = [ 759 | "winapi", 760 | ] 761 | 762 | [[package]] 763 | name = "winapi-x86_64-pc-windows-gnu" 764 | version = "0.4.0" 765 | source = "registry+https://github.com/rust-lang/crates.io-index" 766 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 767 | 768 | [[package]] 769 | name = "windows-sys" 770 | version = "0.42.0" 771 | source = "registry+https://github.com/rust-lang/crates.io-index" 772 | checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" 773 | dependencies = [ 774 | "windows_aarch64_gnullvm", 775 | "windows_aarch64_msvc", 776 | "windows_i686_gnu", 777 | "windows_i686_msvc", 778 | "windows_x86_64_gnu", 779 | "windows_x86_64_gnullvm", 780 | "windows_x86_64_msvc", 781 | ] 782 | 783 | [[package]] 784 | name = "windows_aarch64_gnullvm" 785 | version = "0.42.0" 786 | source = "registry+https://github.com/rust-lang/crates.io-index" 787 | checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" 788 | 789 | [[package]] 790 | name = "windows_aarch64_msvc" 791 | version = "0.42.0" 792 | source = "registry+https://github.com/rust-lang/crates.io-index" 793 | checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" 794 | 795 | [[package]] 796 | name = "windows_i686_gnu" 797 | version = "0.42.0" 798 | source = "registry+https://github.com/rust-lang/crates.io-index" 799 | checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" 800 | 801 | [[package]] 802 | name = "windows_i686_msvc" 803 | version = "0.42.0" 804 | source = "registry+https://github.com/rust-lang/crates.io-index" 805 | checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" 806 | 807 | [[package]] 808 | name = "windows_x86_64_gnu" 809 | version = "0.42.0" 810 | source = "registry+https://github.com/rust-lang/crates.io-index" 811 | checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" 812 | 813 | [[package]] 814 | name = "windows_x86_64_gnullvm" 815 | version = "0.42.0" 816 | source = "registry+https://github.com/rust-lang/crates.io-index" 817 | checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" 818 | 819 | [[package]] 820 | name = "windows_x86_64_msvc" 821 | version = "0.42.0" 822 | source = "registry+https://github.com/rust-lang/crates.io-index" 823 | checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" 824 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "ahash" 7 | version = "0.7.6" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" 10 | dependencies = [ 11 | "getrandom", 12 | "once_cell", 13 | "version_check", 14 | ] 15 | 16 | [[package]] 17 | name = "atty" 18 | version = "0.2.14" 19 | source = "registry+https://github.com/rust-lang/crates.io-index" 20 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 21 | dependencies = [ 22 | "hermit-abi 0.1.19", 23 | "libc", 24 | "winapi", 25 | ] 26 | 27 | [[package]] 28 | name = "autocfg" 29 | version = "1.1.0" 30 | source = "registry+https://github.com/rust-lang/crates.io-index" 31 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 32 | 33 | [[package]] 34 | name = "bitflags" 35 | version = "1.3.2" 36 | source = "registry+https://github.com/rust-lang/crates.io-index" 37 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 38 | 39 | [[package]] 40 | name = "byte-unit" 41 | version = "4.0.19" 42 | source = "registry+https://github.com/rust-lang/crates.io-index" 43 | checksum = "da78b32057b8fdfc352504708feeba7216dcd65a2c9ab02978cbd288d1279b6c" 44 | dependencies = [ 45 | "serde", 46 | "utf8-width", 47 | ] 48 | 49 | [[package]] 50 | name = "bytes" 51 | version = "1.4.0" 52 | source = "registry+https://github.com/rust-lang/crates.io-index" 53 | checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" 54 | 55 | [[package]] 56 | name = "cc" 57 | version = "1.0.79" 58 | source = "registry+https://github.com/rust-lang/crates.io-index" 59 | checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" 60 | 61 | [[package]] 62 | name = "cfg-if" 63 | version = "1.0.0" 64 | source = "registry+https://github.com/rust-lang/crates.io-index" 65 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 66 | 67 | [[package]] 68 | name = "clap" 69 | version = "3.2.23" 70 | source = "registry+https://github.com/rust-lang/crates.io-index" 71 | checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5" 72 | dependencies = [ 73 | "atty", 74 | "bitflags", 75 | "clap_lex", 76 | "indexmap", 77 | "once_cell", 78 | "strsim", 79 | "termcolor", 80 | "textwrap", 81 | ] 82 | 83 | [[package]] 84 | name = "clap_lex" 85 | version = "0.2.4" 86 | source = "registry+https://github.com/rust-lang/crates.io-index" 87 | checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" 88 | dependencies = [ 89 | "os_str_bytes", 90 | ] 91 | 92 | [[package]] 93 | name = "core_affinity" 94 | version = "0.8.0" 95 | source = "registry+https://github.com/rust-lang/crates.io-index" 96 | checksum = "4436406e93f52cce33bfba4be067a9f7229da44a634c385e4b22cdfaca5f84cc" 97 | dependencies = [ 98 | "libc", 99 | "num_cpus", 100 | "winapi", 101 | ] 102 | 103 | [[package]] 104 | name = "dashmap" 105 | version = "5.4.0" 106 | source = "registry+https://github.com/rust-lang/crates.io-index" 107 | checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc" 108 | dependencies = [ 109 | "cfg-if", 110 | "hashbrown", 111 | "lock_api", 112 | "once_cell", 113 | "parking_lot_core", 114 | ] 115 | 116 | [[package]] 117 | name = "futures" 118 | version = "0.3.27" 119 | source = "registry+https://github.com/rust-lang/crates.io-index" 120 | checksum = "531ac96c6ff5fd7c62263c5e3c67a603af4fcaee2e1a0ae5565ba3a11e69e549" 121 | dependencies = [ 122 | "futures-channel", 123 | "futures-core", 124 | "futures-executor", 125 | "futures-io", 126 | "futures-sink", 127 | "futures-task", 128 | "futures-util", 129 | ] 130 | 131 | [[package]] 132 | name = "futures-channel" 133 | version = "0.3.27" 134 | source = "registry+https://github.com/rust-lang/crates.io-index" 135 | checksum = "164713a5a0dcc3e7b4b1ed7d3b433cabc18025386f9339346e8daf15963cf7ac" 136 | dependencies = [ 137 | "futures-core", 138 | "futures-sink", 139 | ] 140 | 141 | [[package]] 142 | name = "futures-core" 143 | version = "0.3.27" 144 | source = "registry+https://github.com/rust-lang/crates.io-index" 145 | checksum = "86d7a0c1aa76363dac491de0ee99faf6941128376f1cf96f07db7603b7de69dd" 146 | 147 | [[package]] 148 | name = "futures-executor" 149 | version = "0.3.27" 150 | source = "registry+https://github.com/rust-lang/crates.io-index" 151 | checksum = "1997dd9df74cdac935c76252744c1ed5794fac083242ea4fe77ef3ed60ba0f83" 152 | dependencies = [ 153 | "futures-core", 154 | "futures-task", 155 | "futures-util", 156 | ] 157 | 158 | [[package]] 159 | name = "futures-io" 160 | version = "0.3.27" 161 | source = "registry+https://github.com/rust-lang/crates.io-index" 162 | checksum = "89d422fa3cbe3b40dca574ab087abb5bc98258ea57eea3fd6f1fa7162c778b91" 163 | 164 | [[package]] 165 | name = "futures-macro" 166 | version = "0.3.27" 167 | source = "registry+https://github.com/rust-lang/crates.io-index" 168 | checksum = "3eb14ed937631bd8b8b8977f2c198443447a8355b6e3ca599f38c975e5a963b6" 169 | dependencies = [ 170 | "proc-macro2", 171 | "quote", 172 | "syn 1.0.109", 173 | ] 174 | 175 | [[package]] 176 | name = "futures-sink" 177 | version = "0.3.27" 178 | source = "registry+https://github.com/rust-lang/crates.io-index" 179 | checksum = "ec93083a4aecafb2a80a885c9de1f0ccae9dbd32c2bb54b0c3a65690e0b8d2f2" 180 | 181 | [[package]] 182 | name = "futures-task" 183 | version = "0.3.27" 184 | source = "registry+https://github.com/rust-lang/crates.io-index" 185 | checksum = "fd65540d33b37b16542a0438c12e6aeead10d4ac5d05bd3f805b8f35ab592879" 186 | 187 | [[package]] 188 | name = "futures-util" 189 | version = "0.3.27" 190 | source = "registry+https://github.com/rust-lang/crates.io-index" 191 | checksum = "3ef6b17e481503ec85211fed8f39d1970f128935ca1f814cd32ac4a6842e84ab" 192 | dependencies = [ 193 | "futures-channel", 194 | "futures-core", 195 | "futures-io", 196 | "futures-macro", 197 | "futures-sink", 198 | "futures-task", 199 | "memchr", 200 | "pin-project-lite", 201 | "pin-utils", 202 | "slab", 203 | ] 204 | 205 | [[package]] 206 | name = "getrandom" 207 | version = "0.2.8" 208 | source = "registry+https://github.com/rust-lang/crates.io-index" 209 | checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" 210 | dependencies = [ 211 | "cfg-if", 212 | "libc", 213 | "wasi", 214 | ] 215 | 216 | [[package]] 217 | name = "hashbrown" 218 | version = "0.12.3" 219 | source = "registry+https://github.com/rust-lang/crates.io-index" 220 | checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" 221 | dependencies = [ 222 | "ahash", 223 | ] 224 | 225 | [[package]] 226 | name = "hermit-abi" 227 | version = "0.1.19" 228 | source = "registry+https://github.com/rust-lang/crates.io-index" 229 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 230 | dependencies = [ 231 | "libc", 232 | ] 233 | 234 | [[package]] 235 | name = "hermit-abi" 236 | version = "0.2.6" 237 | source = "registry+https://github.com/rust-lang/crates.io-index" 238 | checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" 239 | dependencies = [ 240 | "libc", 241 | ] 242 | 243 | [[package]] 244 | name = "indexmap" 245 | version = "1.9.2" 246 | source = "registry+https://github.com/rust-lang/crates.io-index" 247 | checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" 248 | dependencies = [ 249 | "autocfg", 250 | "hashbrown", 251 | ] 252 | 253 | [[package]] 254 | name = "jemalloc-sys" 255 | version = "0.5.3+5.3.0-patched" 256 | source = "registry+https://github.com/rust-lang/crates.io-index" 257 | checksum = "f9bd5d616ea7ed58b571b2e209a65759664d7fb021a0819d7a790afc67e47ca1" 258 | dependencies = [ 259 | "cc", 260 | "libc", 261 | ] 262 | 263 | [[package]] 264 | name = "jemallocator" 265 | version = "0.5.0" 266 | source = "registry+https://github.com/rust-lang/crates.io-index" 267 | checksum = "16c2514137880c52b0b4822b563fadd38257c1f380858addb74a400889696ea6" 268 | dependencies = [ 269 | "jemalloc-sys", 270 | "libc", 271 | ] 272 | 273 | [[package]] 274 | name = "lazy_static" 275 | version = "1.4.0" 276 | source = "registry+https://github.com/rust-lang/crates.io-index" 277 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 278 | 279 | [[package]] 280 | name = "libc" 281 | version = "0.2.139" 282 | source = "registry+https://github.com/rust-lang/crates.io-index" 283 | checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" 284 | 285 | [[package]] 286 | name = "lock_api" 287 | version = "0.4.9" 288 | source = "registry+https://github.com/rust-lang/crates.io-index" 289 | checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" 290 | dependencies = [ 291 | "autocfg", 292 | "scopeguard", 293 | ] 294 | 295 | [[package]] 296 | name = "log" 297 | version = "0.4.17" 298 | source = "registry+https://github.com/rust-lang/crates.io-index" 299 | checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" 300 | dependencies = [ 301 | "cfg-if", 302 | ] 303 | 304 | [[package]] 305 | name = "memchr" 306 | version = "2.5.0" 307 | source = "registry+https://github.com/rust-lang/crates.io-index" 308 | checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" 309 | 310 | [[package]] 311 | name = "memcrs" 312 | version = "0.0.1" 313 | dependencies = [ 314 | "byte-unit", 315 | "bytes", 316 | "clap", 317 | "core_affinity", 318 | "dashmap", 319 | "futures", 320 | "futures-util", 321 | "jemallocator", 322 | "log", 323 | "num-derive", 324 | "num-traits", 325 | "num_cpus", 326 | "rand", 327 | "serde", 328 | "serde_derive", 329 | "socket2", 330 | "tokio", 331 | "tokio-util", 332 | "tracing", 333 | "tracing-attributes", 334 | "tracing-log", 335 | "tracing-subscriber", 336 | ] 337 | 338 | [[package]] 339 | name = "mio" 340 | version = "0.8.6" 341 | source = "registry+https://github.com/rust-lang/crates.io-index" 342 | checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" 343 | dependencies = [ 344 | "libc", 345 | "log", 346 | "wasi", 347 | "windows-sys", 348 | ] 349 | 350 | [[package]] 351 | name = "num-derive" 352 | version = "0.3.3" 353 | source = "registry+https://github.com/rust-lang/crates.io-index" 354 | checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" 355 | dependencies = [ 356 | "proc-macro2", 357 | "quote", 358 | "syn 1.0.109", 359 | ] 360 | 361 | [[package]] 362 | name = "num-traits" 363 | version = "0.2.15" 364 | source = "registry+https://github.com/rust-lang/crates.io-index" 365 | checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" 366 | dependencies = [ 367 | "autocfg", 368 | ] 369 | 370 | [[package]] 371 | name = "num_cpus" 372 | version = "1.15.0" 373 | source = "registry+https://github.com/rust-lang/crates.io-index" 374 | checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" 375 | dependencies = [ 376 | "hermit-abi 0.2.6", 377 | "libc", 378 | ] 379 | 380 | [[package]] 381 | name = "once_cell" 382 | version = "1.17.1" 383 | source = "registry+https://github.com/rust-lang/crates.io-index" 384 | checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" 385 | 386 | [[package]] 387 | name = "os_str_bytes" 388 | version = "6.4.1" 389 | source = "registry+https://github.com/rust-lang/crates.io-index" 390 | checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" 391 | 392 | [[package]] 393 | name = "parking_lot" 394 | version = "0.12.1" 395 | source = "registry+https://github.com/rust-lang/crates.io-index" 396 | checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" 397 | dependencies = [ 398 | "lock_api", 399 | "parking_lot_core", 400 | ] 401 | 402 | [[package]] 403 | name = "parking_lot_core" 404 | version = "0.9.7" 405 | source = "registry+https://github.com/rust-lang/crates.io-index" 406 | checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" 407 | dependencies = [ 408 | "cfg-if", 409 | "libc", 410 | "redox_syscall", 411 | "smallvec", 412 | "windows-sys", 413 | ] 414 | 415 | [[package]] 416 | name = "pin-project-lite" 417 | version = "0.2.9" 418 | source = "registry+https://github.com/rust-lang/crates.io-index" 419 | checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" 420 | 421 | [[package]] 422 | name = "pin-utils" 423 | version = "0.1.0" 424 | source = "registry+https://github.com/rust-lang/crates.io-index" 425 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 426 | 427 | [[package]] 428 | name = "ppv-lite86" 429 | version = "0.2.17" 430 | source = "registry+https://github.com/rust-lang/crates.io-index" 431 | checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" 432 | 433 | [[package]] 434 | name = "proc-macro2" 435 | version = "1.0.52" 436 | source = "registry+https://github.com/rust-lang/crates.io-index" 437 | checksum = "1d0e1ae9e836cc3beddd63db0df682593d7e2d3d891ae8c9083d2113e1744224" 438 | dependencies = [ 439 | "unicode-ident", 440 | ] 441 | 442 | [[package]] 443 | name = "quote" 444 | version = "1.0.26" 445 | source = "registry+https://github.com/rust-lang/crates.io-index" 446 | checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" 447 | dependencies = [ 448 | "proc-macro2", 449 | ] 450 | 451 | [[package]] 452 | name = "rand" 453 | version = "0.8.5" 454 | source = "registry+https://github.com/rust-lang/crates.io-index" 455 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 456 | dependencies = [ 457 | "libc", 458 | "rand_chacha", 459 | "rand_core", 460 | ] 461 | 462 | [[package]] 463 | name = "rand_chacha" 464 | version = "0.3.1" 465 | source = "registry+https://github.com/rust-lang/crates.io-index" 466 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 467 | dependencies = [ 468 | "ppv-lite86", 469 | "rand_core", 470 | ] 471 | 472 | [[package]] 473 | name = "rand_core" 474 | version = "0.6.4" 475 | source = "registry+https://github.com/rust-lang/crates.io-index" 476 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 477 | dependencies = [ 478 | "getrandom", 479 | ] 480 | 481 | [[package]] 482 | name = "redox_syscall" 483 | version = "0.2.16" 484 | source = "registry+https://github.com/rust-lang/crates.io-index" 485 | checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" 486 | dependencies = [ 487 | "bitflags", 488 | ] 489 | 490 | [[package]] 491 | name = "scopeguard" 492 | version = "1.1.0" 493 | source = "registry+https://github.com/rust-lang/crates.io-index" 494 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 495 | 496 | [[package]] 497 | name = "serde" 498 | version = "1.0.157" 499 | source = "registry+https://github.com/rust-lang/crates.io-index" 500 | checksum = "707de5fcf5df2b5788fca98dd7eab490bc2fd9b7ef1404defc462833b83f25ca" 501 | 502 | [[package]] 503 | name = "serde_derive" 504 | version = "1.0.157" 505 | source = "registry+https://github.com/rust-lang/crates.io-index" 506 | checksum = "78997f4555c22a7971214540c4a661291970619afd56de19f77e0de86296e1e5" 507 | dependencies = [ 508 | "proc-macro2", 509 | "quote", 510 | "syn 2.0.2", 511 | ] 512 | 513 | [[package]] 514 | name = "sharded-slab" 515 | version = "0.1.4" 516 | source = "registry+https://github.com/rust-lang/crates.io-index" 517 | checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" 518 | dependencies = [ 519 | "lazy_static", 520 | ] 521 | 522 | [[package]] 523 | name = "signal-hook-registry" 524 | version = "1.4.1" 525 | source = "registry+https://github.com/rust-lang/crates.io-index" 526 | checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" 527 | dependencies = [ 528 | "libc", 529 | ] 530 | 531 | [[package]] 532 | name = "slab" 533 | version = "0.4.8" 534 | source = "registry+https://github.com/rust-lang/crates.io-index" 535 | checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" 536 | dependencies = [ 537 | "autocfg", 538 | ] 539 | 540 | [[package]] 541 | name = "smallvec" 542 | version = "1.10.0" 543 | source = "registry+https://github.com/rust-lang/crates.io-index" 544 | checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" 545 | 546 | [[package]] 547 | name = "socket2" 548 | version = "0.4.9" 549 | source = "registry+https://github.com/rust-lang/crates.io-index" 550 | checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" 551 | dependencies = [ 552 | "libc", 553 | "winapi", 554 | ] 555 | 556 | [[package]] 557 | name = "strsim" 558 | version = "0.10.0" 559 | source = "registry+https://github.com/rust-lang/crates.io-index" 560 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" 561 | 562 | [[package]] 563 | name = "syn" 564 | version = "1.0.109" 565 | source = "registry+https://github.com/rust-lang/crates.io-index" 566 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 567 | dependencies = [ 568 | "proc-macro2", 569 | "quote", 570 | "unicode-ident", 571 | ] 572 | 573 | [[package]] 574 | name = "syn" 575 | version = "2.0.2" 576 | source = "registry+https://github.com/rust-lang/crates.io-index" 577 | checksum = "59d3276aee1fa0c33612917969b5172b5be2db051232a6e4826f1a1a9191b045" 578 | dependencies = [ 579 | "proc-macro2", 580 | "quote", 581 | "unicode-ident", 582 | ] 583 | 584 | [[package]] 585 | name = "termcolor" 586 | version = "1.2.0" 587 | source = "registry+https://github.com/rust-lang/crates.io-index" 588 | checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" 589 | dependencies = [ 590 | "winapi-util", 591 | ] 592 | 593 | [[package]] 594 | name = "textwrap" 595 | version = "0.16.0" 596 | source = "registry+https://github.com/rust-lang/crates.io-index" 597 | checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" 598 | 599 | [[package]] 600 | name = "thread_local" 601 | version = "1.1.7" 602 | source = "registry+https://github.com/rust-lang/crates.io-index" 603 | checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" 604 | dependencies = [ 605 | "cfg-if", 606 | "once_cell", 607 | ] 608 | 609 | [[package]] 610 | name = "tokio" 611 | version = "1.26.0" 612 | source = "registry+https://github.com/rust-lang/crates.io-index" 613 | checksum = "03201d01c3c27a29c8a5cee5b55a93ddae1ccf6f08f65365c2c918f8c1b76f64" 614 | dependencies = [ 615 | "autocfg", 616 | "bytes", 617 | "libc", 618 | "memchr", 619 | "mio", 620 | "num_cpus", 621 | "parking_lot", 622 | "pin-project-lite", 623 | "signal-hook-registry", 624 | "socket2", 625 | "tokio-macros", 626 | "windows-sys", 627 | ] 628 | 629 | [[package]] 630 | name = "tokio-macros" 631 | version = "1.8.2" 632 | source = "registry+https://github.com/rust-lang/crates.io-index" 633 | checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" 634 | dependencies = [ 635 | "proc-macro2", 636 | "quote", 637 | "syn 1.0.109", 638 | ] 639 | 640 | [[package]] 641 | name = "tokio-util" 642 | version = "0.7.7" 643 | source = "registry+https://github.com/rust-lang/crates.io-index" 644 | checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" 645 | dependencies = [ 646 | "bytes", 647 | "futures-core", 648 | "futures-io", 649 | "futures-sink", 650 | "futures-util", 651 | "hashbrown", 652 | "pin-project-lite", 653 | "slab", 654 | "tokio", 655 | "tracing", 656 | ] 657 | 658 | [[package]] 659 | name = "tracing" 660 | version = "0.1.37" 661 | source = "registry+https://github.com/rust-lang/crates.io-index" 662 | checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" 663 | dependencies = [ 664 | "cfg-if", 665 | "pin-project-lite", 666 | "tracing-attributes", 667 | "tracing-core", 668 | ] 669 | 670 | [[package]] 671 | name = "tracing-attributes" 672 | version = "0.1.23" 673 | source = "registry+https://github.com/rust-lang/crates.io-index" 674 | checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" 675 | dependencies = [ 676 | "proc-macro2", 677 | "quote", 678 | "syn 1.0.109", 679 | ] 680 | 681 | [[package]] 682 | name = "tracing-core" 683 | version = "0.1.30" 684 | source = "registry+https://github.com/rust-lang/crates.io-index" 685 | checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" 686 | dependencies = [ 687 | "once_cell", 688 | "valuable", 689 | ] 690 | 691 | [[package]] 692 | name = "tracing-log" 693 | version = "0.1.3" 694 | source = "registry+https://github.com/rust-lang/crates.io-index" 695 | checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" 696 | dependencies = [ 697 | "lazy_static", 698 | "log", 699 | "tracing-core", 700 | ] 701 | 702 | [[package]] 703 | name = "tracing-subscriber" 704 | version = "0.3.16" 705 | source = "registry+https://github.com/rust-lang/crates.io-index" 706 | checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70" 707 | dependencies = [ 708 | "sharded-slab", 709 | "thread_local", 710 | "tracing-core", 711 | ] 712 | 713 | [[package]] 714 | name = "unicode-ident" 715 | version = "1.0.6" 716 | source = "registry+https://github.com/rust-lang/crates.io-index" 717 | checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" 718 | 719 | [[package]] 720 | name = "utf8-width" 721 | version = "0.1.6" 722 | source = "registry+https://github.com/rust-lang/crates.io-index" 723 | checksum = "5190c9442dcdaf0ddd50f37420417d219ae5261bbf5db120d0f9bab996c9cba1" 724 | 725 | [[package]] 726 | name = "valuable" 727 | version = "0.1.0" 728 | source = "registry+https://github.com/rust-lang/crates.io-index" 729 | checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" 730 | 731 | [[package]] 732 | name = "version_check" 733 | version = "0.9.4" 734 | source = "registry+https://github.com/rust-lang/crates.io-index" 735 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 736 | 737 | [[package]] 738 | name = "wasi" 739 | version = "0.11.0+wasi-snapshot-preview1" 740 | source = "registry+https://github.com/rust-lang/crates.io-index" 741 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 742 | 743 | [[package]] 744 | name = "winapi" 745 | version = "0.3.9" 746 | source = "registry+https://github.com/rust-lang/crates.io-index" 747 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 748 | dependencies = [ 749 | "winapi-i686-pc-windows-gnu", 750 | "winapi-x86_64-pc-windows-gnu", 751 | ] 752 | 753 | [[package]] 754 | name = "winapi-i686-pc-windows-gnu" 755 | version = "0.4.0" 756 | source = "registry+https://github.com/rust-lang/crates.io-index" 757 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 758 | 759 | [[package]] 760 | name = "winapi-util" 761 | version = "0.1.5" 762 | source = "registry+https://github.com/rust-lang/crates.io-index" 763 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 764 | dependencies = [ 765 | "winapi", 766 | ] 767 | 768 | [[package]] 769 | name = "winapi-x86_64-pc-windows-gnu" 770 | version = "0.4.0" 771 | source = "registry+https://github.com/rust-lang/crates.io-index" 772 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 773 | 774 | [[package]] 775 | name = "windows-sys" 776 | version = "0.45.0" 777 | source = "registry+https://github.com/rust-lang/crates.io-index" 778 | checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" 779 | dependencies = [ 780 | "windows-targets", 781 | ] 782 | 783 | [[package]] 784 | name = "windows-targets" 785 | version = "0.42.1" 786 | source = "registry+https://github.com/rust-lang/crates.io-index" 787 | checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" 788 | dependencies = [ 789 | "windows_aarch64_gnullvm", 790 | "windows_aarch64_msvc", 791 | "windows_i686_gnu", 792 | "windows_i686_msvc", 793 | "windows_x86_64_gnu", 794 | "windows_x86_64_gnullvm", 795 | "windows_x86_64_msvc", 796 | ] 797 | 798 | [[package]] 799 | name = "windows_aarch64_gnullvm" 800 | version = "0.42.1" 801 | source = "registry+https://github.com/rust-lang/crates.io-index" 802 | checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" 803 | 804 | [[package]] 805 | name = "windows_aarch64_msvc" 806 | version = "0.42.1" 807 | source = "registry+https://github.com/rust-lang/crates.io-index" 808 | checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" 809 | 810 | [[package]] 811 | name = "windows_i686_gnu" 812 | version = "0.42.1" 813 | source = "registry+https://github.com/rust-lang/crates.io-index" 814 | checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" 815 | 816 | [[package]] 817 | name = "windows_i686_msvc" 818 | version = "0.42.1" 819 | source = "registry+https://github.com/rust-lang/crates.io-index" 820 | checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" 821 | 822 | [[package]] 823 | name = "windows_x86_64_gnu" 824 | version = "0.42.1" 825 | source = "registry+https://github.com/rust-lang/crates.io-index" 826 | checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" 827 | 828 | [[package]] 829 | name = "windows_x86_64_gnullvm" 830 | version = "0.42.1" 831 | source = "registry+https://github.com/rust-lang/crates.io-index" 832 | checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" 833 | 834 | [[package]] 835 | name = "windows_x86_64_msvc" 836 | version = "0.42.1" 837 | source = "registry+https://github.com/rust-lang/crates.io-index" 838 | checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" 839 | --------------------------------------------------------------------------------