A multi-threaded implementation of redis written in rust 🦀.
This project is intended to be a drop-in replacement for redis. It’s under construction at the moment.
redis-proto is a black-box multi-threaded re-implementation of redis, backed by tokio.
It features data-structure key-space/lock granularity, written entirely in safe rust.
It’s currently protocol compatible with redis, so you should be able to test it out with your favourite tools.
The multi-threaded nature has advantages and disadvantages.
On one hand, KEYS * isn’t particularly crippling for the server as it’ll just keep a thread busy.
On the other hand, there’s some lock-juggling overhead, especially for writes, which messes with tokio.
There’s currently no official release for the project. You can compile and install it yourself with the following command:
cargo install --git https://github.com/obaraelijah/redis-proto
Note: This project requires the rust nightly. You can use rustup to install it.
Once it compiles you should be able to run it with ~ redis-proto.
If you wish to download and run it yourself, you can do the following
~ git clone https://github.com/obaraelijah/redis-proto ~ cd redis-proto ~ cargo run
Then use your favorite redis client. Eg. redis-cli:
~ redis-cli 127.0.0.1:6379> set foo bar OK 127.0.0.1:6379> get foo "bar"
Or using the redis library for python:
import redis
from pprint import pprint
r = redis.Redis()
r.set('foobar', 'foobar')
pprint(r.get('foobar'))
for i in range(100):
r.rpush('list', i)
list_res = r.lrange('list', 0, -1)
pprint(list_res[0:3])
pprint(sum(map(int, list_res)))
total = 0
for i in range(100):
total += int(r.lpop('list'))
pprint(total)Which will print:
b'foobar'
[b'0', b'1', b'2']
4950
4950- [X] Keys
- [X] Sets
- [X] Lists
- [X] Hashes
- [ ] HyperLogLog
- [ ] Geo
- [-] Sorted Sets
- [X] Basic Functionality
- [ ] Still need some operations
- [ ] Strings
- [X] Resp / server
- [ ] Database compatibility
- [ ] Unsure if this is a good thing – may be better to port existing dumps.
- [ ] Blocking / Concurrent Ops (ttl/save-on-x-ops)
- [ ] CLI / config compatibility
- [ ] Authentication
Conduct: =Have fun and be respectful. =
Contact: Make an issue or PR against this repo, or send an email to [email protected]. If you know of a better forum, please suggest it!
NOTE: DO NOT USE THE REDIS SOURCE CODE IN ANY WAY!
This project is under active development, so things are a little messy.
The general design of redis-proto is:
- A Command (
set foo bar) is read off the socket and passed to the translate function insrc/ops.rs.- The parser generates a
RedisValue, which is the lingua franca ofredis-proto.
- The parser generates a
- This gets converted to an
Ops::XYZ(XYZOps::Foobar(..))enum object, which is consumed by theop_interactfunction.- A macro is used to provide automate this.
- This operation is executed against the global
Stateobject (using theop_interactfunction)- This will return an
ReturnValuetype, which is a more convenient form ofRedisValue. - This
ReturnValueis converted and sent back to the client.
- This will return an
Therefore, if you want to do something like implement hashes, you will need to:
- Add a new struct member in
State.- You first define the type:
type KeyHash = DashMap<Key, HashMap<Key, Value>> - Then add it to State:
pub hashes: KeyHash
- You first define the type:
- Define a new file for your data type,
src/hashes.rs.- Keep your type definitions in
src/types.rs!
- Keep your type definitions in
- Create an enum to track your commands,
op_variants! { HashOps, HGet(Key, Key), HSet(Key, Key, Value) } - Implement parsing for your enum in
src/ops.rs.- You should be able to follow the existing parsing infrastructure. Should just be extra entries in
translate_arrayinsrc/ops.rs. - You will need to add your return type to the
ok!macro. Just copy/paste an existing line. - You should return something like
ok!(HashOps::HSet(x, y, z)). - A stretch goal is to automate parsing.
- You should be able to follow the existing parsing infrastructure. Should just be extra entries in
- Implement a
async *_interactfor your type; I would follow existing implementations (eg.src/keys.rs).- I would keep the redis docs open, and play around with the commands in the web console (or wherever) to determine behavior.
- Add a new match entry in the
async op_interactfunction insrc/ops.rs.
- Test it! (follow existing testing bits; eg.
src/keys.rs). - Please add the commands to the list below.
- If you’re using emacs, just fire up the server and evaluate the babel block below (see
README.orgsource) - Alternatively, copy the script into a terminal and copy/paste the output below. (see raw
README.org)
- If you’re using emacs, just fire up the server and evaluate the babel block below (see
Set (Key, Value)MSet (RVec<(Key, Value)>)Get (Key)MGet (RVec<Key>)Del (RVec<Key>)Rename (Key, Key)RenameNx (Key, Key)
LIndex (Key, Index)LLen (Key)LPop (Key)LPush (Key, RVec<Value>)LPushX (Key, Value)LRange (Key, Index, Index)LSet (Key, Index, Value)LTrim (Key, Index, Index)RPop (Key)RPush (Key, RVec<Value>)RPushX (Key, Value)RPopLPush (Key, Key)BLPop (Key, UTimeout)BRPop (Key, UTimeout)
HGet (Key, Key)HSet (Key, Key, Value)HExists (Key, Key)HGetAll (Key)HMGet (Key, RVec<Key>)HKeys (Key)HMSet (Key, RVec<(Key, Value)>)HIncrBy (Key, Key, Count)HLen (Key)HDel (Key, RVec<Key>)HVals (Key)HStrLen (Key, Key)HSetNX (Key, Key, Value)
SAdd (Key, RVec<Value>)SCard (Key)SDiff (RVec<Value>)SDiffStore (Key, RVec<Value>)SInter (RVec<Value>)SInterStore (Key, RVec<Value>)SIsMember (Key, Value)SMembers (Key)SMove (Key, Key, Value)SPop (Key, Option<Count>)SRandMembers (Key, Option<Count>)SRem (Key, RVec<Value>)SUnion (RVec<Value>)SUnionStore (Key, RVec<Value>)
ZAdd (Key, RVec<(Score, Key)>)ZRem (Key, RVec<Key>)ZRange (Key, Score, Score)ZCard (Key)ZScore (Key, Key)ZPopMax (Key, Count)ZPopMin (Key, Count)ZRank (Key, Key)
BInsert (Key, Value)BContains (Key, Value)
STPush (Key, Value)STPop (Key)STPeek (Key)STSize (Key)
PfAdd (Key, RVec<Value>)PfCount (RVec<Key>)PfMerge (Key, RVec<Key>)
Keys ()Exists (Vec<Key>)Pong ()FlushAll ()FlushDB ()Echo (Value)PrintCmds ()Select (Index)Script (Value)EmbeddedScript (Value, Vec<RedisValueRef>)Info ()
