├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .vscode └── launch.json ├── Cargo.lock ├── Cargo.toml ├── Dockerfile ├── LICENSE ├── Makefile ├── NOTICE ├── README-ZH.md ├── README.md ├── config.toml ├── docs ├── 20220826144447.jpg ├── 20220826144516.jpg ├── 20220826144530.jpg ├── 20220826145733.jpg ├── 20220826150505.jpg ├── 20220902160513.jpg ├── 20220902160620.jpg ├── performance.md ├── tidis-arch.png └── tidis-introduction.md ├── rust-toolchain ├── src ├── bin │ └── server.rs ├── client.rs ├── cluster.rs ├── cmd │ ├── auth.rs │ ├── cluster.rs │ ├── cmdtype.rs │ ├── debug.rs │ ├── del.rs │ ├── eval.rs │ ├── exists.rs │ ├── expire.rs │ ├── fake.rs │ ├── get.rs │ ├── hdel.rs │ ├── hexists.rs │ ├── hget.rs │ ├── hgetall.rs │ ├── hincrby.rs │ ├── hkeys.rs │ ├── hlen.rs │ ├── hmget.rs │ ├── hset.rs │ ├── hstrlen.rs │ ├── hvals.rs │ ├── incrdecr.rs │ ├── lindex.rs │ ├── linsert.rs │ ├── llen.rs │ ├── lrange.rs │ ├── lrem.rs │ ├── lset.rs │ ├── ltrim.rs │ ├── mget.rs │ ├── mod.rs │ ├── mset.rs │ ├── multi.rs │ ├── persist.rs │ ├── ping.rs │ ├── pop.rs │ ├── publish.rs │ ├── push.rs │ ├── sadd.rs │ ├── scan.rs │ ├── scard.rs │ ├── script.rs │ ├── set.rs │ ├── setex.rs │ ├── setnx.rs │ ├── sismember.rs │ ├── smembers.rs │ ├── smismember.rs │ ├── spop.rs │ ├── srandmember.rs │ ├── srem.rs │ ├── strlen.rs │ ├── subscribe.rs │ ├── ttl.rs │ ├── unknown.rs │ ├── zadd.rs │ ├── zcard.rs │ ├── zcount.rs │ ├── zincrby.rs │ ├── zpop.rs │ ├── zrange.rs │ ├── zrangebyscore.rs │ ├── zrank.rs │ ├── zrem.rs │ ├── zremrangebyrank.rs │ ├── zremrangebyscore.rs │ ├── zrevrange.rs │ └── zscore.rs ├── config.rs ├── connection.rs ├── db.rs ├── frame.rs ├── gc.rs ├── lib.rs ├── metrics │ ├── http.rs │ └── mod.rs ├── parse.rs ├── server.rs ├── shutdown.rs ├── tikv │ ├── client.rs │ ├── encoding │ │ ├── decode.rs │ │ ├── encode.rs │ │ └── mod.rs │ ├── errors.rs │ ├── hash.rs │ ├── list.rs │ ├── lua.rs │ ├── mod.rs │ ├── set.rs │ ├── string.rs │ └── zset.rs └── utils.rs └── test ├── rediswrap.py ├── requirements.txt ├── test_generic.py ├── test_hash.py ├── test_helper.py ├── test_invalid.py ├── test_list.py ├── test_lua.py ├── test_set.py ├── test_string.py ├── test_util.py └── test_zset.py /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - master 7 | workflow_dispatch: 8 | 9 | # Concurrency ensures that only a single job or workflow using the same concurrency group will run at a time. 10 | concurrency: 11 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} 12 | cancel-in-progress: true 13 | 14 | jobs: 15 | dev: 16 | name: Run fmt, clippy and check 17 | runs-on: ubuntu-latest 18 | steps: 19 | - uses: actions/checkout@v2 20 | - uses: lukka/get-cmake@latest 21 | - name: Configure git for private modules 22 | env: 23 | TOKEN: ${{ secrets.GIT_ACTION_BOT }} 24 | run: git config --global url."https://${TOKEN}@github.com/".insteadOf "https://github.com/" 25 | - name: Install `rust` toolchain 26 | uses: actions-rs/toolchain@v1 27 | with: 28 | toolchain: nightly-2022-07-27-x86_64-unknown-linux-gnu 29 | default: true 30 | profile: minimal 31 | components: clippy, rustfmt 32 | - name: Run format 33 | env: 34 | RUSTFLAGS: -Dwarnings 35 | RUST_BACKTRACE: "1" 36 | LOG_LEVEL: INFO 37 | EXTRA_CARGO_ARGS: --no-run 38 | working-directory: ./ 39 | run: | 40 | cargo fmt --check 41 | - name: Run clippy 42 | env: 43 | RUSTFLAGS: -Dwarnings 44 | RUST_BACKTRACE: "1" 45 | LOG_LEVEL: INFO 46 | EXTRA_CARGO_ARGS: --no-run 47 | working-directory: ./ 48 | run: | 49 | cargo clippy 50 | - name: Run check 51 | env: 52 | RUSTFLAGS: -Dwarnings 53 | RUST_BACKTRACE: "1" 54 | LOG_LEVEL: INFO 55 | EXTRA_CARGO_ARGS: --no-run 56 | working-directory: ./ 57 | run: | 58 | cargo check 59 | - name: Setup tikv instance 60 | run: | 61 | curl --proto '=https' --tlsv1.2 -sSf https://tiup-mirrors.pingcap.com/install.sh | sh 62 | /home/runner/.tiup/bin/tiup playground nightly --mode tikv-slim --without-monitor & 63 | - name: Setup tidis 64 | run: | 65 | cargo build 66 | ./target/debug/tidis-server --config="config.toml" & 67 | - name: Setup test tool env 68 | uses: actions/setup-python@v3 69 | with: 70 | python-version: '3.x' 71 | cache: 'pip' 72 | - name: Setup test tool dependency 73 | working-directory: test 74 | run: pip install -r requirements.txt 75 | - name: Run E2E test 76 | working-directory: test 77 | run: python test_helper.py --port 6666 78 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | *.log 4 | *.swp 5 | *.swo 6 | *~ 7 | -------------------------------------------------------------------------------- /.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 | "type": "lldb", 9 | "request": "launch", 10 | "name": "Debug executable 'tidis-server'", 11 | "cargo": { 12 | "args": [ 13 | "build", 14 | "--bin=tidis-server", 15 | "--package=tidis" 16 | ], 17 | "filter": { 18 | "name": "tidis-server", 19 | "kind": "bin" 20 | } 21 | }, 22 | "args": [ 23 | "--config=config.toml" 24 | ], 25 | "cwd": "${workspaceFolder}", 26 | "env": { 27 | //"RUST_LOG": "debug" 28 | } 29 | }, 30 | { 31 | "type": "lldb", 32 | "request": "launch", 33 | "name": "Run executable 'tidis-server'", 34 | "cargo": { 35 | "args": [ 36 | "build", 37 | "-r", 38 | "--bin=tidis-server", 39 | "--package=tidis" 40 | ], 41 | "filter": { 42 | "name": "tidis-server", 43 | "kind": "bin" 44 | } 45 | }, 46 | "args": [ 47 | "--config=config.toml" 48 | ], 49 | "cwd": "${workspaceFolder}", 50 | "env": { 51 | //"RUST_LOG": "debug" 52 | } 53 | } 54 | ] 55 | } -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["The Tidis Authors"] 3 | edition = "2018" 4 | name = "tidis" 5 | version = "2.0.0" 6 | license = "Apache-2.0" 7 | readme = "README.md" 8 | repository = "https://github.com/tikv/tidis" 9 | description = """ 10 | The service layer for TiKV, a distributed key-value store, powered by PingCAP. 11 | """ 12 | 13 | [[bin]] 14 | name = "tidis-server" 15 | path = "src/bin/server.rs" 16 | 17 | [dependencies] 18 | async-stream = "0.3.0" 19 | atoi = "0.3.2" 20 | bytes = "1" 21 | chrono = "0.4" 22 | crc = "2.0" 23 | structopt = "0.3.14" 24 | slog = { version = "2.3", features = ["max_level_trace", "release_max_level_debug"] } 25 | slog-term = { version = "2.4" } 26 | tokio = { version = "1", features = ["full"] } 27 | tokio-stream = "0.1" 28 | tokio-util = { version = "0.7.1", features = ["rt"] } 29 | tikv-client = { git = "https://github.com/yongman/client-rust.git", branch = "dev-scanner" } 30 | #tikv-client = { path = "../client-rust" } 31 | lazy_static = "1.4.0" 32 | thiserror = "1" 33 | prometheus = { version = "0.13.0", features = ["process"]} 34 | pprof = { version = "0.9", features = ["flamegraph", "protobuf-codec"] } 35 | hyper = { version = "0.14.17", features = ["full"] } 36 | async-std = { version = "1.11.0", features = ["unstable"] } 37 | async-tls = { version = "0.11.0", features = ["server"], default-features = false } 38 | rustls = "0.19.0" 39 | rand = {version = "0.8.5", features = ["small_rng"] } 40 | regex = "1" 41 | toml = { version = "0.5.8" } 42 | serde = { version = "1.0", features = ["derive"] } 43 | futures = { version = "0.3", default-features = false } 44 | mlua = { version = "0.7.4", features = ["lua51", "async", "vendored", "macros", "send"]} 45 | sha1 = "0.10.0" 46 | hex = "0.4.3" 47 | 48 | [profile.release] 49 | opt-level = 3 50 | debug = false 51 | lto = "thin" 52 | incremental = true 53 | panic = 'unwind' 54 | debug-assertions = false 55 | overflow-checks = false 56 | rpath = false 57 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM centos:7.6.1810 as builder 2 | 3 | RUN yum install -y epel-release && \ 4 | yum clean all && \ 5 | yum makecache 6 | 7 | RUN yum install -y centos-release-scl && \ 8 | yum install -y \ 9 | devtoolset-8 \ 10 | perl cmake3 && \ 11 | yum clean all 12 | 13 | # CentOS gives cmake 3 a weird binary name, so we link it to something more normal 14 | # This is required by many build scripts, including ours. 15 | RUN ln -s /usr/bin/cmake3 /usr/bin/cmake 16 | ENV LIBRARY_PATH /usr/local/lib:$LIBRARY_PATH 17 | ENV LD_LIBRARY_PATH /usr/local/lib:$LD_LIBRARY_PATH 18 | 19 | # Install Rustup 20 | RUN curl https://sh.rustup.rs -sSf | sh -s -- --no-modify-path --default-toolchain none -y 21 | ENV PATH /root/.cargo/bin/:$PATH 22 | 23 | # Install the Rust toolchain 24 | WORKDIR /tikv 25 | COPY rust-toolchain ./ 26 | RUN rustup self update \ 27 | && rustup set profile minimal \ 28 | && rustup default $(cat "rust-toolchain") 29 | 30 | COPY src ./src/ 31 | COPY Cargo* ./ 32 | COPY rust-toolchain ./ 33 | COPY Makefile ./ 34 | RUN source /opt/rh/devtoolset-8/enable && make release 35 | 36 | FROM centos:7.6.1810 37 | 38 | COPY --from=builder /tikv/target/release/tidis-server /tidis-server 39 | 40 | EXPOSE 6666 6443 8080 41 | 42 | ENTRYPOINT ["/tidis-server"] 43 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: default check unit-test integration-test test all debug release 2 | 3 | default: check debug 4 | 5 | check: 6 | cargo check --all --all-targets --all-features 7 | cargo fmt -- --check 8 | cargo clippy --all-targets --all-features -- -D clippy::all 9 | 10 | debug: 11 | cargo build 12 | 13 | release: 14 | cargo build --release 15 | 16 | unit-test: 17 | cargo test --all 18 | 19 | integration-test: 20 | @echo "start tikv-service manually" 21 | python3 test/test_helper.py -p 6666 22 | 23 | test: unit-test integration-test 24 | 25 | all: check test 26 | -------------------------------------------------------------------------------- /config.toml: -------------------------------------------------------------------------------- 1 | [server] 2 | listen = "0.0.0.0" 3 | port = 6666 4 | tls_listen = "0.0.0.0" 5 | tls_port = 6443 6 | tls_key_file = "" 7 | tls_cert_file = "" 8 | tls_auth_client = false 9 | tls_ca_cert_file = "" 10 | pd_addrs = "127.0.0.1:2379" 11 | instance_id = "1" 12 | prometheus_listen = "0.0.0.0" 13 | prometheus_port = 8080 14 | log_level = "info" 15 | log_file = "tikv-service.log" 16 | 17 | [backend] 18 | use_async_commit = true 19 | try_one_pc_commit = true 20 | use_pessimistic_txn = false 21 | local_pool_number = 8 22 | txn_retry_count = 2 23 | txn_region_backoff_delay_ms = 2 24 | txn_region_backoff_delay_attemps = 5 25 | txn_lock_backoff_delay_ms = 2 26 | txn_lock_backoff_delay_attemps = 5 27 | 28 | completion_queue_size = 1 29 | grpc_keepalive_time = 10000 30 | grpc_keepalive_timeout = 2000 31 | allow_batch = true 32 | max_batch_wait_time = 10 33 | max_batch_size = 20 34 | max_inflight_requests = 10000 35 | -------------------------------------------------------------------------------- /docs/20220826144447.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidb-incubator/tidis/555856206f248737b12d3f148b10f08a604bc821/docs/20220826144447.jpg -------------------------------------------------------------------------------- /docs/20220826144516.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidb-incubator/tidis/555856206f248737b12d3f148b10f08a604bc821/docs/20220826144516.jpg -------------------------------------------------------------------------------- /docs/20220826144530.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidb-incubator/tidis/555856206f248737b12d3f148b10f08a604bc821/docs/20220826144530.jpg -------------------------------------------------------------------------------- /docs/20220826145733.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidb-incubator/tidis/555856206f248737b12d3f148b10f08a604bc821/docs/20220826145733.jpg -------------------------------------------------------------------------------- /docs/20220826150505.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidb-incubator/tidis/555856206f248737b12d3f148b10f08a604bc821/docs/20220826150505.jpg -------------------------------------------------------------------------------- /docs/20220902160513.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidb-incubator/tidis/555856206f248737b12d3f148b10f08a604bc821/docs/20220902160513.jpg -------------------------------------------------------------------------------- /docs/20220902160620.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidb-incubator/tidis/555856206f248737b12d3f148b10f08a604bc821/docs/20220902160620.jpg -------------------------------------------------------------------------------- /docs/performance.md: -------------------------------------------------------------------------------- 1 | # Basic Performance 2 | 3 | ## Deploy Environment 4 | 5 | - Platform: AWS c6gd 6 | - Arch: aarch64 7 | - vCPU: 16 8 | - RAM: 16G 9 | - Disk: NVME 800G (TiKV node only) 10 | 11 | ## Deploy topology 12 | 13 | We deployed a minimal TiKV cluster with three storage nodes for our basic benchmark, the topology of the cluster as follows. 14 | 15 | ``` 16 | +-------+------------------------+-----------------------+ 17 | | host | components | comments | 18 | +-------+------------------------+-----------------------+ 19 | | node1 | TiKV | /data with local ssd | 20 | +-------+------------------------+-----------------------+ 21 | | node2 | TiKV | /data with local ssd | 22 | +-------+------------------------+-----------------------+ 23 | | node3 | TiKV | /data with local ssd | 24 | +-------+------------------------+-----------------------+ 25 | | node4 | Tidis | no extra /data mounts | 26 | +-------+------------------------+-----------------------+ 27 | | node5 | Tidis | no extra /data mounts | 28 | +-------+------------------------+-----------------------+ 29 | | node6 | Tidis | no extra /data mounts | 30 | +-------+------------------------+-----------------------+ 31 | | node7 | PD & Benchmark clients | no extra /data mounts | 32 | +-------+------------------------+-----------------------+ 33 | ``` 34 | 35 | ## Benchmark 36 | 37 | ### 1. Import datasets 38 | 39 | Before the benchmark, we import a large amount of datasets for different data types, string, hash, list, set and sorted set. 40 | 30 million keys were imported totally, and this will make the benchmark results more accurate and have real reference value. 41 | 42 | ### 2. Benchmark with different concurrency 43 | 44 | - Read throughput 45 | 46 | ![](./20220826144516.jpg) 47 | 48 | - Write throughput 49 | 50 | ![](./20220826144447.jpg) 51 | 52 | - Latency in different access model 53 | 54 | ![](./20220826144530.jpg) 55 | 56 | ### 3. Performance comparison 57 | 58 | - Throughput (higher is better) 59 | 60 | ![](./20220902160513.jpg) 61 | 62 | In the comparison, we can see the all them are in the same level in read scenario, `Tidis2.0` and `Tidis1.0`'s throughput has no much gap, and better than `Titan` obviously. 63 | But in the write scenario, `Tidis2.0` throughput is better obviously, almost double to `Tidis` and `Titan`. 64 | 65 | - Latency (lower is better) 66 | 67 | ![](./20220902160620.jpg) 68 | 69 | In the comparison, we can see all them have not much difference in small concurrency for read, and `Tidis2.0`'s advantage shows up as the concurrency increases. 70 | In write scenerio, latency of `Tidis2.0` are always the best and stay stable as the concurrency increases. 71 | 72 | -------------------------------------------------------------------------------- /docs/tidis-arch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidb-incubator/tidis/555856206f248737b12d3f148b10f08a604bc821/docs/tidis-arch.png -------------------------------------------------------------------------------- /rust-toolchain: -------------------------------------------------------------------------------- 1 | nightly-2022-07-27 2 | -------------------------------------------------------------------------------- /src/client.rs: -------------------------------------------------------------------------------- 1 | use async_std::net::TcpStream; 2 | use std::fmt; 3 | use std::fmt::Formatter; 4 | use std::os::unix::io::{AsRawFd, RawFd}; 5 | use std::sync::atomic::{AtomicU64, Ordering}; 6 | use std::time::SystemTime; 7 | use tokio::sync::mpsc::Sender; 8 | 9 | // reserve id 0 10 | static COUNTER: AtomicU64 = AtomicU64::new(1); 11 | 12 | #[derive(Debug, Clone)] 13 | pub struct Client { 14 | id: u64, 15 | // name here does not constrain to uniqueness 16 | name: String, 17 | fd: RawFd, 18 | // last command played 19 | cmd: String, 20 | 21 | local_addr: String, 22 | peer_addr: String, 23 | 24 | create_time: SystemTime, 25 | last_interaction: SystemTime, 26 | 27 | kill_tx: Sender<()>, 28 | } 29 | 30 | impl Client { 31 | pub fn new(socket: TcpStream, kill_tx: Sender<()>) -> Client { 32 | let now = SystemTime::now(); 33 | Client { 34 | id: COUNTER.fetch_add(1, Ordering::Relaxed), 35 | name: "".to_owned(), 36 | fd: socket.as_raw_fd(), 37 | cmd: "".to_owned(), 38 | local_addr: socket.local_addr().unwrap().to_string(), 39 | peer_addr: socket.peer_addr().unwrap().to_string(), 40 | create_time: now, 41 | last_interaction: now, 42 | kill_tx, 43 | } 44 | } 45 | 46 | pub fn interact(&mut self, cmd_name: &str) { 47 | self.cmd = cmd_name.to_string(); 48 | self.last_interaction = SystemTime::now(); 49 | } 50 | 51 | pub async fn kill(&self) { 52 | let _ = self.kill_tx.send(()).await; 53 | } 54 | 55 | pub fn id(&self) -> u64 { 56 | self.id 57 | } 58 | 59 | pub fn name(&self) -> &str { 60 | self.name.as_str() 61 | } 62 | 63 | pub fn local_addr(&self) -> &str { 64 | &self.local_addr 65 | } 66 | pub fn peer_addr(&self) -> &str { 67 | &self.peer_addr 68 | } 69 | 70 | pub fn age(&self) -> u64 { 71 | self.create_time.elapsed().unwrap().as_secs() 72 | } 73 | 74 | pub fn idle(&self) -> u64 { 75 | self.last_interaction.elapsed().unwrap().as_secs() 76 | } 77 | 78 | pub fn set_name(&mut self, name: &str) { 79 | self.name = name.to_string(); 80 | } 81 | } 82 | 83 | impl fmt::Display for Client { 84 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 85 | write!( 86 | f, 87 | "id={} addr={} laddr={} fd={} name={} age={} idle={} flags=N \ 88 | db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 argv-mem=10 obl=0 oll=0 omem=0 \ 89 | tot-mem=0 events=r cmd={} user=default redir=-1", 90 | self.id, 91 | self.peer_addr, 92 | self.local_addr, 93 | self.fd, 94 | self.name, 95 | self.age(), 96 | self.idle(), 97 | self.cmd 98 | ) 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/cmd/auth.rs: -------------------------------------------------------------------------------- 1 | use crate::cmd::Invalid; 2 | use crate::Parse; 3 | 4 | #[derive(Debug, Clone)] 5 | pub struct Auth { 6 | passwd: String, 7 | valid: bool, 8 | } 9 | 10 | impl Auth { 11 | pub fn new(passwd: String) -> Auth { 12 | Auth { 13 | passwd, 14 | valid: true, 15 | } 16 | } 17 | 18 | pub fn passwd(&self) -> &str { 19 | &self.passwd 20 | } 21 | 22 | pub fn valid(&self) -> bool { 23 | self.valid 24 | } 25 | 26 | pub(crate) fn parse_frames(parse: &mut Parse) -> crate::Result { 27 | let passwd = parse.next_string()?; 28 | 29 | Ok(Auth { 30 | passwd, 31 | valid: true, 32 | }) 33 | } 34 | } 35 | 36 | impl Invalid for Auth { 37 | fn new_invalid() -> Auth { 38 | Auth { 39 | passwd: "".to_owned(), 40 | valid: false, 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/cmd/cluster.rs: -------------------------------------------------------------------------------- 1 | use crate::cluster::Cluster as Topo; 2 | use crate::cmd::Invalid; 3 | use crate::config::LOGGER; 4 | use crate::tikv::errors::REDIS_UNKNOWN_SUBCOMMAND; 5 | use crate::utils::{resp_err, resp_invalid_arguments}; 6 | use crate::{Connection, Parse}; 7 | use slog::debug; 8 | 9 | #[derive(Debug, Clone)] 10 | pub struct Cluster { 11 | subcommand: String, 12 | valid: bool, 13 | } 14 | 15 | impl Cluster { 16 | pub fn new(subcommand: impl ToString) -> Cluster { 17 | Cluster { 18 | subcommand: subcommand.to_string(), 19 | valid: true, 20 | } 21 | } 22 | 23 | pub(crate) fn parse_frames(parse: &mut Parse) -> crate::Result { 24 | let subcommand = parse.next_string()?; 25 | 26 | Ok(Cluster::new(subcommand)) 27 | } 28 | 29 | pub(crate) async fn apply(self, topo: &Topo, dst: &mut Connection) -> crate::Result<()> { 30 | if !self.valid { 31 | dst.write_frame(&resp_invalid_arguments()).await?; 32 | return Ok(()); 33 | } 34 | 35 | let response = match self.subcommand.to_uppercase().as_str() { 36 | "INFO" => topo.cluster_info(), 37 | "SLOTS" => topo.cluster_slots(), 38 | "NODES" => topo.cluster_nodes(), 39 | _ => resp_err(REDIS_UNKNOWN_SUBCOMMAND), 40 | }; 41 | 42 | debug!( 43 | LOGGER, 44 | "res, {} -> {}, {:?}", 45 | dst.local_addr(), 46 | dst.peer_addr(), 47 | response 48 | ); 49 | 50 | dst.write_frame(&response).await?; 51 | 52 | Ok(()) 53 | } 54 | } 55 | 56 | impl Invalid for Cluster { 57 | fn new_invalid() -> Cluster { 58 | Cluster { 59 | subcommand: "".to_owned(), 60 | valid: false, 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/cmd/cmdtype.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use crate::config::LOGGER; 4 | use crate::tikv::errors::AsyncResult; 5 | use crate::tikv::string::StringCommandCtx; 6 | use crate::utils::resp_invalid_arguments; 7 | use crate::{Connection, Frame, Parse}; 8 | use bytes::Bytes; 9 | use slog::debug; 10 | use tikv_client::Transaction; 11 | use tokio::sync::Mutex; 12 | 13 | use crate::config::is_use_txn_api; 14 | 15 | use super::Invalid; 16 | 17 | #[derive(Debug, Clone)] 18 | pub struct Type { 19 | key: String, 20 | valid: bool, 21 | } 22 | 23 | impl Type { 24 | pub fn new(key: impl ToString) -> Type { 25 | Type { 26 | key: key.to_string(), 27 | valid: true, 28 | } 29 | } 30 | 31 | pub fn key(&self) -> &str { 32 | &self.key 33 | } 34 | 35 | pub(crate) fn parse_frames(parse: &mut Parse) -> crate::Result { 36 | let key = parse.next_string()?; 37 | 38 | Ok(Type::new(key)) 39 | } 40 | 41 | pub(crate) fn parse_argv(argv: &Vec) -> crate::Result { 42 | if argv.len() != 1 { 43 | return Ok(Type::new_invalid()); 44 | } 45 | let key = &String::from_utf8_lossy(&argv[0]); 46 | Ok(Type::new(key)) 47 | } 48 | 49 | pub(crate) async fn apply(self, dst: &mut Connection) -> crate::Result<()> { 50 | let response = self.cmd_type(None).await.unwrap_or_else(Into::into); 51 | 52 | debug!( 53 | LOGGER, 54 | "res, {} -> {}, {:?}", 55 | dst.local_addr(), 56 | dst.peer_addr(), 57 | response 58 | ); 59 | 60 | dst.write_frame(&response).await?; 61 | 62 | Ok(()) 63 | } 64 | 65 | pub async fn cmd_type(&self, txn: Option>>) -> AsyncResult { 66 | if !self.valid { 67 | return Ok(resp_invalid_arguments()); 68 | } 69 | 70 | if is_use_txn_api() { 71 | StringCommandCtx::new(txn) 72 | .do_async_txnkv_type(&self.key) 73 | .await 74 | } else { 75 | StringCommandCtx::new(txn) 76 | .do_async_rawkv_type(&self.key) 77 | .await 78 | } 79 | } 80 | } 81 | 82 | impl Invalid for Type { 83 | fn new_invalid() -> Type { 84 | Type { 85 | key: "".to_owned(), 86 | valid: false, 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/cmd/debug.rs: -------------------------------------------------------------------------------- 1 | use crate::cmd::Invalid; 2 | use crate::config::LOGGER; 3 | use crate::tikv::errors::REDIS_NOT_SUPPORTED_DEBUG_SUB_COMMAND_ERR; 4 | use crate::tikv::{start_profiler, stop_profiler}; 5 | use crate::utils::{resp_err, resp_invalid_arguments, resp_ok}; 6 | use crate::{Connection, Parse}; 7 | use slog::debug; 8 | 9 | #[derive(Debug, Clone)] 10 | pub struct Debug { 11 | subcommand: String, 12 | valid: bool, 13 | } 14 | 15 | impl Debug { 16 | pub fn new(subcommand: impl ToString) -> Debug { 17 | Debug { 18 | subcommand: subcommand.to_string(), 19 | valid: true, 20 | } 21 | } 22 | 23 | pub(crate) fn parse_frames(parse: &mut Parse) -> crate::Result { 24 | let subcommand = parse.next_string()?; 25 | 26 | Ok(Debug::new(subcommand)) 27 | } 28 | 29 | pub(crate) async fn apply(self, dst: &mut Connection) -> crate::Result<()> { 30 | if !self.valid { 31 | dst.write_frame(&resp_invalid_arguments()).await?; 32 | return Ok(()); 33 | } 34 | 35 | let response = match self.subcommand.to_lowercase().as_str() { 36 | "profiler_start" => { 37 | start_profiler(); 38 | resp_ok() 39 | } 40 | "profiler_stop" => { 41 | stop_profiler(); 42 | resp_ok() 43 | } 44 | _ => resp_err(REDIS_NOT_SUPPORTED_DEBUG_SUB_COMMAND_ERR), 45 | }; 46 | 47 | debug!( 48 | LOGGER, 49 | "res, {} -> {}, {:?}", 50 | dst.local_addr(), 51 | dst.peer_addr(), 52 | response 53 | ); 54 | 55 | dst.write_frame(&response).await?; 56 | 57 | Ok(()) 58 | } 59 | } 60 | 61 | impl Invalid for Debug { 62 | fn new_invalid() -> Debug { 63 | Debug { 64 | subcommand: "".to_owned(), 65 | valid: false, 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/cmd/del.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use crate::config::is_use_txn_api; 4 | use crate::config::LOGGER; 5 | use crate::tikv::errors::{AsyncResult, REDIS_NOT_SUPPORTED_ERR}; 6 | use crate::tikv::string::StringCommandCtx; 7 | use crate::utils::{resp_err, resp_invalid_arguments}; 8 | use crate::{Connection, Frame, Parse}; 9 | use bytes::Bytes; 10 | use slog::debug; 11 | use tikv_client::Transaction; 12 | use tokio::sync::Mutex; 13 | 14 | use super::Invalid; 15 | 16 | #[derive(Debug, Clone)] 17 | pub struct Del { 18 | keys: Vec, 19 | valid: bool, 20 | } 21 | 22 | impl Del { 23 | /// Get the keys 24 | pub fn keys(&self) -> &Vec { 25 | &self.keys 26 | } 27 | 28 | pub fn add_key(&mut self, key: String) { 29 | self.keys.push(key); 30 | } 31 | 32 | pub(crate) fn parse_frames(parse: &mut Parse) -> crate::Result { 33 | let mut del = Del::default(); 34 | while let Ok(key) = parse.next_string() { 35 | del.add_key(key); 36 | } 37 | 38 | Ok(del) 39 | } 40 | 41 | pub(crate) fn parse_argv(argv: &Vec) -> crate::Result { 42 | if argv.is_empty() { 43 | return Ok(Del { 44 | keys: vec![], 45 | valid: false, 46 | }); 47 | } 48 | Ok(Del { 49 | keys: argv 50 | .iter() 51 | .map(|x| String::from_utf8_lossy(x).to_string()) 52 | .collect::>(), 53 | valid: true, 54 | }) 55 | } 56 | 57 | pub(crate) async fn apply(self, dst: &mut Connection) -> crate::Result<()> { 58 | let response = self.del(None).await.unwrap_or_else(Into::into); 59 | 60 | debug!( 61 | LOGGER, 62 | "res, {} -> {}, {:?}", 63 | dst.local_addr(), 64 | dst.peer_addr(), 65 | response 66 | ); 67 | 68 | dst.write_frame(&response).await?; 69 | 70 | Ok(()) 71 | } 72 | 73 | pub async fn del(&self, txn: Option>>) -> AsyncResult { 74 | if !self.valid { 75 | return Ok(resp_invalid_arguments()); 76 | } 77 | if is_use_txn_api() { 78 | StringCommandCtx::new(txn) 79 | .do_async_txnkv_del(&self.keys) 80 | .await 81 | } else { 82 | Ok(resp_err(REDIS_NOT_SUPPORTED_ERR)) 83 | } 84 | } 85 | } 86 | 87 | impl Default for Del { 88 | /// Create a new `Del` command which fetches `key` vector. 89 | fn default() -> Self { 90 | Del { 91 | keys: vec![], 92 | valid: true, 93 | } 94 | } 95 | } 96 | 97 | impl Invalid for Del { 98 | fn new_invalid() -> Self { 99 | Del { 100 | keys: vec![], 101 | valid: false, 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/cmd/eval.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use crate::config::is_use_txn_api; 4 | use crate::db::Db; 5 | use crate::tikv::errors::{AsyncResult, REDIS_NOT_SUPPORTED_ERR}; 6 | use crate::tikv::get_txn_client; 7 | use crate::tikv::lua::LuaCommandCtx; 8 | use crate::utils::{resp_err, resp_invalid_arguments}; 9 | use crate::{Connection, Frame, Parse}; 10 | 11 | use bytes::Bytes; 12 | use mlua::Lua; 13 | use tokio::sync::Mutex; 14 | 15 | use crate::cmd::Invalid; 16 | use crate::config::LOGGER; 17 | use slog::debug; 18 | 19 | #[derive(Debug, Clone)] 20 | pub struct Eval { 21 | script: String, 22 | numkeys: i64, 23 | keys: Vec, 24 | args: Vec, 25 | valid: bool, 26 | } 27 | 28 | impl Eval { 29 | pub fn new(script: &str, numkeys: i64) -> Eval { 30 | Eval { 31 | script: script.to_owned(), 32 | numkeys, 33 | keys: vec![], 34 | args: vec![], 35 | valid: true, 36 | } 37 | } 38 | 39 | /// Get the key 40 | pub fn keys(&self) -> &Vec { 41 | &self.keys 42 | } 43 | 44 | pub fn add_key(&mut self, key: String) { 45 | self.keys.push(key); 46 | } 47 | 48 | pub fn add_arg(&mut self, arg: Bytes) { 49 | self.args.push(arg); 50 | } 51 | 52 | pub(crate) fn parse_frames(parse: &mut Parse) -> crate::Result { 53 | let script = parse.next_string()?; 54 | let numkeys = parse.next_int()?; 55 | if numkeys < 0 { 56 | return Ok(Self::new_invalid()); 57 | } 58 | let mut eval = Eval::new(&script, numkeys); 59 | 60 | for _ in 0..eval.numkeys { 61 | if let Ok(key) = parse.next_string() { 62 | eval.add_key(key); 63 | } else { 64 | break; 65 | } 66 | } 67 | 68 | while let Ok(arg) = parse.next_bytes() { 69 | eval.add_arg(arg); 70 | } 71 | 72 | Ok(eval) 73 | } 74 | 75 | pub(crate) async fn apply( 76 | self, 77 | dst: &mut Connection, 78 | is_sha: bool, 79 | db: &Db, 80 | lua: &Option, 81 | ) -> crate::Result<()> { 82 | let response = self.eval(is_sha, db, lua).await?; 83 | 84 | debug!( 85 | LOGGER, 86 | "res, {} -> {}, {:?}", 87 | dst.local_addr(), 88 | dst.peer_addr(), 89 | response 90 | ); 91 | 92 | dst.write_frame(&response).await?; 93 | 94 | Ok(()) 95 | } 96 | 97 | async fn eval(&self, is_sha: bool, db: &Db, lua: &Option) -> AsyncResult { 98 | if !self.valid { 99 | return Ok(resp_invalid_arguments()); 100 | } 101 | 102 | if !is_use_txn_api() { 103 | return Ok(resp_err(REDIS_NOT_SUPPORTED_ERR)); 104 | } 105 | 106 | // create new txn 107 | let client = get_txn_client()?; 108 | let txn = client.begin().await?; 109 | let txn_rc = Arc::new(Mutex::new(txn)); 110 | 111 | let ctx = LuaCommandCtx::new(Some(txn_rc.clone()), lua); 112 | 113 | let resp = if is_sha { 114 | ctx.do_async_evalsha(&self.script, db, &self.keys, &self.args) 115 | .await 116 | } else { 117 | ctx.do_async_eval(&self.script, db, &self.keys, &self.args) 118 | .await 119 | }; 120 | match resp { 121 | Ok(r) => { 122 | txn_rc.lock().await.commit().await?; 123 | Ok(r) 124 | } 125 | Err(e) => { 126 | txn_rc.lock().await.rollback().await?; 127 | Ok(resp_err(e)) 128 | } 129 | } 130 | } 131 | } 132 | 133 | impl Invalid for Eval { 134 | fn new_invalid() -> Eval { 135 | Eval { 136 | script: "".to_owned(), 137 | numkeys: 0, 138 | keys: vec![], 139 | args: vec![], 140 | valid: false, 141 | } 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/cmd/exists.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use crate::config::is_use_txn_api; 4 | use crate::tikv::errors::AsyncResult; 5 | use crate::tikv::string::StringCommandCtx; 6 | use crate::utils::resp_invalid_arguments; 7 | use crate::{Connection, Frame, Parse}; 8 | use bytes::Bytes; 9 | use tikv_client::Transaction; 10 | use tokio::sync::Mutex; 11 | 12 | use crate::cmd::Invalid; 13 | use crate::config::LOGGER; 14 | use slog::debug; 15 | 16 | #[derive(Debug, Clone)] 17 | pub struct Exists { 18 | keys: Vec, 19 | valid: bool, 20 | } 21 | 22 | impl Exists { 23 | /// Get the keys 24 | pub fn keys(&self) -> &Vec { 25 | &self.keys 26 | } 27 | 28 | pub fn add_key(&mut self, key: String) { 29 | self.keys.push(key) 30 | } 31 | 32 | pub(crate) fn parse_frames(parse: &mut Parse) -> crate::Result { 33 | let mut exists = Exists::default(); 34 | 35 | while let Ok(key) = parse.next_string() { 36 | exists.add_key(key); 37 | } 38 | 39 | Ok(exists) 40 | } 41 | 42 | pub(crate) fn parse_argv(argv: &Vec) -> crate::Result { 43 | if argv.is_empty() { 44 | return Ok(Exists { 45 | keys: vec![], 46 | valid: false, 47 | }); 48 | } 49 | Ok(Exists { 50 | keys: argv 51 | .iter() 52 | .map(|x| String::from_utf8_lossy(x).to_string()) 53 | .collect::>(), 54 | valid: true, 55 | }) 56 | } 57 | 58 | pub(crate) async fn apply(self, dst: &mut Connection) -> crate::Result<()> { 59 | let response = self.exists(None).await.unwrap_or_else(Into::into); 60 | 61 | debug!( 62 | LOGGER, 63 | "res, {} -> {}, {:?}", 64 | dst.local_addr(), 65 | dst.peer_addr(), 66 | response 67 | ); 68 | 69 | dst.write_frame(&response).await?; 70 | 71 | Ok(()) 72 | } 73 | 74 | pub async fn exists(&self, txn: Option>>) -> AsyncResult { 75 | if !self.valid { 76 | return Ok(resp_invalid_arguments()); 77 | } 78 | if is_use_txn_api() { 79 | StringCommandCtx::new(txn) 80 | .do_async_txnkv_exists(&self.keys) 81 | .await 82 | } else { 83 | StringCommandCtx::new(txn) 84 | .do_async_rawkv_exists(&self.keys) 85 | .await 86 | } 87 | } 88 | } 89 | 90 | impl Default for Exists { 91 | fn default() -> Self { 92 | Exists { 93 | keys: vec![], 94 | valid: true, 95 | } 96 | } 97 | } 98 | 99 | impl Invalid for Exists { 100 | fn new_invalid() -> Exists { 101 | Exists { 102 | keys: vec![], 103 | valid: false, 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/cmd/expire.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use crate::cmd::Invalid; 4 | use crate::config::is_use_txn_api; 5 | use crate::config::LOGGER; 6 | use crate::tikv::errors::{AsyncResult, REDIS_NOT_SUPPORTED_ERR}; 7 | use crate::tikv::string::StringCommandCtx; 8 | use crate::utils::{resp_err, resp_invalid_arguments, timestamp_from_ttl}; 9 | use crate::{Connection, Frame, Parse}; 10 | use bytes::Bytes; 11 | use slog::debug; 12 | use tikv_client::Transaction; 13 | use tokio::sync::Mutex; 14 | 15 | #[derive(Debug, Clone)] 16 | pub struct Expire { 17 | key: String, 18 | seconds: i64, 19 | valid: bool, 20 | } 21 | 22 | impl Expire { 23 | pub fn new(key: impl ToString, seconds: i64) -> Expire { 24 | Expire { 25 | key: key.to_string(), 26 | seconds, 27 | valid: true, 28 | } 29 | } 30 | 31 | /// Get the key 32 | pub fn key(&self) -> &str { 33 | &self.key 34 | } 35 | 36 | pub fn seconds(&self) -> i64 { 37 | self.seconds 38 | } 39 | 40 | pub(crate) fn parse_frames(parse: &mut Parse) -> crate::Result { 41 | let key = parse.next_string()?; 42 | let seconds = parse.next_int()?; 43 | 44 | Ok(Expire { 45 | key, 46 | seconds, 47 | valid: true, 48 | }) 49 | } 50 | 51 | pub(crate) fn parse_argv(argv: &Vec) -> crate::Result { 52 | if argv.len() != 2 { 53 | return Ok(Expire::new_invalid()); 54 | } 55 | let key = String::from_utf8_lossy(&argv[0]); 56 | match String::from_utf8_lossy(&argv[1]).parse::() { 57 | Ok(v) => Ok(Expire::new(key, v)), 58 | Err(_) => Ok(Expire::new_invalid()), 59 | } 60 | } 61 | 62 | pub(crate) async fn apply( 63 | self, 64 | dst: &mut Connection, 65 | is_millis: bool, 66 | expire_at: bool, 67 | ) -> crate::Result<()> { 68 | let response = self 69 | .expire(is_millis, expire_at, None) 70 | .await 71 | .unwrap_or_else(Into::into); 72 | debug!( 73 | LOGGER, 74 | "res, {} -> {}, {:?}", 75 | dst.local_addr(), 76 | dst.peer_addr(), 77 | response 78 | ); 79 | 80 | dst.write_frame(&response).await?; 81 | 82 | Ok(()) 83 | } 84 | 85 | pub async fn expire( 86 | self, 87 | is_millis: bool, 88 | expire_at: bool, 89 | txn: Option>>, 90 | ) -> AsyncResult { 91 | if !self.valid { 92 | return Ok(resp_invalid_arguments()); 93 | } 94 | let mut ttl = self.seconds as u64; 95 | if is_use_txn_api() { 96 | if !is_millis { 97 | ttl *= 1000; 98 | } 99 | if !expire_at { 100 | ttl = timestamp_from_ttl(ttl); 101 | } 102 | StringCommandCtx::new(txn) 103 | .do_async_txnkv_expire(&self.key, ttl) 104 | .await 105 | } else { 106 | Ok(resp_err(REDIS_NOT_SUPPORTED_ERR)) 107 | } 108 | } 109 | } 110 | 111 | impl Invalid for Expire { 112 | fn new_invalid() -> Expire { 113 | Expire { 114 | key: "".to_owned(), 115 | seconds: 0, 116 | valid: false, 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/cmd/get.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use crate::config::LOGGER; 4 | use crate::tikv::errors::AsyncResult; 5 | use crate::tikv::string::StringCommandCtx; 6 | use crate::utils::resp_invalid_arguments; 7 | use crate::{Connection, Frame, Parse}; 8 | use bytes::Bytes; 9 | use slog::debug; 10 | use tikv_client::Transaction; 11 | use tokio::sync::Mutex; 12 | 13 | use crate::config::is_use_txn_api; 14 | 15 | use super::Invalid; 16 | 17 | /// Get the value of key. 18 | /// 19 | /// If the key does not exist the special value nil is returned. An error is 20 | /// returned if the value stored at key is not a string, because GET only 21 | /// handles string values. 22 | #[derive(Debug, Clone)] 23 | pub struct Get { 24 | /// Name of the key to get 25 | key: String, 26 | valid: bool, 27 | } 28 | 29 | impl Get { 30 | /// Create a new `Get` command which fetches `key`. 31 | pub fn new(key: impl ToString) -> Get { 32 | Get { 33 | key: key.to_string(), 34 | valid: true, 35 | } 36 | } 37 | 38 | /// Get the key 39 | pub fn key(&self) -> &str { 40 | &self.key 41 | } 42 | 43 | /// Parse a `Get` instance from a received frame. 44 | /// 45 | /// The `Parse` argument provides a cursor-like API to read fields from the 46 | /// `Frame`. At this point, the entire frame has already been received from 47 | /// the socket. 48 | /// 49 | /// The `GET` string has already been consumed. 50 | /// 51 | /// # Returns 52 | /// 53 | /// Returns the `Get` value on success. If the frame is malformed, `Err` is 54 | /// returned. 55 | /// 56 | /// # Format 57 | /// 58 | /// Expects an array frame containing two entries. 59 | /// 60 | /// ```text 61 | /// GET key 62 | /// ``` 63 | pub(crate) fn parse_frames(parse: &mut Parse) -> crate::Result { 64 | // The `GET` string has already been consumed. The next value is the 65 | // name of the key to get. If the next value is not a string or the 66 | // input is fully consumed, then an error is returned. 67 | let key = parse.next_string()?; 68 | 69 | Ok(Get { key, valid: true }) 70 | } 71 | 72 | pub(crate) fn parse_argv(argv: &Vec) -> crate::Result { 73 | if argv.len() != 1 { 74 | return Ok(Get::new_invalid()); 75 | } 76 | let key = &String::from_utf8_lossy(&argv[0]); 77 | Ok(Get::new(key)) 78 | } 79 | 80 | /// Apply the `Get` command to the specified `Db` instance. 81 | /// 82 | /// The response is written to `dst`. This is called by the server in order 83 | /// to execute a received command. 84 | pub(crate) async fn apply(self, dst: &mut Connection) -> crate::Result<()> { 85 | // Get the value from the shared database state 86 | let response = self.get(None).await.unwrap_or_else(Into::into); 87 | 88 | debug!( 89 | LOGGER, 90 | "res, {} -> {}, {:?}", 91 | dst.local_addr(), 92 | dst.peer_addr(), 93 | response 94 | ); 95 | 96 | // Write the response back to the client 97 | dst.write_frame(&response).await?; 98 | 99 | Ok(()) 100 | } 101 | 102 | pub async fn get(&self, txn: Option>>) -> AsyncResult { 103 | if !self.valid { 104 | return Ok(resp_invalid_arguments()); 105 | } 106 | if is_use_txn_api() { 107 | StringCommandCtx::new(txn) 108 | .do_async_txnkv_get(&self.key) 109 | .await 110 | } else { 111 | StringCommandCtx::new(txn) 112 | .do_async_rawkv_get(&self.key) 113 | .await 114 | } 115 | } 116 | } 117 | 118 | impl Invalid for Get { 119 | fn new_invalid() -> Get { 120 | Get { 121 | key: "".to_owned(), 122 | valid: false, 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/cmd/hdel.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use crate::cmd::{Invalid, Parse}; 4 | use crate::config::is_use_txn_api; 5 | use crate::tikv::errors::{AsyncResult, REDIS_NOT_SUPPORTED_ERR}; 6 | use crate::tikv::hash::HashCommandCtx; 7 | use crate::utils::{resp_err, resp_invalid_arguments}; 8 | use crate::{Connection, Frame}; 9 | 10 | use crate::config::LOGGER; 11 | use bytes::Bytes; 12 | use slog::debug; 13 | use tikv_client::Transaction; 14 | use tokio::sync::Mutex; 15 | 16 | #[derive(Debug, Clone)] 17 | pub struct Hdel { 18 | key: String, 19 | fields: Vec, 20 | valid: bool, 21 | } 22 | 23 | impl Hdel { 24 | pub fn new(key: &str) -> Hdel { 25 | Hdel { 26 | fields: vec![], 27 | key: key.to_owned(), 28 | valid: true, 29 | } 30 | } 31 | 32 | pub fn key(&self) -> &str { 33 | &self.key 34 | } 35 | 36 | pub fn add_field(&mut self, field: &str) { 37 | self.fields.push(field.to_owned()); 38 | } 39 | 40 | pub(crate) fn parse_frames(parse: &mut Parse) -> crate::Result { 41 | let key = parse.next_string()?; 42 | let mut hdel = Hdel::new(&key); 43 | while let Ok(f) = parse.next_string() { 44 | hdel.add_field(&f); 45 | } 46 | Ok(hdel) 47 | } 48 | 49 | pub(crate) fn parse_argv(argv: &Vec) -> crate::Result { 50 | if argv.len() < 2 { 51 | return Ok(Hdel::new_invalid()); 52 | } 53 | let mut hdel = Hdel::new(&String::from_utf8_lossy(&argv[0])); 54 | for arg in &argv[1..] { 55 | hdel.add_field(&String::from_utf8_lossy(arg)); 56 | } 57 | Ok(hdel) 58 | } 59 | 60 | pub(crate) async fn apply(self, dst: &mut Connection) -> crate::Result<()> { 61 | let response = self.hdel(None).await?; 62 | debug!( 63 | LOGGER, 64 | "res, {} -> {}, {:?}", 65 | dst.local_addr(), 66 | dst.peer_addr(), 67 | response 68 | ); 69 | dst.write_frame(&response).await?; 70 | 71 | Ok(()) 72 | } 73 | 74 | pub async fn hdel(&self, txn: Option>>) -> AsyncResult { 75 | if !self.valid { 76 | return Ok(resp_invalid_arguments()); 77 | } 78 | if is_use_txn_api() { 79 | HashCommandCtx::new(txn) 80 | .do_async_txnkv_hdel(&self.key, &self.fields) 81 | .await 82 | } else { 83 | Ok(resp_err(REDIS_NOT_SUPPORTED_ERR)) 84 | } 85 | } 86 | } 87 | 88 | impl Invalid for Hdel { 89 | fn new_invalid() -> Hdel { 90 | Hdel { 91 | fields: vec![], 92 | key: "".to_owned(), 93 | valid: false, 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/cmd/hexists.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use crate::cmd::{Invalid, Parse}; 4 | use crate::config::is_use_txn_api; 5 | use crate::tikv::errors::{AsyncResult, REDIS_NOT_SUPPORTED_ERR}; 6 | use crate::tikv::hash::HashCommandCtx; 7 | use crate::utils::{resp_err, resp_invalid_arguments}; 8 | use crate::{Connection, Frame}; 9 | 10 | use crate::config::LOGGER; 11 | use bytes::Bytes; 12 | use slog::debug; 13 | use tikv_client::Transaction; 14 | use tokio::sync::Mutex; 15 | 16 | #[derive(Debug, Clone)] 17 | pub struct Hexists { 18 | key: String, 19 | field: String, 20 | valid: bool, 21 | } 22 | 23 | impl Hexists { 24 | pub fn new(key: &str, field: &str) -> Hexists { 25 | Hexists { 26 | field: field.to_owned(), 27 | key: key.to_owned(), 28 | valid: true, 29 | } 30 | } 31 | 32 | pub fn key(&self) -> &str { 33 | &self.key 34 | } 35 | 36 | pub fn field(&self) -> &str { 37 | &self.field 38 | } 39 | 40 | pub(crate) fn parse_frames(parse: &mut Parse) -> crate::Result { 41 | let key = parse.next_string()?; 42 | let field = parse.next_string()?; 43 | Ok(Hexists::new(&key, &field)) 44 | } 45 | 46 | pub(crate) fn parse_argv(argv: &Vec) -> crate::Result { 47 | if argv.len() != 2 { 48 | return Ok(Hexists::new_invalid()); 49 | } 50 | Ok(Hexists::new( 51 | &String::from_utf8_lossy(&argv[0]), 52 | &String::from_utf8_lossy(&argv[1]), 53 | )) 54 | } 55 | 56 | pub(crate) async fn apply(self, dst: &mut Connection) -> crate::Result<()> { 57 | let response = self.hexists(None).await?; 58 | debug!( 59 | LOGGER, 60 | "res, {} -> {}, {:?}", 61 | dst.local_addr(), 62 | dst.peer_addr(), 63 | response 64 | ); 65 | dst.write_frame(&response).await?; 66 | 67 | Ok(()) 68 | } 69 | 70 | pub async fn hexists(&self, txn: Option>>) -> AsyncResult { 71 | if !self.valid { 72 | return Ok(resp_invalid_arguments()); 73 | } 74 | if is_use_txn_api() { 75 | HashCommandCtx::new(txn) 76 | .do_async_txnkv_hexists(&self.key, &self.field) 77 | .await 78 | } else { 79 | Ok(resp_err(REDIS_NOT_SUPPORTED_ERR)) 80 | } 81 | } 82 | } 83 | 84 | impl Invalid for Hexists { 85 | fn new_invalid() -> Hexists { 86 | Hexists { 87 | field: "".to_owned(), 88 | key: "".to_owned(), 89 | valid: false, 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/cmd/hget.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use crate::cmd::{Invalid, Parse}; 4 | use crate::config::is_use_txn_api; 5 | use crate::tikv::errors::{AsyncResult, REDIS_NOT_SUPPORTED_ERR}; 6 | use crate::tikv::hash::HashCommandCtx; 7 | use crate::utils::{resp_err, resp_invalid_arguments}; 8 | use crate::{Connection, Frame}; 9 | 10 | use crate::config::LOGGER; 11 | use bytes::Bytes; 12 | use slog::debug; 13 | use tikv_client::Transaction; 14 | use tokio::sync::Mutex; 15 | #[derive(Debug, Clone)] 16 | pub struct Hget { 17 | key: String, 18 | field: String, 19 | valid: bool, 20 | } 21 | 22 | impl Hget { 23 | pub fn new(key: &str, field: &str) -> Hget { 24 | Hget { 25 | field: field.to_owned(), 26 | key: key.to_owned(), 27 | valid: true, 28 | } 29 | } 30 | 31 | pub fn key(&self) -> &str { 32 | &self.key 33 | } 34 | 35 | pub fn field(&self) -> &str { 36 | &self.field 37 | } 38 | 39 | pub(crate) fn parse_frames(parse: &mut Parse) -> crate::Result { 40 | let key = parse.next_string()?; 41 | let field = parse.next_string()?; 42 | Ok(Hget::new(&key, &field)) 43 | } 44 | 45 | pub(crate) fn parse_argv(argv: &Vec) -> crate::Result { 46 | if argv.len() != 2 { 47 | return Ok(Hget::new_invalid()); 48 | } 49 | Ok(Hget::new( 50 | &String::from_utf8_lossy(&argv[0]), 51 | &String::from_utf8_lossy(&argv[1]), 52 | )) 53 | } 54 | 55 | pub(crate) async fn apply(self, dst: &mut Connection) -> crate::Result<()> { 56 | let response = self.hget(None).await?; 57 | debug!( 58 | LOGGER, 59 | "res, {} -> {}, {:?}", 60 | dst.local_addr(), 61 | dst.peer_addr(), 62 | response 63 | ); 64 | dst.write_frame(&response).await?; 65 | 66 | Ok(()) 67 | } 68 | 69 | pub async fn hget(&self, txn: Option>>) -> AsyncResult { 70 | if !self.valid { 71 | return Ok(resp_invalid_arguments()); 72 | } 73 | if is_use_txn_api() { 74 | HashCommandCtx::new(txn) 75 | .do_async_txnkv_hget(&self.key, &self.field) 76 | .await 77 | } else { 78 | Ok(resp_err(REDIS_NOT_SUPPORTED_ERR)) 79 | } 80 | } 81 | } 82 | 83 | impl Invalid for Hget { 84 | fn new_invalid() -> Hget { 85 | Hget { 86 | field: "".to_owned(), 87 | key: "".to_owned(), 88 | valid: false, 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/cmd/hgetall.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use crate::cmd::{Invalid, Parse}; 4 | use crate::config::is_use_txn_api; 5 | use crate::tikv::errors::{AsyncResult, REDIS_NOT_SUPPORTED_ERR}; 6 | use crate::tikv::hash::HashCommandCtx; 7 | use crate::utils::{resp_err, resp_invalid_arguments}; 8 | use crate::{Connection, Frame}; 9 | 10 | use crate::config::LOGGER; 11 | use bytes::Bytes; 12 | use slog::debug; 13 | use tikv_client::Transaction; 14 | use tokio::sync::Mutex; 15 | 16 | #[derive(Debug, Clone)] 17 | pub struct Hgetall { 18 | key: String, 19 | valid: bool, 20 | } 21 | 22 | impl Hgetall { 23 | pub fn new(key: &str) -> Hgetall { 24 | Hgetall { 25 | key: key.to_string(), 26 | valid: true, 27 | } 28 | } 29 | 30 | /// Get the key 31 | pub fn key(&self) -> &str { 32 | &self.key 33 | } 34 | 35 | pub fn set_key(&mut self, key: &str) { 36 | self.key = key.to_owned(); 37 | } 38 | 39 | pub(crate) fn parse_frames(parse: &mut Parse) -> crate::Result { 40 | let key = parse.next_string()?; 41 | Ok(Hgetall { key, valid: true }) 42 | } 43 | 44 | pub(crate) fn parse_argv(argv: &Vec) -> crate::Result { 45 | if argv.len() != 1 { 46 | return Ok(Hgetall::new_invalid()); 47 | } 48 | let key = &String::from_utf8_lossy(&argv[0]); 49 | Ok(Hgetall::new(key)) 50 | } 51 | 52 | pub(crate) async fn apply(self, dst: &mut Connection) -> crate::Result<()> { 53 | let response = self.hgetall(None).await?; 54 | debug!( 55 | LOGGER, 56 | "res, {} -> {}, {:?}", 57 | dst.local_addr(), 58 | dst.peer_addr(), 59 | response 60 | ); 61 | dst.write_frame(&response).await?; 62 | 63 | Ok(()) 64 | } 65 | 66 | pub async fn hgetall(&self, txn: Option>>) -> AsyncResult { 67 | if !self.valid { 68 | return Ok(resp_invalid_arguments()); 69 | } 70 | if is_use_txn_api() { 71 | HashCommandCtx::new(txn) 72 | .do_async_txnkv_hgetall(&self.key, true, true) 73 | .await 74 | } else { 75 | Ok(resp_err(REDIS_NOT_SUPPORTED_ERR)) 76 | } 77 | } 78 | } 79 | 80 | impl Invalid for Hgetall { 81 | fn new_invalid() -> Hgetall { 82 | Hgetall { 83 | key: "".to_string(), 84 | valid: false, 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/cmd/hincrby.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use crate::cmd::{Invalid, Parse}; 4 | use crate::config::is_use_txn_api; 5 | use crate::tikv::errors::{AsyncResult, REDIS_NOT_SUPPORTED_ERR}; 6 | use crate::tikv::hash::HashCommandCtx; 7 | use crate::utils::{resp_err, resp_invalid_arguments}; 8 | use crate::{Connection, Frame}; 9 | 10 | use crate::config::LOGGER; 11 | use bytes::Bytes; 12 | use slog::debug; 13 | use tikv_client::Transaction; 14 | use tokio::sync::Mutex; 15 | 16 | #[derive(Debug, Clone)] 17 | pub struct Hincrby { 18 | key: String, 19 | field: String, 20 | step: i64, 21 | valid: bool, 22 | } 23 | 24 | impl Hincrby { 25 | pub fn new(key: &str, field: &str, step: i64) -> Hincrby { 26 | Hincrby { 27 | key: key.to_string(), 28 | field: field.to_string(), 29 | step, 30 | valid: true, 31 | } 32 | } 33 | 34 | /// Get the key 35 | pub fn key(&self) -> &str { 36 | &self.key 37 | } 38 | 39 | pub fn field(&self) -> &str { 40 | &self.field 41 | } 42 | 43 | pub fn set_key(&mut self, key: &str) { 44 | self.key = key.to_owned(); 45 | } 46 | 47 | pub fn set_field(&mut self, field: &str) { 48 | self.field = field.to_owned(); 49 | } 50 | 51 | pub(crate) fn parse_frames(parse: &mut Parse) -> crate::Result { 52 | let key = parse.next_string()?; 53 | let field = parse.next_string()?; 54 | let step = parse.next_int()?; 55 | Ok(Hincrby { 56 | key, 57 | field, 58 | step, 59 | valid: true, 60 | }) 61 | } 62 | 63 | pub(crate) fn parse_argv(argv: &Vec) -> crate::Result { 64 | if argv.len() != 3 { 65 | return Ok(Hincrby::new_invalid()); 66 | } 67 | let key = &String::from_utf8_lossy(&argv[0]); 68 | let field = &String::from_utf8_lossy(&argv[1]); 69 | let step = String::from_utf8_lossy(&argv[2]).parse::(); 70 | match step { 71 | Ok(v) => Ok(Hincrby::new(key, field, v)), 72 | Err(_) => Ok(Hincrby::new_invalid()), 73 | } 74 | } 75 | 76 | pub(crate) async fn apply(self, dst: &mut Connection) -> crate::Result<()> { 77 | let response = self.hincrby(None).await?; 78 | debug!( 79 | LOGGER, 80 | "res, {} -> {}, {:?}", 81 | dst.local_addr(), 82 | dst.peer_addr(), 83 | response 84 | ); 85 | dst.write_frame(&response).await?; 86 | 87 | Ok(()) 88 | } 89 | 90 | pub async fn hincrby(&self, txn: Option>>) -> AsyncResult { 91 | if !self.valid { 92 | return Ok(resp_invalid_arguments()); 93 | } 94 | if is_use_txn_api() { 95 | HashCommandCtx::new(txn) 96 | .do_async_txnkv_hincrby(&self.key, &self.field, self.step) 97 | .await 98 | } else { 99 | Ok(resp_err(REDIS_NOT_SUPPORTED_ERR)) 100 | } 101 | } 102 | } 103 | 104 | impl Invalid for Hincrby { 105 | fn new_invalid() -> Hincrby { 106 | Hincrby { 107 | key: "".to_string(), 108 | field: "".to_string(), 109 | step: 0, 110 | valid: false, 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/cmd/hkeys.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use crate::cmd::{Invalid, Parse}; 4 | use crate::config::is_use_txn_api; 5 | use crate::tikv::errors::{AsyncResult, REDIS_NOT_SUPPORTED_ERR}; 6 | use crate::tikv::hash::HashCommandCtx; 7 | use crate::utils::{resp_err, resp_invalid_arguments}; 8 | use crate::{Connection, Frame}; 9 | 10 | use crate::config::LOGGER; 11 | use bytes::Bytes; 12 | use slog::debug; 13 | use tikv_client::Transaction; 14 | use tokio::sync::Mutex; 15 | 16 | #[derive(Debug, Clone)] 17 | pub struct Hkeys { 18 | key: String, 19 | valid: bool, 20 | } 21 | 22 | impl Hkeys { 23 | pub fn new(key: &str) -> Hkeys { 24 | Hkeys { 25 | key: key.to_string(), 26 | valid: true, 27 | } 28 | } 29 | 30 | /// Get the key 31 | pub fn key(&self) -> &str { 32 | &self.key 33 | } 34 | 35 | pub fn set_key(&mut self, key: &str) { 36 | self.key = key.to_owned(); 37 | } 38 | 39 | pub(crate) fn parse_frames(parse: &mut Parse) -> crate::Result { 40 | let key = parse.next_string()?; 41 | Ok(Hkeys { key, valid: true }) 42 | } 43 | 44 | pub(crate) fn parse_argv(argv: &Vec) -> crate::Result { 45 | if argv.len() != 1 { 46 | return Ok(Hkeys::new_invalid()); 47 | } 48 | let key = &String::from_utf8_lossy(&argv[0]); 49 | Ok(Hkeys::new(key)) 50 | } 51 | 52 | pub(crate) async fn apply(self, dst: &mut Connection) -> crate::Result<()> { 53 | let response = self.hkeys(None).await?; 54 | debug!( 55 | LOGGER, 56 | "res, {} -> {}, {:?}", 57 | dst.local_addr(), 58 | dst.peer_addr(), 59 | response 60 | ); 61 | dst.write_frame(&response).await?; 62 | 63 | Ok(()) 64 | } 65 | 66 | pub async fn hkeys(&self, txn: Option>>) -> AsyncResult { 67 | if !self.valid { 68 | return Ok(resp_invalid_arguments()); 69 | } 70 | if is_use_txn_api() { 71 | HashCommandCtx::new(txn) 72 | .do_async_txnkv_hgetall(&self.key, true, false) 73 | .await 74 | } else { 75 | Ok(resp_err(REDIS_NOT_SUPPORTED_ERR)) 76 | } 77 | } 78 | } 79 | 80 | impl Invalid for Hkeys { 81 | fn new_invalid() -> Hkeys { 82 | Hkeys { 83 | key: "".to_owned(), 84 | valid: false, 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/cmd/hlen.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use crate::cmd::{Invalid, Parse}; 4 | use crate::config::is_use_txn_api; 5 | use crate::tikv::errors::{AsyncResult, REDIS_NOT_SUPPORTED_ERR}; 6 | use crate::tikv::hash::HashCommandCtx; 7 | use crate::utils::{resp_err, resp_invalid_arguments}; 8 | use crate::{Connection, Frame}; 9 | 10 | use crate::config::LOGGER; 11 | use bytes::Bytes; 12 | use slog::debug; 13 | use tikv_client::Transaction; 14 | use tokio::sync::Mutex; 15 | 16 | #[derive(Debug, Clone)] 17 | pub struct Hlen { 18 | key: String, 19 | valid: bool, 20 | } 21 | 22 | impl Hlen { 23 | pub fn new(key: &str) -> Hlen { 24 | Hlen { 25 | key: key.to_string(), 26 | valid: true, 27 | } 28 | } 29 | 30 | /// Get the key 31 | pub fn key(&self) -> &str { 32 | &self.key 33 | } 34 | 35 | pub fn set_key(&mut self, key: &str) { 36 | self.key = key.to_owned(); 37 | } 38 | 39 | pub(crate) fn parse_frames(parse: &mut Parse) -> crate::Result { 40 | let key = parse.next_string()?; 41 | Ok(Hlen { key, valid: true }) 42 | } 43 | 44 | pub(crate) fn parse_argv(argv: &Vec) -> crate::Result { 45 | if argv.len() != 1 { 46 | return Ok(Hlen::new_invalid()); 47 | } 48 | let key = &String::from_utf8_lossy(&argv[0]); 49 | Ok(Hlen::new(key)) 50 | } 51 | 52 | pub(crate) async fn apply(self, dst: &mut Connection) -> crate::Result<()> { 53 | let response = self.hlen(None).await?; 54 | debug!( 55 | LOGGER, 56 | "res, {} -> {}, {:?}", 57 | dst.local_addr(), 58 | dst.peer_addr(), 59 | response 60 | ); 61 | dst.write_frame(&response).await?; 62 | 63 | Ok(()) 64 | } 65 | 66 | pub async fn hlen(&self, txn: Option>>) -> AsyncResult { 67 | if !self.valid { 68 | return Ok(resp_invalid_arguments()); 69 | } 70 | if is_use_txn_api() { 71 | HashCommandCtx::new(txn) 72 | .do_async_txnkv_hlen(&self.key) 73 | .await 74 | } else { 75 | Ok(resp_err(REDIS_NOT_SUPPORTED_ERR)) 76 | } 77 | } 78 | } 79 | 80 | impl Invalid for Hlen { 81 | fn new_invalid() -> Hlen { 82 | Hlen { 83 | key: "".to_string(), 84 | valid: false, 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/cmd/hmget.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use crate::cmd::{Invalid, Parse}; 4 | use crate::config::is_use_txn_api; 5 | use crate::tikv::errors::{AsyncResult, REDIS_NOT_SUPPORTED_ERR}; 6 | use crate::tikv::hash::HashCommandCtx; 7 | use crate::utils::{resp_err, resp_invalid_arguments}; 8 | use crate::{Connection, Frame}; 9 | 10 | use crate::config::LOGGER; 11 | use bytes::Bytes; 12 | use slog::debug; 13 | use tikv_client::Transaction; 14 | use tokio::sync::Mutex; 15 | 16 | #[derive(Debug, Clone)] 17 | pub struct Hmget { 18 | key: String, 19 | fields: Vec, 20 | valid: bool, 21 | } 22 | 23 | impl Hmget { 24 | pub fn new(key: &str) -> Hmget { 25 | Hmget { 26 | key: key.to_owned(), 27 | fields: vec![], 28 | valid: true, 29 | } 30 | } 31 | 32 | pub fn key(&self) -> &str { 33 | &self.key 34 | } 35 | 36 | pub fn fields(&self) -> &Vec { 37 | &self.fields 38 | } 39 | 40 | pub fn add_field(&mut self, field: &str) { 41 | self.fields.push(field.to_string()); 42 | } 43 | 44 | pub(crate) fn parse_frames(parse: &mut Parse) -> crate::Result { 45 | let key = parse.next_string()?; 46 | let mut hmget = Hmget::new(&key); 47 | while let Ok(field) = parse.next_string() { 48 | hmget.add_field(&field); 49 | } 50 | Ok(hmget) 51 | } 52 | 53 | pub(crate) fn parse_argv(argv: &Vec) -> crate::Result { 54 | if argv.len() < 2 { 55 | return Ok(Hmget::new_invalid()); 56 | } 57 | let key = &String::from_utf8_lossy(&argv[0]); 58 | let mut hmget = Hmget::new(key); 59 | for arg in &argv[1..argv.len()] { 60 | hmget.add_field(&String::from_utf8_lossy(arg)); 61 | } 62 | Ok(hmget) 63 | } 64 | 65 | pub(crate) async fn apply(self, dst: &mut Connection) -> crate::Result<()> { 66 | let response = self.hmget(None).await?; 67 | debug!( 68 | LOGGER, 69 | "res, {} -> {}, {:?}", 70 | dst.local_addr(), 71 | dst.peer_addr(), 72 | response 73 | ); 74 | dst.write_frame(&response).await?; 75 | 76 | Ok(()) 77 | } 78 | 79 | pub async fn hmget(&self, txn: Option>>) -> AsyncResult { 80 | if !self.valid { 81 | return Ok(resp_invalid_arguments()); 82 | } 83 | if is_use_txn_api() { 84 | HashCommandCtx::new(txn) 85 | .do_async_txnkv_hmget(&self.key, &self.fields) 86 | .await 87 | } else { 88 | Ok(resp_err(REDIS_NOT_SUPPORTED_ERR)) 89 | } 90 | } 91 | } 92 | 93 | impl Invalid for Hmget { 94 | fn new_invalid() -> Hmget { 95 | Hmget { 96 | key: "".to_owned(), 97 | fields: vec![], 98 | valid: false, 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/cmd/hset.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use crate::cmd::{Invalid, Parse}; 4 | use crate::config::is_use_txn_api; 5 | use crate::tikv::errors::{AsyncResult, REDIS_NOT_SUPPORTED_ERR}; 6 | use crate::tikv::hash::HashCommandCtx; 7 | use crate::utils::{resp_err, resp_invalid_arguments}; 8 | use crate::{Connection, Frame}; 9 | 10 | use crate::config::LOGGER; 11 | use bytes::Bytes; 12 | use slog::debug; 13 | use tikv_client::{KvPair, Transaction}; 14 | use tokio::sync::Mutex; 15 | 16 | #[derive(Debug, Clone)] 17 | pub struct Hset { 18 | key: String, 19 | field_and_value: Vec, 20 | valid: bool, 21 | } 22 | 23 | impl Hset { 24 | /// Get the key 25 | pub fn key(&self) -> &str { 26 | &self.key 27 | } 28 | 29 | pub fn set_key(&mut self, key: &str) { 30 | self.key = key.to_owned(); 31 | } 32 | 33 | /// Get the field and value pairs 34 | pub fn fields(&self) -> &Vec { 35 | &self.field_and_value 36 | } 37 | 38 | pub fn add_field_value(&mut self, kv: KvPair) { 39 | self.field_and_value.push(kv); 40 | } 41 | 42 | pub(crate) fn parse_frames(parse: &mut Parse) -> crate::Result { 43 | let mut hset = Hset::default(); 44 | 45 | let key = parse.next_string()?; 46 | hset.set_key(&key); 47 | 48 | while let Ok(field) = parse.next_string() { 49 | if let Ok(value) = parse.next_bytes() { 50 | let kv = KvPair::new(field, value.to_vec()); 51 | hset.add_field_value(kv); 52 | } else { 53 | return Err("protocol error".into()); 54 | } 55 | } 56 | Ok(hset) 57 | } 58 | 59 | pub(crate) fn parse_argv(argv: &Vec) -> crate::Result { 60 | if argv.len() % 2 != 1 { 61 | return Ok(Hset::new_invalid()); 62 | } 63 | let key = String::from_utf8_lossy(&argv[0]).to_string(); 64 | let mut hset = Hset::default(); 65 | hset.set_key(&key); 66 | 67 | for idx in (1..argv.len()).step_by(2) { 68 | let field = String::from_utf8_lossy(&argv[idx]); 69 | let value = argv[idx + 1].clone(); 70 | let kv = KvPair::new(field.to_string(), value); 71 | hset.add_field_value(kv); 72 | } 73 | Ok(hset) 74 | } 75 | 76 | pub(crate) async fn apply( 77 | self, 78 | dst: &mut Connection, 79 | is_hmset: bool, 80 | is_nx: bool, 81 | ) -> crate::Result<()> { 82 | let response = self.hset(None, is_hmset, is_nx).await?; 83 | debug!( 84 | LOGGER, 85 | "res, {} -> {}, {:?}", 86 | dst.local_addr(), 87 | dst.peer_addr(), 88 | response 89 | ); 90 | dst.write_frame(&response).await?; 91 | 92 | Ok(()) 93 | } 94 | 95 | pub async fn hset( 96 | &self, 97 | txn: Option>>, 98 | is_hmset: bool, 99 | is_nx: bool, 100 | ) -> AsyncResult { 101 | if !self.valid || (is_nx && self.field_and_value.len() != 1) { 102 | return Ok(resp_invalid_arguments()); 103 | } 104 | 105 | if is_use_txn_api() { 106 | HashCommandCtx::new(txn) 107 | .do_async_txnkv_hset(&self.key, &self.field_and_value, is_hmset, is_nx) 108 | .await 109 | } else { 110 | Ok(resp_err(REDIS_NOT_SUPPORTED_ERR)) 111 | } 112 | } 113 | } 114 | 115 | impl Default for Hset { 116 | fn default() -> Self { 117 | Hset { 118 | field_and_value: vec![], 119 | key: String::new(), 120 | valid: true, 121 | } 122 | } 123 | } 124 | 125 | impl Invalid for Hset { 126 | fn new_invalid() -> Hset { 127 | Hset { 128 | field_and_value: vec![], 129 | key: String::new(), 130 | valid: false, 131 | } 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/cmd/hstrlen.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use crate::cmd::{Invalid, Parse}; 4 | use crate::config::is_use_txn_api; 5 | use crate::tikv::errors::{AsyncResult, REDIS_NOT_SUPPORTED_ERR}; 6 | use crate::tikv::hash::HashCommandCtx; 7 | use crate::utils::{resp_err, resp_invalid_arguments}; 8 | use crate::{Connection, Frame}; 9 | 10 | use crate::config::LOGGER; 11 | use bytes::Bytes; 12 | use slog::debug; 13 | use tikv_client::Transaction; 14 | use tokio::sync::Mutex; 15 | 16 | #[derive(Debug, Clone)] 17 | pub struct Hstrlen { 18 | key: String, 19 | field: String, 20 | valid: bool, 21 | } 22 | 23 | impl Hstrlen { 24 | pub fn new(key: &str, field: &str) -> Hstrlen { 25 | Hstrlen { 26 | field: field.to_owned(), 27 | key: key.to_owned(), 28 | valid: true, 29 | } 30 | } 31 | 32 | pub fn new_invalid() -> Hstrlen { 33 | Hstrlen { 34 | field: "".to_owned(), 35 | key: "".to_owned(), 36 | valid: false, 37 | } 38 | } 39 | 40 | pub fn key(&self) -> &str { 41 | &self.key 42 | } 43 | 44 | pub fn field(&self) -> &str { 45 | &self.field 46 | } 47 | 48 | pub(crate) fn parse_frames(parse: &mut Parse) -> crate::Result { 49 | let key = parse.next_string()?; 50 | let field = parse.next_string()?; 51 | Ok(Hstrlen::new(&key, &field)) 52 | } 53 | 54 | pub(crate) fn parse_argv(argv: &Vec) -> crate::Result { 55 | if argv.len() != 2 { 56 | return Ok(Hstrlen::new_invalid()); 57 | } 58 | Ok(Hstrlen::new( 59 | &String::from_utf8_lossy(&argv[0]), 60 | &String::from_utf8_lossy(&argv[1]), 61 | )) 62 | } 63 | 64 | pub(crate) async fn apply(self, dst: &mut Connection) -> crate::Result<()> { 65 | let response = self.hstrlen(None).await?; 66 | debug!( 67 | LOGGER, 68 | "res, {} -> {}, {:?}", 69 | dst.local_addr(), 70 | dst.peer_addr(), 71 | response 72 | ); 73 | dst.write_frame(&response).await?; 74 | 75 | Ok(()) 76 | } 77 | 78 | pub async fn hstrlen(&self, txn: Option>>) -> AsyncResult { 79 | if !self.valid { 80 | return Ok(resp_invalid_arguments()); 81 | } 82 | if is_use_txn_api() { 83 | HashCommandCtx::new(txn) 84 | .do_async_txnkv_hstrlen(&self.key, &self.field) 85 | .await 86 | } else { 87 | Ok(resp_err(REDIS_NOT_SUPPORTED_ERR)) 88 | } 89 | } 90 | } 91 | 92 | impl Invalid for Hstrlen { 93 | fn new_invalid() -> Hstrlen { 94 | Hstrlen { 95 | field: "".to_owned(), 96 | key: "".to_owned(), 97 | valid: false, 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/cmd/hvals.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use crate::cmd::{Invalid, Parse}; 4 | use crate::config::is_use_txn_api; 5 | use crate::tikv::errors::{AsyncResult, REDIS_NOT_SUPPORTED_ERR}; 6 | use crate::tikv::hash::HashCommandCtx; 7 | use crate::utils::{resp_err, resp_invalid_arguments}; 8 | use crate::{Connection, Frame}; 9 | 10 | use crate::config::LOGGER; 11 | use bytes::Bytes; 12 | use slog::debug; 13 | use tikv_client::Transaction; 14 | use tokio::sync::Mutex; 15 | 16 | #[derive(Debug, Clone)] 17 | pub struct Hvals { 18 | key: String, 19 | valid: bool, 20 | } 21 | 22 | impl Hvals { 23 | pub fn new(key: &str) -> Hvals { 24 | Hvals { 25 | key: key.to_string(), 26 | valid: true, 27 | } 28 | } 29 | 30 | /// Get the key 31 | pub fn key(&self) -> &str { 32 | &self.key 33 | } 34 | 35 | pub fn set_key(&mut self, key: &str) { 36 | self.key = key.to_owned(); 37 | } 38 | 39 | pub(crate) fn parse_frames(parse: &mut Parse) -> crate::Result { 40 | let key = parse.next_string()?; 41 | Ok(Hvals { key, valid: true }) 42 | } 43 | 44 | pub(crate) fn parse_argv(argv: &Vec) -> crate::Result { 45 | if argv.len() != 1 { 46 | return Ok(Hvals::new_invalid()); 47 | } 48 | let key = &String::from_utf8_lossy(&argv[0]); 49 | Ok(Hvals::new(key)) 50 | } 51 | 52 | pub(crate) async fn apply(self, dst: &mut Connection) -> crate::Result<()> { 53 | let response = self.hvals(None).await?; 54 | debug!( 55 | LOGGER, 56 | "res, {} -> {}, {:?}", 57 | dst.local_addr(), 58 | dst.peer_addr(), 59 | response 60 | ); 61 | dst.write_frame(&response).await?; 62 | 63 | Ok(()) 64 | } 65 | 66 | pub async fn hvals(&self, txn: Option>>) -> AsyncResult { 67 | if !self.valid { 68 | return Ok(resp_invalid_arguments()); 69 | } 70 | if is_use_txn_api() { 71 | HashCommandCtx::new(txn) 72 | .do_async_txnkv_hgetall(&self.key, false, true) 73 | .await 74 | } else { 75 | Ok(resp_err(REDIS_NOT_SUPPORTED_ERR)) 76 | } 77 | } 78 | } 79 | 80 | impl Invalid for Hvals { 81 | fn new_invalid() -> Hvals { 82 | Hvals { 83 | key: "".to_owned(), 84 | valid: false, 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/cmd/incrdecr.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use crate::config::is_use_txn_api; 4 | use crate::tikv::errors::{AsyncResult, DECREMENT_OVERFLOW}; 5 | use crate::tikv::string::StringCommandCtx; 6 | use crate::utils::{resp_err, resp_invalid_arguments}; 7 | use crate::{Connection, Frame, Parse}; 8 | 9 | use crate::cmd::Invalid; 10 | use crate::config::LOGGER; 11 | use bytes::Bytes; 12 | use slog::debug; 13 | use tikv_client::Transaction; 14 | use tokio::sync::Mutex; 15 | 16 | #[derive(Debug, Clone)] 17 | pub struct IncrDecr { 18 | key: String, 19 | step: i64, 20 | valid: bool, 21 | } 22 | 23 | impl IncrDecr { 24 | pub fn new(key: impl ToString, step: i64) -> IncrDecr { 25 | IncrDecr { 26 | key: key.to_string(), 27 | step, 28 | valid: true, 29 | } 30 | } 31 | 32 | pub fn key(&self) -> &str { 33 | &self.key 34 | } 35 | 36 | pub(crate) fn parse_frames(parse: &mut Parse, single_step: bool) -> crate::Result { 37 | let key = parse.next_string()?; 38 | let step = if single_step { 1 } else { parse.next_int()? }; 39 | Ok(IncrDecr { 40 | key, 41 | step, 42 | valid: true, 43 | }) 44 | } 45 | 46 | pub(crate) fn parse_argv(argv: &Vec, single_step: bool) -> crate::Result { 47 | if (single_step && argv.len() != 1) || (!single_step && argv.len() != 2) { 48 | return Ok(IncrDecr::new_invalid()); 49 | } 50 | let key = &String::from_utf8_lossy(&argv[0]); 51 | let step = if single_step { 52 | Ok(1) 53 | } else { 54 | String::from_utf8_lossy(&argv[1]).parse::() 55 | }; 56 | 57 | match step { 58 | Ok(step) => Ok(IncrDecr::new(key, step)), 59 | Err(_) => Ok(IncrDecr::new_invalid()), 60 | } 61 | } 62 | 63 | pub(crate) async fn apply(mut self, dst: &mut Connection, inc: bool) -> crate::Result<()> { 64 | let response = self.incr_by(None, inc).await.unwrap_or_else(Into::into); 65 | 66 | debug!( 67 | LOGGER, 68 | "res, {} -> {}, {:?}", 69 | dst.local_addr(), 70 | dst.peer_addr(), 71 | response 72 | ); 73 | 74 | dst.write_frame(&response).await?; 75 | 76 | Ok(()) 77 | } 78 | 79 | pub async fn incr_by( 80 | &mut self, 81 | txn: Option>>, 82 | inc: bool, 83 | ) -> AsyncResult { 84 | if !self.valid { 85 | return Ok(resp_invalid_arguments()); 86 | } 87 | 88 | if !inc { 89 | if self.step == i64::MIN { 90 | return Ok(resp_err(DECREMENT_OVERFLOW)); 91 | } 92 | self.step = -self.step; 93 | } 94 | 95 | if is_use_txn_api() { 96 | StringCommandCtx::new(txn) 97 | .do_async_txnkv_incr(&self.key, self.step) 98 | .await 99 | } else { 100 | StringCommandCtx::new(None) 101 | .do_async_rawkv_incr(&self.key, self.step) 102 | .await 103 | } 104 | } 105 | } 106 | 107 | impl Invalid for IncrDecr { 108 | fn new_invalid() -> IncrDecr { 109 | IncrDecr { 110 | key: "".to_owned(), 111 | step: 0, 112 | valid: false, 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/cmd/lindex.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use crate::cmd::{Invalid, Parse}; 4 | use crate::config::is_use_txn_api; 5 | use crate::tikv::errors::{AsyncResult, REDIS_NOT_SUPPORTED_ERR}; 6 | use crate::tikv::list::ListCommandCtx; 7 | use crate::utils::{resp_err, resp_invalid_arguments}; 8 | use crate::{Connection, Frame}; 9 | 10 | use crate::config::LOGGER; 11 | use bytes::Bytes; 12 | use slog::debug; 13 | use tikv_client::Transaction; 14 | use tokio::sync::Mutex; 15 | 16 | #[derive(Debug, Clone)] 17 | pub struct Lindex { 18 | key: String, 19 | idx: i64, 20 | valid: bool, 21 | } 22 | 23 | impl Lindex { 24 | pub fn new(key: &str, idx: i64) -> Lindex { 25 | Lindex { 26 | key: key.to_owned(), 27 | idx, 28 | valid: true, 29 | } 30 | } 31 | 32 | pub fn key(&self) -> &str { 33 | &self.key 34 | } 35 | 36 | pub(crate) fn parse_frames(parse: &mut Parse) -> crate::Result { 37 | let key = parse.next_string()?; 38 | let idx = parse.next_int()?; 39 | 40 | Ok(Lindex { 41 | key, 42 | idx, 43 | valid: true, 44 | }) 45 | } 46 | 47 | pub(crate) fn parse_argv(argv: &Vec) -> crate::Result { 48 | if argv.len() != 2 { 49 | return Ok(Lindex::new_invalid()); 50 | } 51 | let key = &String::from_utf8_lossy(&argv[0]); 52 | let idx = match String::from_utf8_lossy(&argv[1]).parse::() { 53 | Ok(v) => v, 54 | Err(_) => return Ok(Lindex::new_invalid()), 55 | }; 56 | Ok(Lindex::new(key, idx)) 57 | } 58 | 59 | pub(crate) async fn apply(self, dst: &mut Connection) -> crate::Result<()> { 60 | let response = self.lindex(None).await?; 61 | debug!( 62 | LOGGER, 63 | "res, {} -> {}, {:?}", 64 | dst.local_addr(), 65 | dst.peer_addr(), 66 | response 67 | ); 68 | dst.write_frame(&response).await?; 69 | 70 | Ok(()) 71 | } 72 | 73 | pub async fn lindex(self, txn: Option>>) -> AsyncResult { 74 | if !self.valid { 75 | return Ok(resp_invalid_arguments()); 76 | } 77 | if is_use_txn_api() { 78 | ListCommandCtx::new(txn) 79 | .do_async_txnkv_lindex(&self.key, self.idx) 80 | .await 81 | } else { 82 | Ok(resp_err(REDIS_NOT_SUPPORTED_ERR)) 83 | } 84 | } 85 | } 86 | 87 | impl Invalid for Lindex { 88 | fn new_invalid() -> Lindex { 89 | Lindex { 90 | key: "".to_owned(), 91 | idx: 0, 92 | valid: false, 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/cmd/linsert.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use crate::cmd::Parse; 4 | use crate::config::is_use_txn_api; 5 | use crate::tikv::errors::{AsyncResult, REDIS_NOT_SUPPORTED_ERR}; 6 | use crate::tikv::list::ListCommandCtx; 7 | use crate::utils::{resp_err, resp_invalid_arguments}; 8 | use crate::{Connection, Frame}; 9 | 10 | use crate::config::LOGGER; 11 | use bytes::Bytes; 12 | use slog::debug; 13 | use tikv_client::Transaction; 14 | use tokio::sync::Mutex; 15 | 16 | use super::Invalid; 17 | 18 | #[derive(Debug, Clone)] 19 | pub struct Linsert { 20 | key: String, 21 | before_pivot: bool, 22 | pivot: Bytes, 23 | element: Bytes, 24 | valid: bool, 25 | } 26 | 27 | impl Linsert { 28 | pub fn new(key: &str, before_pivot: bool, pivot: Bytes, element: Bytes) -> Linsert { 29 | Linsert { 30 | key: key.to_owned(), 31 | before_pivot, 32 | pivot, 33 | element, 34 | valid: true, 35 | } 36 | } 37 | 38 | pub(crate) fn parse_frames(parse: &mut Parse) -> crate::Result { 39 | let key = parse.next_string()?; 40 | let pos = parse.next_string()?; 41 | let before_pivot = match pos.to_lowercase().as_str() { 42 | "before" => true, 43 | "after" => false, 44 | _ => { 45 | return Ok(Linsert::new_invalid()); 46 | } 47 | }; 48 | let pivot = parse.next_bytes()?; 49 | let element = parse.next_bytes()?; 50 | 51 | Ok(Linsert { 52 | key, 53 | before_pivot, 54 | pivot, 55 | element, 56 | valid: true, 57 | }) 58 | } 59 | 60 | pub(crate) fn parse_argv(argv: &Vec) -> crate::Result { 61 | if argv.len() != 4 { 62 | return Ok(Linsert::new_invalid()); 63 | } 64 | let key = &String::from_utf8_lossy(&argv[0]); 65 | let before_pivot = match String::from_utf8_lossy(&argv[1]).to_lowercase().as_str() { 66 | "before" => true, 67 | "after" => false, 68 | _ => { 69 | return Ok(Linsert::new_invalid()); 70 | } 71 | }; 72 | 73 | let pivot = argv[2].clone(); 74 | let element = argv[3].clone(); 75 | Ok(Linsert::new(key, before_pivot, pivot, element)) 76 | } 77 | 78 | pub(crate) async fn apply(self, dst: &mut Connection) -> crate::Result<()> { 79 | let response = self.linsert(None).await?; 80 | debug!( 81 | LOGGER, 82 | "res, {} -> {}, {:?}", 83 | dst.local_addr(), 84 | dst.peer_addr(), 85 | response 86 | ); 87 | dst.write_frame(&response).await?; 88 | 89 | Ok(()) 90 | } 91 | 92 | pub async fn linsert(&self, txn: Option>>) -> AsyncResult { 93 | if !self.valid { 94 | return Ok(resp_invalid_arguments()); 95 | } 96 | if is_use_txn_api() { 97 | ListCommandCtx::new(txn) 98 | .do_async_txnkv_linsert(&self.key, self.before_pivot, &self.pivot, &self.element) 99 | .await 100 | } else { 101 | Ok(resp_err(REDIS_NOT_SUPPORTED_ERR)) 102 | } 103 | } 104 | } 105 | 106 | impl Invalid for Linsert { 107 | fn new_invalid() -> Linsert { 108 | Linsert { 109 | key: "".to_owned(), 110 | before_pivot: false, 111 | pivot: Bytes::new(), 112 | element: Bytes::new(), 113 | valid: false, 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/cmd/llen.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use crate::cmd::{Invalid, Parse}; 4 | use crate::config::is_use_txn_api; 5 | use crate::tikv::errors::{AsyncResult, REDIS_NOT_SUPPORTED_ERR}; 6 | use crate::tikv::list::ListCommandCtx; 7 | use crate::utils::{resp_err, resp_invalid_arguments}; 8 | use crate::{Connection, Frame}; 9 | 10 | use crate::config::LOGGER; 11 | use bytes::Bytes; 12 | use slog::debug; 13 | use tikv_client::Transaction; 14 | use tokio::sync::Mutex; 15 | 16 | #[derive(Debug, Clone)] 17 | pub struct Llen { 18 | key: String, 19 | valid: bool, 20 | } 21 | 22 | impl Llen { 23 | pub fn new(key: &str) -> Llen { 24 | Llen { 25 | key: key.to_owned(), 26 | valid: true, 27 | } 28 | } 29 | 30 | pub fn key(&self) -> &str { 31 | &self.key 32 | } 33 | 34 | pub(crate) fn parse_frames(parse: &mut Parse) -> crate::Result { 35 | let key = parse.next_string()?; 36 | 37 | Ok(Llen { key, valid: true }) 38 | } 39 | 40 | pub(crate) fn parse_argv(argv: &Vec) -> crate::Result { 41 | if argv.len() != 1 { 42 | return Ok(Llen::new_invalid()); 43 | } 44 | let key = &String::from_utf8_lossy(&argv[0]); 45 | Ok(Llen::new(key)) 46 | } 47 | 48 | pub(crate) async fn apply(self, dst: &mut Connection) -> crate::Result<()> { 49 | let response = self.llen(None).await?; 50 | debug!( 51 | LOGGER, 52 | "res, {} -> {}, {:?}", 53 | dst.local_addr(), 54 | dst.peer_addr(), 55 | response 56 | ); 57 | dst.write_frame(&response).await?; 58 | 59 | Ok(()) 60 | } 61 | 62 | pub async fn llen(self, txn: Option>>) -> AsyncResult { 63 | if !self.valid { 64 | return Ok(resp_invalid_arguments()); 65 | } 66 | if is_use_txn_api() { 67 | ListCommandCtx::new(txn) 68 | .do_async_txnkv_llen(&self.key) 69 | .await 70 | } else { 71 | Ok(resp_err(REDIS_NOT_SUPPORTED_ERR)) 72 | } 73 | } 74 | } 75 | 76 | impl Invalid for Llen { 77 | fn new_invalid() -> Llen { 78 | Llen { 79 | key: "".to_owned(), 80 | valid: false, 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/cmd/lrange.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use crate::cmd::{Invalid, Parse}; 4 | use crate::config::is_use_txn_api; 5 | use crate::tikv::errors::{AsyncResult, REDIS_NOT_SUPPORTED_ERR}; 6 | use crate::tikv::list::ListCommandCtx; 7 | use crate::utils::{resp_err, resp_invalid_arguments}; 8 | use crate::{Connection, Frame}; 9 | 10 | use crate::config::LOGGER; 11 | use bytes::Bytes; 12 | use slog::debug; 13 | use tikv_client::Transaction; 14 | use tokio::sync::Mutex; 15 | 16 | #[derive(Debug, Clone)] 17 | pub struct Lrange { 18 | key: String, 19 | left: i64, 20 | right: i64, 21 | valid: bool, 22 | } 23 | 24 | impl Lrange { 25 | pub fn new(key: &str, left: i64, right: i64) -> Lrange { 26 | Lrange { 27 | key: key.to_owned(), 28 | left, 29 | right, 30 | valid: true, 31 | } 32 | } 33 | 34 | pub fn key(&self) -> &str { 35 | &self.key 36 | } 37 | 38 | pub(crate) fn parse_frames(parse: &mut Parse) -> crate::Result { 39 | let key = parse.next_string()?; 40 | let left = parse.next_int()?; 41 | let right = parse.next_int()?; 42 | 43 | Ok(Lrange { 44 | key, 45 | left, 46 | right, 47 | valid: true, 48 | }) 49 | } 50 | 51 | pub(crate) fn parse_argv(argv: &Vec) -> crate::Result { 52 | if argv.len() != 3 { 53 | return Ok(Lrange::new_invalid()); 54 | } 55 | let key = &String::from_utf8_lossy(&argv[0]); 56 | let left = match String::from_utf8_lossy(&argv[1]).parse::() { 57 | Ok(v) => v, 58 | Err(_) => return Ok(Lrange::new_invalid()), 59 | }; 60 | 61 | let right = match String::from_utf8_lossy(&argv[2]).parse::() { 62 | Ok(v) => v, 63 | Err(_) => return Ok(Lrange::new_invalid()), 64 | }; 65 | Ok(Lrange::new(key, left, right)) 66 | } 67 | 68 | pub(crate) async fn apply(self, dst: &mut Connection) -> crate::Result<()> { 69 | let response = self.lrange(None).await?; 70 | debug!( 71 | LOGGER, 72 | "res, {} -> {}, {:?}", 73 | dst.local_addr(), 74 | dst.peer_addr(), 75 | response 76 | ); 77 | dst.write_frame(&response).await?; 78 | 79 | Ok(()) 80 | } 81 | 82 | pub async fn lrange(&self, txn: Option>>) -> AsyncResult { 83 | if !self.valid { 84 | return Ok(resp_invalid_arguments()); 85 | } 86 | if is_use_txn_api() { 87 | ListCommandCtx::new(txn) 88 | .do_async_txnkv_lrange(&self.key, self.left, self.right) 89 | .await 90 | } else { 91 | Ok(resp_err(REDIS_NOT_SUPPORTED_ERR)) 92 | } 93 | } 94 | } 95 | 96 | impl Invalid for Lrange { 97 | fn new_invalid() -> Lrange { 98 | Lrange { 99 | key: "".to_owned(), 100 | left: 0, 101 | right: 0, 102 | valid: false, 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/cmd/lrem.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use crate::cmd::Parse; 4 | use crate::config::is_use_txn_api; 5 | use crate::tikv::errors::{AsyncResult, REDIS_NOT_SUPPORTED_ERR}; 6 | use crate::tikv::list::ListCommandCtx; 7 | use crate::utils::{resp_err, resp_invalid_arguments}; 8 | use crate::{Connection, Frame}; 9 | 10 | use crate::config::LOGGER; 11 | use bytes::Bytes; 12 | use slog::debug; 13 | use tikv_client::Transaction; 14 | use tokio::sync::Mutex; 15 | 16 | use super::Invalid; 17 | 18 | #[derive(Debug, Clone)] 19 | pub struct Lrem { 20 | key: String, 21 | count: i64, 22 | element: Bytes, 23 | valid: bool, 24 | } 25 | 26 | impl Lrem { 27 | pub fn new(key: &str, count: i64, element: Bytes) -> Lrem { 28 | Lrem { 29 | key: key.to_owned(), 30 | count, 31 | element, 32 | valid: true, 33 | } 34 | } 35 | 36 | pub(crate) fn parse_frames(parse: &mut Parse) -> crate::Result { 37 | let key = parse.next_string()?; 38 | let count = parse.next_int()?; 39 | let element = parse.next_bytes()?; 40 | 41 | Ok(Lrem { 42 | key, 43 | count, 44 | element, 45 | valid: true, 46 | }) 47 | } 48 | 49 | pub(crate) fn parse_argv(argv: &Vec) -> crate::Result { 50 | if argv.len() != 3 { 51 | return Ok(Lrem::new_invalid()); 52 | } 53 | let key = &String::from_utf8_lossy(&argv[0]); 54 | let count = String::from_utf8_lossy(&argv[1]).parse::()?; 55 | 56 | let element = argv[2].clone(); 57 | Ok(Lrem::new(key, count, element)) 58 | } 59 | 60 | pub(crate) async fn apply(self, dst: &mut Connection) -> crate::Result<()> { 61 | let response = self.lrem(None).await?; 62 | debug!( 63 | LOGGER, 64 | "res, {} -> {}, {:?}", 65 | dst.local_addr(), 66 | dst.peer_addr(), 67 | response 68 | ); 69 | dst.write_frame(&response).await?; 70 | 71 | Ok(()) 72 | } 73 | 74 | pub async fn lrem(&self, txn: Option>>) -> AsyncResult { 75 | if !self.valid { 76 | return Ok(resp_invalid_arguments()); 77 | } 78 | if is_use_txn_api() { 79 | let mut from_head = true; 80 | let mut count = self.count; 81 | if self.count < 0 { 82 | from_head = false; 83 | count = -count; 84 | } 85 | ListCommandCtx::new(txn) 86 | .do_async_txnkv_lrem(&self.key, count as usize, from_head, &self.element) 87 | .await 88 | } else { 89 | Ok(resp_err(REDIS_NOT_SUPPORTED_ERR)) 90 | } 91 | } 92 | } 93 | 94 | impl Invalid for Lrem { 95 | fn new_invalid() -> Lrem { 96 | Lrem { 97 | key: "".to_owned(), 98 | count: 0, 99 | element: Bytes::new(), 100 | valid: false, 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/cmd/lset.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use crate::cmd::{Invalid, Parse}; 4 | use crate::config::is_use_txn_api; 5 | use crate::tikv::errors::{AsyncResult, REDIS_NOT_SUPPORTED_ERR}; 6 | use crate::tikv::list::ListCommandCtx; 7 | use crate::utils::{resp_err, resp_invalid_arguments}; 8 | use crate::{Connection, Frame}; 9 | 10 | use crate::config::LOGGER; 11 | use bytes::Bytes; 12 | use slog::debug; 13 | use tikv_client::Transaction; 14 | use tokio::sync::Mutex; 15 | 16 | #[derive(Debug, Clone)] 17 | pub struct Lset { 18 | key: String, 19 | idx: i64, 20 | element: Bytes, 21 | valid: bool, 22 | } 23 | 24 | impl Lset { 25 | pub fn new(key: &str, idx: i64, ele: Bytes) -> Lset { 26 | Lset { 27 | key: key.to_owned(), 28 | idx, 29 | element: ele, 30 | valid: true, 31 | } 32 | } 33 | 34 | pub fn key(&self) -> &str { 35 | &self.key 36 | } 37 | 38 | pub(crate) fn parse_frames(parse: &mut Parse) -> crate::Result { 39 | let key = parse.next_string()?; 40 | let idx = parse.next_int()?; 41 | let element = parse.next_bytes()?; 42 | 43 | Ok(Lset { 44 | key, 45 | idx, 46 | element, 47 | valid: true, 48 | }) 49 | } 50 | 51 | pub(crate) fn parse_argv(argv: &Vec) -> crate::Result { 52 | if argv.len() != 3 { 53 | return Ok(Lset::new_invalid()); 54 | } 55 | let key = &String::from_utf8_lossy(&argv[0]); 56 | let idx = match String::from_utf8_lossy(&argv[1]).parse::() { 57 | Ok(v) => v, 58 | Err(_) => return Ok(Lset::new_invalid()), 59 | }; 60 | let ele = argv[2].clone(); 61 | Ok(Lset::new(key, idx, ele)) 62 | } 63 | 64 | pub(crate) async fn apply(self, dst: &mut Connection) -> crate::Result<()> { 65 | let response = self.lset(None).await?; 66 | debug!( 67 | LOGGER, 68 | "res, {} -> {}, {:?}", 69 | dst.local_addr(), 70 | dst.peer_addr(), 71 | response 72 | ); 73 | dst.write_frame(&response).await?; 74 | 75 | Ok(()) 76 | } 77 | 78 | pub async fn lset(&self, txn: Option>>) -> AsyncResult { 79 | if !self.valid { 80 | return Ok(resp_invalid_arguments()); 81 | } 82 | if is_use_txn_api() { 83 | ListCommandCtx::new(txn) 84 | .do_async_txnkv_lset(&self.key, self.idx, &self.element) 85 | .await 86 | } else { 87 | Ok(resp_err(REDIS_NOT_SUPPORTED_ERR)) 88 | } 89 | } 90 | } 91 | 92 | impl Invalid for Lset { 93 | fn new_invalid() -> Lset { 94 | Lset { 95 | key: "".to_owned(), 96 | idx: 0, 97 | element: Bytes::new(), 98 | valid: false, 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/cmd/ltrim.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use crate::cmd::{Invalid, Parse}; 4 | use crate::config::is_use_txn_api; 5 | use crate::tikv::errors::{AsyncResult, REDIS_NOT_SUPPORTED_ERR}; 6 | use crate::tikv::list::ListCommandCtx; 7 | use crate::utils::{resp_err, resp_invalid_arguments}; 8 | use crate::{Connection, Frame}; 9 | 10 | use crate::config::LOGGER; 11 | use bytes::Bytes; 12 | use slog::debug; 13 | use tikv_client::Transaction; 14 | use tokio::sync::Mutex; 15 | 16 | #[derive(Debug, Clone)] 17 | pub struct Ltrim { 18 | key: String, 19 | start: i64, 20 | end: i64, 21 | valid: bool, 22 | } 23 | 24 | impl Ltrim { 25 | pub fn new(key: &str, start: i64, end: i64) -> Ltrim { 26 | Ltrim { 27 | key: key.to_owned(), 28 | start, 29 | end, 30 | valid: true, 31 | } 32 | } 33 | 34 | pub fn key(&self) -> &str { 35 | &self.key 36 | } 37 | 38 | pub(crate) fn parse_frames(parse: &mut Parse) -> crate::Result { 39 | let key = parse.next_string()?; 40 | let start = parse.next_int()?; 41 | let end = parse.next_int()?; 42 | 43 | Ok(Ltrim { 44 | key, 45 | start, 46 | end, 47 | valid: true, 48 | }) 49 | } 50 | 51 | pub(crate) fn parse_argv(argv: &Vec) -> crate::Result { 52 | if argv.len() != 3 { 53 | return Ok(Ltrim::new_invalid()); 54 | } 55 | let key = &String::from_utf8_lossy(&argv[0]); 56 | let start = match String::from_utf8_lossy(&argv[1]).parse::() { 57 | Ok(v) => v, 58 | Err(_) => return Ok(Ltrim::new_invalid()), 59 | }; 60 | 61 | let end = match String::from_utf8_lossy(&argv[2]).parse::() { 62 | Ok(v) => v, 63 | Err(_) => return Ok(Ltrim::new_invalid()), 64 | }; 65 | Ok(Ltrim::new(key, start, end)) 66 | } 67 | 68 | pub(crate) async fn apply(self, dst: &mut Connection) -> crate::Result<()> { 69 | let response = self.ltrim(None).await?; 70 | debug!( 71 | LOGGER, 72 | "res, {} -> {}, {:?}", 73 | dst.local_addr(), 74 | dst.peer_addr(), 75 | response 76 | ); 77 | dst.write_frame(&response).await?; 78 | 79 | Ok(()) 80 | } 81 | 82 | pub async fn ltrim(&self, txn: Option>>) -> AsyncResult { 83 | if !self.valid { 84 | return Ok(resp_invalid_arguments()); 85 | } 86 | if is_use_txn_api() { 87 | ListCommandCtx::new(txn) 88 | .do_async_txnkv_ltrim(&self.key, self.start, self.end) 89 | .await 90 | } else { 91 | Ok(resp_err(REDIS_NOT_SUPPORTED_ERR)) 92 | } 93 | } 94 | } 95 | 96 | impl Invalid for Ltrim { 97 | fn new_invalid() -> Ltrim { 98 | Ltrim { 99 | key: "".to_owned(), 100 | start: 0, 101 | end: 0, 102 | valid: false, 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/cmd/mget.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use crate::cmd::Invalid; 4 | use crate::config::is_use_txn_api; 5 | use crate::config::LOGGER; 6 | use crate::tikv::errors::AsyncResult; 7 | use crate::tikv::string::StringCommandCtx; 8 | use crate::utils::resp_invalid_arguments; 9 | use crate::{Connection, Frame, Parse}; 10 | use bytes::Bytes; 11 | use slog::debug; 12 | use tikv_client::Transaction; 13 | use tokio::sync::Mutex; 14 | 15 | /// Get the value of key. 16 | /// 17 | /// If the key does not exist the special value nil is returned. An error is 18 | /// returned if the value stored at key is not a string, because GET only 19 | /// handles string values. 20 | #[derive(Debug, Clone)] 21 | pub struct Mget { 22 | /// Name of the keys to get 23 | keys: Vec, 24 | valid: bool, 25 | } 26 | 27 | impl Mget { 28 | /// Get the keys 29 | pub fn keys(&self) -> &Vec { 30 | &self.keys 31 | } 32 | 33 | pub fn add_key(&mut self, key: String) { 34 | self.keys.push(key); 35 | } 36 | 37 | pub(crate) fn parse_frames(parse: &mut Parse) -> crate::Result { 38 | // The `MGET` string has already been consumed. The next value is the 39 | // name of the key to get. If the next value is not a string or the 40 | // input is fully consumed, then an error is returned. 41 | let mut mget = Mget::default(); 42 | 43 | while let Ok(key) = parse.next_string() { 44 | mget.add_key(key); 45 | } 46 | 47 | Ok(mget) 48 | } 49 | 50 | pub(crate) fn parse_argv(argv: &Vec) -> crate::Result { 51 | if argv.is_empty() { 52 | return Ok(Mget::new_invalid()); 53 | } 54 | let mut mget = Mget::default(); 55 | for arg in argv { 56 | mget.add_key(String::from_utf8_lossy(arg).to_string()); 57 | } 58 | Ok(mget) 59 | } 60 | 61 | pub(crate) async fn apply(self, dst: &mut Connection) -> crate::Result<()> { 62 | let response = self.batch_get(None).await.unwrap_or_else(Into::into); 63 | 64 | debug!( 65 | LOGGER, 66 | "res, {} -> {}, {:?}", 67 | dst.local_addr(), 68 | dst.peer_addr(), 69 | response 70 | ); 71 | 72 | // Write the response back to the client 73 | dst.write_frame(&response).await?; 74 | 75 | Ok(()) 76 | } 77 | 78 | pub async fn batch_get(&self, txn: Option>>) -> AsyncResult { 79 | if !self.valid { 80 | return Ok(resp_invalid_arguments()); 81 | } 82 | if is_use_txn_api() { 83 | StringCommandCtx::new(txn) 84 | .do_async_txnkv_batch_get(&self.keys) 85 | .await 86 | } else { 87 | StringCommandCtx::new(txn) 88 | .do_async_rawkv_batch_get(&self.keys) 89 | .await 90 | } 91 | } 92 | } 93 | 94 | impl Default for Mget { 95 | /// Create a new `Mget` command which fetches `key` vector. 96 | fn default() -> Self { 97 | Mget { 98 | keys: vec![], 99 | valid: true, 100 | } 101 | } 102 | } 103 | 104 | impl Invalid for Mget { 105 | fn new_invalid() -> Mget { 106 | Mget { 107 | keys: vec![], 108 | valid: false, 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/cmd/mset.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use crate::config::is_use_txn_api; 4 | use crate::tikv::errors::AsyncResult; 5 | use crate::tikv::string::StringCommandCtx; 6 | use crate::tikv::KEY_ENCODER; 7 | use crate::utils::resp_invalid_arguments; 8 | use crate::{Connection, Frame, Parse}; 9 | use tikv_client::{KvPair, Transaction}; 10 | use tokio::sync::Mutex; 11 | 12 | use crate::cmd::Invalid; 13 | use crate::config::LOGGER; 14 | use bytes::Bytes; 15 | use slog::debug; 16 | 17 | #[derive(Debug, Clone)] 18 | pub struct Mset { 19 | keys: Vec, 20 | vals: Vec, 21 | valid: bool, 22 | } 23 | 24 | impl Mset { 25 | pub fn new_invalid() -> Mset { 26 | Mset { 27 | keys: vec![], 28 | vals: vec![], 29 | valid: false, 30 | } 31 | } 32 | 33 | /// Get the keys 34 | pub fn keys(&self) -> &Vec { 35 | &self.keys 36 | } 37 | 38 | pub fn vals(&self) -> &Vec { 39 | &self.vals 40 | } 41 | 42 | pub fn add_key(&mut self, key: String) { 43 | self.keys.push(key); 44 | } 45 | 46 | pub fn add_val(&mut self, val: Bytes) { 47 | self.vals.push(val); 48 | } 49 | 50 | pub(crate) fn parse_frames(parse: &mut Parse) -> crate::Result { 51 | let mut mset = Mset::default(); 52 | 53 | while let Ok(key) = parse.next_string() { 54 | mset.add_key(key); 55 | if let Ok(val) = parse.next_bytes() { 56 | mset.add_val(val); 57 | } else { 58 | return Err("protocol error".into()); 59 | } 60 | } 61 | 62 | Ok(mset) 63 | } 64 | 65 | pub(crate) fn parse_argv(argv: &Vec) -> crate::Result { 66 | if argv.len() % 2 != 0 { 67 | return Ok(Mset::new_invalid()); 68 | } 69 | let mut mset = Mset::default(); 70 | for idx in (0..argv.len()).step_by(2) { 71 | mset.add_key(String::from_utf8_lossy(&argv[idx]).to_string()); 72 | mset.add_val(argv[idx + 1].clone()); 73 | } 74 | Ok(mset) 75 | } 76 | 77 | pub(crate) async fn apply(self, dst: &mut Connection) -> crate::Result<()> { 78 | let response = self.batch_put(None).await.unwrap_or_else(Into::into); 79 | 80 | debug!( 81 | LOGGER, 82 | "res, {} -> {}, {:?}", 83 | dst.local_addr(), 84 | dst.peer_addr(), 85 | response 86 | ); 87 | 88 | // Write the response back to the client 89 | dst.write_frame(&response).await?; 90 | 91 | Ok(()) 92 | } 93 | 94 | pub async fn batch_put(&self, txn: Option>>) -> AsyncResult { 95 | if !self.valid { 96 | return Ok(resp_invalid_arguments()); 97 | } 98 | let mut kvs = Vec::new(); 99 | if is_use_txn_api() { 100 | for (idx, key) in self.keys.iter().enumerate() { 101 | let val = KEY_ENCODER.encode_txnkv_string_value(&mut self.vals[idx].to_vec(), 0); 102 | let ekey = KEY_ENCODER.encode_txnkv_string(key); 103 | let kvpair = KvPair::from((ekey, val.to_vec())); 104 | kvs.push(kvpair); 105 | } 106 | StringCommandCtx::new(txn) 107 | .do_async_txnkv_batch_put(kvs) 108 | .await 109 | } else { 110 | for (idx, key) in self.keys.iter().enumerate() { 111 | let val = &self.vals[idx]; 112 | let ekey = KEY_ENCODER.encode_rawkv_string(key); 113 | let kvpair = KvPair::from((ekey, val.to_vec())); 114 | kvs.push(kvpair); 115 | } 116 | StringCommandCtx::new(None) 117 | .do_async_rawkv_batch_put(kvs) 118 | .await 119 | } 120 | } 121 | } 122 | 123 | impl Default for Mset { 124 | /// Create a new `Mset` command which fetches `key` vector. 125 | fn default() -> Mset { 126 | Mset { 127 | keys: vec![], 128 | vals: vec![], 129 | valid: true, 130 | } 131 | } 132 | } 133 | 134 | impl Invalid for Mset { 135 | fn new_invalid() -> Mset { 136 | Mset { 137 | keys: vec![], 138 | vals: vec![], 139 | valid: false, 140 | } 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/cmd/persist.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use crate::cmd::Invalid; 4 | use crate::config::is_use_txn_api; 5 | use crate::config::LOGGER; 6 | use crate::tikv::errors::{AsyncResult, REDIS_NOT_SUPPORTED_ERR}; 7 | use crate::tikv::string::StringCommandCtx; 8 | use crate::utils::{resp_err, resp_invalid_arguments}; 9 | use crate::{Connection, Frame, Parse}; 10 | use bytes::Bytes; 11 | use slog::debug; 12 | use tikv_client::Transaction; 13 | use tokio::sync::Mutex; 14 | 15 | #[derive(Debug, Clone)] 16 | pub struct Persist { 17 | key: String, 18 | valid: bool, 19 | } 20 | 21 | impl Persist { 22 | pub fn new(key: impl ToString) -> Persist { 23 | Persist { 24 | key: key.to_string(), 25 | valid: true, 26 | } 27 | } 28 | 29 | /// Get the key 30 | pub fn key(&self) -> &str { 31 | &self.key 32 | } 33 | 34 | pub(crate) fn parse_frames(parse: &mut Parse) -> crate::Result { 35 | let key = parse.next_string()?; 36 | 37 | Ok(Persist { key, valid: true }) 38 | } 39 | 40 | pub(crate) fn parse_argv(argv: &Vec) -> crate::Result { 41 | if argv.len() != 1 { 42 | return Ok(Persist::new_invalid()); 43 | } 44 | Ok(Persist { 45 | key: String::from_utf8_lossy(&argv[0]).to_string(), 46 | valid: true, 47 | }) 48 | } 49 | 50 | pub(crate) async fn apply(self, dst: &mut Connection) -> crate::Result<()> { 51 | let response = self.persist(None).await.unwrap_or_else(Into::into); 52 | 53 | debug!( 54 | LOGGER, 55 | "res, {} -> {}, {:?}", 56 | dst.local_addr(), 57 | dst.peer_addr(), 58 | response 59 | ); 60 | 61 | dst.write_frame(&response).await?; 62 | 63 | Ok(()) 64 | } 65 | 66 | pub async fn persist(self, txn: Option>>) -> AsyncResult { 67 | if !self.valid { 68 | return Ok(resp_invalid_arguments()); 69 | } 70 | if is_use_txn_api() { 71 | StringCommandCtx::new(txn) 72 | .do_async_txnkv_expire(&self.key, 0) 73 | .await 74 | } else { 75 | Ok(resp_err(REDIS_NOT_SUPPORTED_ERR)) 76 | } 77 | } 78 | } 79 | 80 | impl Invalid for Persist { 81 | fn new_invalid() -> Persist { 82 | Persist { 83 | key: "".to_owned(), 84 | valid: false, 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/cmd/ping.rs: -------------------------------------------------------------------------------- 1 | use crate::cmd::Invalid; 2 | use crate::config::LOGGER; 3 | use crate::utils::resp_invalid_arguments; 4 | use crate::{Connection, Frame, Parse, ParseError}; 5 | use bytes::Bytes; 6 | use slog::debug; 7 | 8 | /// Returns PONG if no argument is provided, otherwise 9 | /// return a copy of the argument as a bulk. 10 | /// 11 | /// This command is often used to test if a connection 12 | /// is still alive, or to measure latency. 13 | #[derive(Debug, Clone)] 14 | pub struct Ping { 15 | /// optional message to be returned 16 | msg: Option, 17 | valid: bool, 18 | } 19 | 20 | impl Ping { 21 | /// Create a new `Ping` command with optional `msg`. 22 | pub fn new(msg: Option) -> Ping { 23 | Ping { msg, valid: true } 24 | } 25 | 26 | /// Parse a `Ping` instance from a received frame. 27 | /// 28 | /// The `Parse` argument provides a cursor-like API to read fields from the 29 | /// `Frame`. At this point, the entire frame has already been received from 30 | /// the socket. 31 | /// 32 | /// The `PING` string has already been consumed. 33 | /// 34 | /// # Returns 35 | /// 36 | /// Returns the `Ping` value on success. If the frame is malformed, `Err` is 37 | /// returned. 38 | /// 39 | /// # Format 40 | /// 41 | /// Expects an array frame containing `PING` and an optional message. 42 | /// 43 | /// ```text 44 | /// PING [message] 45 | /// ``` 46 | pub(crate) fn parse_frames(parse: &mut Parse) -> crate::Result { 47 | match parse.next_string() { 48 | Ok(msg) => Ok(Ping::new(Some(msg))), 49 | Err(ParseError::EndOfStream) => Ok(Ping::default()), 50 | Err(e) => Err(e.into()), 51 | } 52 | } 53 | 54 | /// Apply the `Ping` command and return the message. 55 | /// 56 | /// The response is written to `dst`. This is called by the server in order 57 | /// to execute a received command. 58 | pub(crate) async fn apply(self, dst: &mut Connection) -> crate::Result<()> { 59 | if !self.valid { 60 | dst.write_frame(&resp_invalid_arguments()).await?; 61 | return Ok(()); 62 | } 63 | 64 | let response = match self.msg { 65 | None => Frame::Simple("PONG".to_string()), 66 | Some(msg) => Frame::Bulk(Bytes::from(msg)), 67 | }; 68 | 69 | debug!( 70 | LOGGER, 71 | "res, {} -> {}, {:?}", 72 | dst.local_addr(), 73 | dst.peer_addr(), 74 | response 75 | ); 76 | 77 | // Write the response back to the client 78 | dst.write_frame(&response).await?; 79 | 80 | Ok(()) 81 | } 82 | } 83 | 84 | impl Default for Ping { 85 | fn default() -> Self { 86 | Ping { 87 | msg: None, 88 | valid: true, 89 | } 90 | } 91 | } 92 | 93 | impl Invalid for Ping { 94 | fn new_invalid() -> Ping { 95 | Ping { 96 | msg: None, 97 | valid: false, 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/cmd/pop.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use crate::cmd::{Invalid, Parse}; 4 | use crate::config::is_use_txn_api; 5 | use crate::tikv::errors::{AsyncResult, REDIS_NOT_SUPPORTED_ERR}; 6 | use crate::tikv::list::ListCommandCtx; 7 | use crate::utils::{resp_err, resp_invalid_arguments}; 8 | use crate::{Connection, Frame}; 9 | 10 | use crate::config::LOGGER; 11 | use bytes::Bytes; 12 | use slog::debug; 13 | use tikv_client::Transaction; 14 | use tokio::sync::Mutex; 15 | 16 | #[derive(Debug, Clone)] 17 | pub struct Pop { 18 | key: String, 19 | count: i64, 20 | valid: bool, 21 | } 22 | 23 | impl Pop { 24 | pub fn new(key: &str, count: i64) -> Pop { 25 | Pop { 26 | key: key.to_owned(), 27 | count, 28 | valid: true, 29 | } 30 | } 31 | 32 | pub fn key(&self) -> &str { 33 | &self.key 34 | } 35 | 36 | pub(crate) fn parse_argv(argv: &Vec) -> crate::Result { 37 | if argv.is_empty() || argv.len() > 2 { 38 | return Ok(Pop::new_invalid()); 39 | } 40 | let key = &String::from_utf8_lossy(&argv[0]); 41 | let mut count = 1; 42 | if argv.len() == 2 { 43 | match String::from_utf8_lossy(&argv[1]).parse::() { 44 | Ok(v) => count = v, 45 | Err(_) => { 46 | return Ok(Pop::new_invalid()); 47 | } 48 | } 49 | } 50 | Ok(Pop::new(key, count)) 51 | } 52 | 53 | pub(crate) fn parse_frames(parse: &mut Parse) -> crate::Result { 54 | let key = parse.next_string()?; 55 | let mut count = 1; 56 | 57 | if let Ok(n) = parse.next_int() { 58 | count = n; 59 | } 60 | 61 | let pop = Pop::new(&key, count); 62 | 63 | Ok(pop) 64 | } 65 | 66 | pub(crate) async fn apply(self, dst: &mut Connection, op_left: bool) -> crate::Result<()> { 67 | let response = self.pop(None, op_left).await?; 68 | debug!( 69 | LOGGER, 70 | "res, {} -> {}, {:?}", 71 | dst.local_addr(), 72 | dst.peer_addr(), 73 | response 74 | ); 75 | dst.write_frame(&response).await?; 76 | 77 | Ok(()) 78 | } 79 | 80 | pub async fn pop( 81 | &self, 82 | txn: Option>>, 83 | op_left: bool, 84 | ) -> AsyncResult { 85 | if !self.valid { 86 | return Ok(resp_invalid_arguments()); 87 | } 88 | if is_use_txn_api() { 89 | ListCommandCtx::new(txn) 90 | .do_async_txnkv_pop(&self.key, op_left, self.count) 91 | .await 92 | } else { 93 | Ok(resp_err(REDIS_NOT_SUPPORTED_ERR)) 94 | } 95 | } 96 | } 97 | 98 | impl Invalid for Pop { 99 | fn new_invalid() -> Pop { 100 | Pop { 101 | key: "".to_owned(), 102 | count: 0, 103 | valid: false, 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/cmd/publish.rs: -------------------------------------------------------------------------------- 1 | use crate::{Connection, Db, Frame, Parse}; 2 | 3 | use bytes::Bytes; 4 | 5 | use super::Invalid; 6 | use crate::utils::resp_invalid_arguments; 7 | 8 | /// Posts a message to the given channel. 9 | /// 10 | /// Send a message into a channel without any knowledge of individual consumers. 11 | /// Consumers may subscribe to channels in order to receive the messages. 12 | /// 13 | /// Channel names have no relation to the key-value namespace. Publishing on a 14 | /// channel named "foo" has no relation to setting the "foo" key. 15 | #[derive(Debug, Clone)] 16 | pub struct Publish { 17 | /// Name of the channel on which the message should be published. 18 | channel: String, 19 | 20 | /// The message to publish. 21 | message: Bytes, 22 | valid: bool, 23 | } 24 | 25 | impl Publish { 26 | #[allow(dead_code)] 27 | /// Create a new `Publish` command which sends `message` on `channel`. 28 | pub(crate) fn new(channel: impl ToString, message: Bytes) -> Publish { 29 | Publish { 30 | channel: channel.to_string(), 31 | message, 32 | valid: true, 33 | } 34 | } 35 | 36 | /// Parse a `Publish` instance from a received frame. 37 | /// 38 | /// The `Parse` argument provides a cursor-like API to read fields from the 39 | /// `Frame`. At this point, the entire frame has already been received from 40 | /// the socket. 41 | /// 42 | /// The `PUBLISH` string has already been consumed. 43 | /// 44 | /// # Returns 45 | /// 46 | /// On success, the `Publish` value is returned. If the frame is malformed, 47 | /// `Err` is returned. 48 | /// 49 | /// # Format 50 | /// 51 | /// Expects an array frame containing three entries. 52 | /// 53 | /// ```text 54 | /// PUBLISH channel message 55 | /// ``` 56 | pub(crate) fn parse_frames(parse: &mut Parse) -> crate::Result { 57 | // The `PUBLISH` string has already been consumed. Extract the `channel` 58 | // and `message` values from the frame. 59 | // 60 | // The `channel` must be a valid string. 61 | let channel = parse.next_string()?; 62 | 63 | // The `message` is arbitrary bytes. 64 | let message = parse.next_bytes()?; 65 | 66 | Ok(Publish { 67 | channel, 68 | message, 69 | valid: true, 70 | }) 71 | } 72 | 73 | /// Apply the `Publish` command to the specified `Db` instance. 74 | /// 75 | /// The response is written to `dst`. This is called by the server in order 76 | /// to execute a received command. 77 | pub(crate) async fn apply(self, db: &Db, dst: &mut Connection) -> crate::Result<()> { 78 | if !self.valid { 79 | dst.write_frame(&resp_invalid_arguments()).await?; 80 | return Ok(()); 81 | } 82 | 83 | // The shared state contains the `tokio::sync::broadcast::Sender` for 84 | // all active channels. Calling `db.publish` dispatches the message into 85 | // the appropriate channel. 86 | // 87 | // The number of subscribers currently listening on the channel is 88 | // returned. This does not mean that `num_subscriber` channels will 89 | // receive the message. Subscribers may drop before receiving the 90 | // message. Given this, `num_subscribers` should only be used as a 91 | // "hint". 92 | let num_subscribers = db.publish(&self.channel, self.message); 93 | 94 | // The number of subscribers is returned as the response to the publish 95 | // request. 96 | let response = Frame::Integer(num_subscribers as i64); 97 | 98 | // Write the frame to the client. 99 | dst.write_frame(&response).await?; 100 | 101 | Ok(()) 102 | } 103 | 104 | #[allow(dead_code)] 105 | /// Converts the command into an equivalent `Frame`. 106 | /// 107 | /// This is called by the client when encoding a `Publish` command to send 108 | /// to the server. 109 | pub(crate) fn into_frame(self) -> Frame { 110 | let mut frame = Frame::array(); 111 | frame.push_bulk(Bytes::from("publish".as_bytes())); 112 | frame.push_bulk(Bytes::from(self.channel.into_bytes())); 113 | frame.push_bulk(self.message); 114 | 115 | frame 116 | } 117 | } 118 | 119 | impl Invalid for Publish { 120 | fn new_invalid() -> Self { 121 | Publish { 122 | channel: "".to_string(), 123 | message: Bytes::from(""), 124 | valid: false, 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/cmd/push.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use crate::cmd::{Invalid, Parse}; 4 | use crate::config::is_use_txn_api; 5 | use crate::tikv::errors::{AsyncResult, REDIS_NOT_SUPPORTED_ERR}; 6 | use crate::tikv::list::ListCommandCtx; 7 | use crate::utils::{resp_err, resp_invalid_arguments}; 8 | use crate::{Connection, Frame}; 9 | 10 | use crate::config::LOGGER; 11 | use bytes::Bytes; 12 | use slog::debug; 13 | use tikv_client::Transaction; 14 | use tokio::sync::Mutex; 15 | 16 | #[derive(Debug, Clone)] 17 | pub struct Push { 18 | key: String, 19 | items: Vec, 20 | valid: bool, 21 | } 22 | 23 | impl Push { 24 | pub fn new(key: &str) -> Push { 25 | Push { 26 | items: vec![], 27 | key: key.to_owned(), 28 | valid: true, 29 | } 30 | } 31 | 32 | pub fn key(&self) -> &str { 33 | &self.key 34 | } 35 | 36 | pub fn items(&self) -> &Vec { 37 | &self.items 38 | } 39 | 40 | pub fn add_item(&mut self, item: Bytes) { 41 | self.items.push(item); 42 | } 43 | 44 | pub(crate) fn parse_frames(parse: &mut Parse) -> crate::Result { 45 | let key = parse.next_string()?; 46 | let mut push = Push::new(&key); 47 | 48 | while let Ok(item) = parse.next_bytes() { 49 | push.add_item(item); 50 | } 51 | 52 | Ok(push) 53 | } 54 | 55 | pub(crate) fn parse_argv(argv: &Vec) -> crate::Result { 56 | if argv.len() < 2 { 57 | return Ok(Push::new_invalid()); 58 | } 59 | let mut push = Push::new(&String::from_utf8_lossy(&argv[0])); 60 | 61 | for arg in &argv[1..] { 62 | push.add_item(arg.to_owned()); 63 | } 64 | 65 | Ok(push) 66 | } 67 | 68 | pub(crate) async fn apply(self, dst: &mut Connection, op_left: bool) -> crate::Result<()> { 69 | let response = self.push(None, op_left).await?; 70 | debug!( 71 | LOGGER, 72 | "res, {} -> {}, {:?}", 73 | dst.local_addr(), 74 | dst.peer_addr(), 75 | response 76 | ); 77 | dst.write_frame(&response).await?; 78 | 79 | Ok(()) 80 | } 81 | 82 | pub async fn push( 83 | &self, 84 | txn: Option>>, 85 | op_left: bool, 86 | ) -> AsyncResult { 87 | if !self.valid { 88 | return Ok(resp_invalid_arguments()); 89 | } 90 | if is_use_txn_api() { 91 | ListCommandCtx::new(txn) 92 | .do_async_txnkv_push(&self.key, &self.items, op_left) 93 | .await 94 | } else { 95 | Ok(resp_err(REDIS_NOT_SUPPORTED_ERR)) 96 | } 97 | } 98 | } 99 | 100 | impl Invalid for Push { 101 | fn new_invalid() -> Push { 102 | Push { 103 | items: vec![], 104 | key: "".to_owned(), 105 | valid: false, 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/cmd/sadd.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use crate::cmd::{Invalid, Parse}; 4 | use crate::config::is_use_txn_api; 5 | use crate::tikv::errors::{AsyncResult, REDIS_NOT_SUPPORTED_ERR}; 6 | use crate::tikv::set::SetCommandCtx; 7 | use crate::utils::{resp_err, resp_invalid_arguments}; 8 | use crate::{Connection, Frame}; 9 | 10 | use crate::config::LOGGER; 11 | use bytes::Bytes; 12 | use slog::debug; 13 | use tikv_client::Transaction; 14 | use tokio::sync::Mutex; 15 | 16 | #[derive(Debug, Clone)] 17 | pub struct Sadd { 18 | key: String, 19 | members: Vec, 20 | valid: bool, 21 | } 22 | 23 | impl Sadd { 24 | pub fn new(key: &str) -> Sadd { 25 | Sadd { 26 | key: key.to_string(), 27 | members: vec![], 28 | valid: true, 29 | } 30 | } 31 | 32 | /// Get the key 33 | pub fn key(&self) -> &str { 34 | &self.key 35 | } 36 | 37 | pub fn set_key(&mut self, key: &str) { 38 | self.key = key.to_owned(); 39 | } 40 | 41 | pub fn add_member(&mut self, member: &str) { 42 | self.members.push(member.to_string()); 43 | } 44 | 45 | pub(crate) fn parse_frames(parse: &mut Parse) -> crate::Result { 46 | let key = parse.next_string()?; 47 | let mut sadd = Sadd::new(&key); 48 | while let Ok(member) = parse.next_string() { 49 | sadd.add_member(&member); 50 | } 51 | Ok(sadd) 52 | } 53 | 54 | pub(crate) fn parse_argv(argv: &Vec) -> crate::Result { 55 | if argv.len() < 2 { 56 | return Ok(Sadd::new_invalid()); 57 | } 58 | let key = &String::from_utf8_lossy(&argv[0]); 59 | let mut sadd = Sadd::new(key); 60 | for arg in &argv[1..] { 61 | sadd.add_member(&String::from_utf8_lossy(arg)); 62 | } 63 | Ok(sadd) 64 | } 65 | 66 | pub(crate) async fn apply(self, dst: &mut Connection) -> crate::Result<()> { 67 | let response = self.sadd(None).await?; 68 | debug!( 69 | LOGGER, 70 | "res, {} -> {}, {:?}", 71 | dst.local_addr(), 72 | dst.peer_addr(), 73 | response 74 | ); 75 | dst.write_frame(&response).await?; 76 | 77 | Ok(()) 78 | } 79 | 80 | pub async fn sadd(&self, txn: Option>>) -> AsyncResult { 81 | if !self.valid { 82 | return Ok(resp_invalid_arguments()); 83 | } 84 | if is_use_txn_api() { 85 | SetCommandCtx::new(txn) 86 | .do_async_txnkv_sadd(&self.key, &self.members) 87 | .await 88 | } else { 89 | Ok(resp_err(REDIS_NOT_SUPPORTED_ERR)) 90 | } 91 | } 92 | } 93 | 94 | impl Invalid for Sadd { 95 | fn new_invalid() -> Sadd { 96 | Sadd { 97 | key: "".to_string(), 98 | members: vec![], 99 | valid: false, 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/cmd/scan.rs: -------------------------------------------------------------------------------- 1 | use crate::cmd::{Invalid, Parse}; 2 | use crate::config::is_use_txn_api; 3 | use crate::tikv::errors::{AsyncResult, REDIS_NOT_SUPPORTED_ERR}; 4 | use crate::tikv::string::StringCommandCtx; 5 | use crate::utils::{resp_err, resp_invalid_arguments}; 6 | use crate::{Connection, Frame}; 7 | use bytes::Bytes; 8 | use slog::debug; 9 | use std::convert::TryInto; 10 | use std::sync::Arc; 11 | use tikv_client::Transaction; 12 | use tokio::sync::Mutex; 13 | 14 | use crate::config::LOGGER; 15 | 16 | #[derive(Debug, Clone)] 17 | pub struct Scan { 18 | start: String, 19 | count: i64, 20 | regex: String, 21 | valid: bool, 22 | } 23 | 24 | impl Scan { 25 | pub fn new(start: String, count: i64, regex: String) -> Scan { 26 | Scan { 27 | start, 28 | count, 29 | regex, 30 | valid: true, 31 | } 32 | } 33 | 34 | pub fn valid(&self) -> bool { 35 | self.valid 36 | } 37 | 38 | pub(crate) fn parse_frames(parse: &mut Parse) -> crate::Result { 39 | let start = parse.next_string()?; 40 | let mut count = 10; 41 | let mut regex = ".*?".to_owned(); 42 | while let Ok(flag) = parse.next_string() { 43 | if flag.to_uppercase().as_str() == "COUNT" { 44 | if let Ok(c) = parse.next_int() { 45 | count = c; 46 | }; 47 | } else if flag.to_uppercase().as_str() == "MATCH" { 48 | regex = parse.next_string()?; 49 | } 50 | } 51 | 52 | Ok(Scan { 53 | start, 54 | count, 55 | regex, 56 | valid: true, 57 | }) 58 | } 59 | 60 | pub(crate) fn parse_argv(argv: &Vec) -> crate::Result { 61 | if argv.is_empty() || argv.len() > 5 { 62 | return Ok(Scan::new_invalid()); 63 | } 64 | 65 | let mut count = 10; 66 | let mut regex = ".*?".to_owned(); 67 | let start = String::from_utf8_lossy(&argv[0]); 68 | if argv.len() >= 3 { 69 | if argv[1].to_ascii_uppercase() == b"COUNT" { 70 | if let Ok(c) = String::from_utf8_lossy(&argv[2]).parse::() { 71 | count = c; 72 | } else { 73 | return Ok(Scan::new_invalid()); 74 | } 75 | } else if argv[1].to_ascii_uppercase() == b"MATCH" { 76 | regex = String::from_utf8_lossy(&argv[2]).to_string(); 77 | } else { 78 | return Ok(Scan::new_invalid()); 79 | } 80 | if argv.len() == 5 { 81 | if argv[3].to_ascii_uppercase() == b"COUNT" { 82 | if let Ok(c) = String::from_utf8_lossy(&argv[4]).parse::() { 83 | count = c; 84 | } else { 85 | return Ok(Scan::new_invalid()); 86 | } 87 | } else if argv[3].to_ascii_uppercase() == b"MATCH" { 88 | regex = String::from_utf8_lossy(&argv[4]).to_string(); 89 | } else { 90 | return Ok(Scan::new_invalid()); 91 | } 92 | } 93 | } 94 | 95 | Ok(Scan { 96 | start: start.to_string(), 97 | count, 98 | regex, 99 | valid: true, 100 | }) 101 | } 102 | 103 | pub(crate) async fn apply(self, dst: &mut Connection) -> crate::Result<()> { 104 | let response = self.scan(None).await?; 105 | debug!( 106 | LOGGER, 107 | "res, {} -> {}, {:?}", 108 | dst.local_addr(), 109 | dst.peer_addr(), 110 | response 111 | ); 112 | dst.write_frame(&response).await?; 113 | 114 | Ok(()) 115 | } 116 | 117 | pub async fn scan(&self, txn: Option>>) -> AsyncResult { 118 | if !self.valid { 119 | return Ok(resp_invalid_arguments()); 120 | } 121 | if is_use_txn_api() { 122 | StringCommandCtx::new(txn) 123 | .do_async_txnkv_scan(&self.start, self.count.try_into().unwrap(), &self.regex) 124 | .await 125 | } else { 126 | Ok(resp_err(REDIS_NOT_SUPPORTED_ERR)) 127 | } 128 | } 129 | } 130 | 131 | impl Invalid for Scan { 132 | fn new_invalid() -> Scan { 133 | Scan { 134 | start: "".to_owned(), 135 | count: 0, 136 | regex: "".to_owned(), 137 | valid: false, 138 | } 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/cmd/scard.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use crate::cmd::{Invalid, Parse}; 4 | use crate::config::is_use_txn_api; 5 | use crate::tikv::errors::{AsyncResult, REDIS_NOT_SUPPORTED_ERR}; 6 | use crate::tikv::set::SetCommandCtx; 7 | use crate::utils::{resp_err, resp_invalid_arguments}; 8 | use crate::{Connection, Frame}; 9 | 10 | use crate::config::LOGGER; 11 | use bytes::Bytes; 12 | use slog::debug; 13 | use tikv_client::Transaction; 14 | use tokio::sync::Mutex; 15 | 16 | #[derive(Debug, Clone)] 17 | pub struct Scard { 18 | key: String, 19 | valid: bool, 20 | } 21 | 22 | impl Scard { 23 | pub fn new(key: &str) -> Scard { 24 | Scard { 25 | key: key.to_string(), 26 | valid: true, 27 | } 28 | } 29 | 30 | /// Get the key 31 | pub fn key(&self) -> &str { 32 | &self.key 33 | } 34 | 35 | pub fn set_key(&mut self, key: &str) { 36 | self.key = key.to_owned(); 37 | } 38 | 39 | pub(crate) fn parse_frames(parse: &mut Parse) -> crate::Result { 40 | let key = parse.next_string()?; 41 | Ok(Scard::new(&key)) 42 | } 43 | 44 | pub(crate) fn parse_argv(argv: &Vec) -> crate::Result { 45 | if argv.len() != 1 { 46 | return Ok(Scard::new_invalid()); 47 | } 48 | Ok(Scard::new(&String::from_utf8_lossy(&argv[0]))) 49 | } 50 | 51 | pub(crate) async fn apply(self, dst: &mut Connection) -> crate::Result<()> { 52 | let response = self.scard(None).await?; 53 | debug!( 54 | LOGGER, 55 | "res, {} -> {}, {:?}", 56 | dst.local_addr(), 57 | dst.peer_addr(), 58 | response 59 | ); 60 | dst.write_frame(&response).await?; 61 | 62 | Ok(()) 63 | } 64 | 65 | pub async fn scard(&self, txn: Option>>) -> AsyncResult { 66 | if !self.valid { 67 | return Ok(resp_invalid_arguments()); 68 | } 69 | if is_use_txn_api() { 70 | SetCommandCtx::new(txn) 71 | .do_async_txnkv_scard(&self.key) 72 | .await 73 | } else { 74 | Ok(resp_err(REDIS_NOT_SUPPORTED_ERR)) 75 | } 76 | } 77 | } 78 | 79 | impl Invalid for Scard { 80 | fn new_invalid() -> Scard { 81 | Scard { 82 | key: "".to_owned(), 83 | valid: false, 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/cmd/script.rs: -------------------------------------------------------------------------------- 1 | use std::sync::atomic::{AtomicBool, Ordering}; 2 | 3 | use crate::cmd::Invalid; 4 | use crate::config::LOGGER; 5 | use crate::db::Db; 6 | use crate::tikv::errors::AsyncResult; 7 | use crate::utils::{resp_array, resp_bulk, resp_int, resp_invalid_arguments, resp_ok, sha1hex}; 8 | use crate::{Connection, Frame, Parse}; 9 | use bytes::Bytes; 10 | use slog::debug; 11 | 12 | static SCRIPT_KILLED: AtomicBool = AtomicBool::new(false); 13 | 14 | pub fn script_interuptted() -> bool { 15 | SCRIPT_KILLED.load(Ordering::Relaxed) 16 | } 17 | 18 | pub fn script_set_killed() { 19 | SCRIPT_KILLED.store(true, Ordering::Relaxed) 20 | } 21 | 22 | pub fn script_clear_killed() { 23 | SCRIPT_KILLED.store(false, Ordering::Relaxed) 24 | } 25 | 26 | #[derive(Debug, Clone)] 27 | pub struct Script { 28 | script: String, 29 | is_load: bool, 30 | sha1_vec: Vec, 31 | is_exists: bool, 32 | is_flush: bool, 33 | is_kill: bool, 34 | valid: bool, 35 | } 36 | 37 | impl Script { 38 | pub fn new(subcommand: &str) -> Script { 39 | let mut is_load = false; 40 | let mut is_exists = false; 41 | let mut is_flush = false; 42 | let mut is_kill = false; 43 | match subcommand.to_uppercase().as_str() { 44 | "LOAD" => { 45 | is_load = true; 46 | } 47 | "EXISTS" => { 48 | is_exists = true; 49 | } 50 | "FLUSH" => { 51 | is_flush = true; 52 | } 53 | "KILL" => { 54 | is_kill = true; 55 | } 56 | _ => {} 57 | } 58 | Script { 59 | script: "".to_owned(), 60 | is_load, 61 | sha1_vec: vec![], 62 | is_exists, 63 | is_flush, 64 | is_kill, 65 | valid: true, 66 | } 67 | } 68 | 69 | pub fn set_script(&mut self, script: &str) { 70 | self.script = script.to_owned(); 71 | } 72 | 73 | pub fn add_sha1(&mut self, sha1: &str) { 74 | self.sha1_vec.push(sha1.to_owned()); 75 | } 76 | 77 | pub(crate) fn parse_frames(parse: &mut Parse) -> crate::Result