a simple KV store written in clojure while I was interveiwing at trade ghana.
KWIK supports three data structures strings, lists and maps. It speaks http so it's very easy to use. All you need to interact with it is an http client. The client issues a command to the server and it gets a response back in json.
-
Every kwik response is json compatible. The response could either be a json object or a json primitive.
-
The protocol is a minimal semantic built on top http making it easy to use.
-
All commands take at least one argument which is specified as an extra path to the command. eg /v1/GET/arg1[?args=extra-aguments]. Any extra argument is specified in the arg query. The extra arguments are fed to the command handlers in the argV vector.
-
The kwik database itself is a map but the values are wrapped in a value object and stored with the type information. This way, commands can detect unsupported data types and err gracefully.
-
In code, all command handers return a tuple indicating a success and an error. No exception is deliberately thrown, this makes the code easy to reason about
-
The kwik server returns http 4xx error code when a command errs.
-
Finally to add a new command, all one has to do is to define a handler function and add it as an entry to the command-table. Kwik server will pick it up when a client issues that command
-
Since the protocol runs on top http, which is text based, values like
nil
cannot be represented so when a mapping does not exist, an error is returned so clients can handle it gracefully -
kwik is in-memory so a server restart will lead to data loss.
-
All extra arguments passed to kwik is comma delimited, this means a command cannot process any argument that contains a comma.
-
All arguments passed to command must be url-encoded by clients if they contain contain-url unsafe characters.
-
Due to the fact that clojure data structures are immutable, any command that changes the database will lead to a new copy of the db. For a large dataset, this can be prohibitively expensive.
To run kwik, you need to have the lein tool installed on your system. In the project directory run:
lein run [port]
By default kwik listens on port 8000 but you can supply a custom port.
Assuming the server is running locally on port 8000,
curl http://localhost:8000/v1/[command]/[arg1]?args=[extra arguments]
where command: Any supported command, command names are case insensitive. arg1: The first argument to the command, often times it's the key and is case sensitive extra-arguments: Any other arguments expected by the command.
There's no limitation on the length of keys and values beyond the length permited by the http specs.
Errors All commands return a kwik error on failure and a 4xx http status code. A kwik error has two fields, an error code and a message
Valid error codes are:
- 100 - key does not exist
- 101 - command does not support the type of value held by key
- 102 - Incorrect number of arguments
- 103 - specified command is unknown
- 104 - attempt to retrieve a value from an empty list
- 300 - An unknwon error occured
Kwik command grouped by their respective data structures
- GET
- Returs the string value mapped to a given key.
- example:
curl http://localhost:8000/v1/get/key
- SET
- Sets the specified value to a given key
- returns "OK" on success
- example:
curl http://localhost:8000/v1/set/key?args=value
-
LGET [key]
- Returns the list value stored inside a given key
- example:
curl http://localhost:8000/v1/lget/key
-
LSET [key "value1,value2,value3"]
- Sets a a list of comma-separated values to a given key as list
- returns the length of the list stored at the given key.
- example:
curl http://localhost:8000/v1/lset/key?args=value1,value2,value3
- LAPPEND [Key "value1,value2,value3"]
- Adds the a list of comma-separated values to a given key.
- returns the new length of the list stored at the given key.
- example:
curl http://localhost:8000/v1/lappend/key?args=value1,value2,value3
- LPOP [Key]
- Removes the last entry from a list and returns it.
- example:
curl http://localhost:8000/v1/lpop/key
-
MSET [KEY "key1 value1" "key2 value2" ...]
- Takes a list of space separated values, transform each argument to key-val pairs and puts them into a map at KEY. It's important to ensure that each argument is space separated or you'll get ERR_MALFORMED_ARGUMENTS error.
-example:
curl http://localhost:8000/v1/mset/KEY?args=key1%20va1key2%20val2
- Takes a list of space separated values, transform each argument to key-val pairs and puts them into a map at KEY. It's important to ensure that each argument is space separated or you'll get ERR_MALFORMED_ARGUMENTS error.
-example:
-
MGET [KEY map-key]
- Returns the map entry stored at map-key of a map which is stored at KEY
- example:
curl http://localhost:8000/v1/mget/KEY?args=map-key
- MGETALL [KEY]
- Returns all entries of the map stored at KEY
- example
curl http://localhost:8000/v1/mgetall/KEY
- MDEL [KEY map-key]
- Removes a map entry stored at map-key of a map stored at KEY
- example:
curl http://localhost:8000/v1/mdel/KEY?args=map-key
- KEYS [regex]
- Returns all keys stored on the server that matches the supplied pattern
- example
curl http://localhost:8000/v1/keys/.?foo$
, it's your duty to url-encode the pattern before making the request.
- DEL [KEY]
- Deletes key and the associated value
- Always returns "OK" even if key does not exist
- example
curl http://localhost:8000/v1/del/KEY
Run lein test
in the root directory of the project