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-oxide 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/dpbriggs/redis-oxide
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-oxide.
If you wish to download and run it yourself, you can do the following
~ git clone https://github.com/dpbriggs/redis-oxide ~ cd redis-oxide ~ 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
We should solidify the above before working on the more complex bits, but contributions are welcome :)
- [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, please don't be a jerk.
Contact: Make an issue or PR against this repo, or send an email to david@dpbriggs.ca. 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-oxide 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-oxide.
- 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 ()
