Redis-similar Cache with REST protocol in Golang
- Expiration support (Ttl - time to live)
- Pure Go implementation
- Thread safe
- REST protocol
- Auth support
- Client scalability (multiple servers share the key space)
- Go client library
// Create new cache
cache := gcache.NewCache()
// Set value with ttl (time to live)
cache.Set("key", "value", 1 * time.Second)
// Get value from the cache
value, err := cache.Get("key")
// Get ttl
ttl, err := cache.Ttl("key")
// Delete key from the cache
err := cache.Del("key")
// Update key with a new value
cache.Update("key", "value")
// Update key with a new value and tll
cache.UpdateWithTll("key", "value", 3 * time.Second)
// Get all keys
keys := cache.Keys()
// Get number of items in the cache
count := cache.Count()
// Create new cache
cache := gcache.NewCache()
// Left push
cache.LPush("key", "value")
// Right push
cache.RPush("key", "value")
// Left pop
value, err := cache.LPop("key")
// Right pop
value, err := cache.RPop("key")
// Range
values, err := cache.LRange("key", 2, 10)
// Create new cache
cache := gcache.NewCache()
// Add/Update hash valuy
cache.HSet("key", "hashKey", "some hash value")
// Get hash value
value, err := cache.HGet("key", "hashKey")
The server might be run with or without authentication
// Run the server without authentication
server := server.NewServer()
// Run the server with authentication support.
// Password should be passed into the NewServerWithAuth function
server := server.NewServerWithAuth("pass")
- Keys, List and Hashed share the same keys space. Therefore it's forbidden to create the same key for e.g. Keys and Lists
- Arrays for LRANGE and KEYS are returned as csv (encoding/csv package). Json is not used to make the protocol simple
- Before running the client_test.go execute the server.sh script in the ./cmd/gcache folder. The script runs two cache servers on ports 8080 and 8081. The 8081 server is run with authentication psw=123
The server exposes a REST protocol. Authentication is optional and is implemented as Authorization header with password as a value. If the authentication is failed a standard 401 status is returned in response.
Http method: GET
Url: /keys?key={key}
key - key to get - string - required
Status Code | Meaning | Notes |
---|---|---|
200 | Ok | Body is the value |
400 | Bad Request | |
401 | Auth failed | |
404 | Not Found | |
500 | Server error |
Http method: POST
Url: /keys?key={key}&value={value}&ttl={ttl}
key - key to get - string - required
value - value to set - string - required
ttl - time to live in seconds - int - required
Status Code | Meaning | Notes |
---|---|---|
200 | Ok | |
400 | Bad Request | |
401 | Auth failed | |
500 | Server error |
Http method: PATCH
Url: /keys?key={key}&value={value}&ttl={ttl}
key - key to get - string - required
value - value to set - string - required
ttl - time to live in seconds - int - optional
Status Code | Meaning | Notes |
---|---|---|
200 | Ok | |
400 | Bad Request | |
401 | Auth failed | |
404 | Not Found | |
500 | Server error |
Http method: DELETE
Url: /keys?key={key}
key - key to get - string - required
Status Code | Meaning | Notes |
---|---|---|
200 | Ok | |
400 | Bad Request | |
401 | Auth failed | |
404 | Not Found | |
500 | Server error |
Http method: GET
Url: /keys
Internally implemented as a linked list
Status Code | Meaning | Notes |
---|---|---|
200 | Ok | Body is the csv list of keys |
401 | Auth failed | |
500 | Server error |
Http method: POST
Url: /lists?op=lpush&key={key}&value={value}
key - key list name - string - required
value - value to push into the list - string - required
Status Code | Meaning | Notes |
---|---|---|
200 | Ok | |
400 | Bad Request | |
401 | Auth failed | |
500 | Server error |
Http method: POST
Url: /lists?op=rpush&key={key}&value={value}
key - key list name - string - required
value - value to push into the list - string - required
Status Code | Meaning | Notes |
---|---|---|
200 | Ok | |
400 | Bad Request | |
401 | Auth failed | |
500 | Server error |
Http method: POST
Url: /lists?op=lpop&key={key}
Pop removes element from the list therefore Http POST is used
key - key list name - string - required
Status Code | Meaning | Notes |
---|---|---|
200 | Ok | Body is the poped value |
400 | Bad Request | |
401 | Auth failed | |
404 | Not Found | |
500 | Server error |
Http method: POST
Url: /lists?op=rpop&key={key}
Pop removes element from the list therefore Http POST is used
key - key list name - string - required
Status Code | Meaning | Notes |
---|---|---|
200 | Ok | Body is the poped value |
400 | Bad Request | |
401 | Auth failed | |
404 | Not Found | |
500 | Server error |
Http method: GET
Url: /lists?op=range&key={key}&from={from}&to={to}
Out of range indexes will not produce an error. If start is larger than the end of the list, an empty list is returned.
If stop is larger than the actual end of the list, Redis will treat it like the last element of the list.
Note that if you have a list of numbers from 0 to 100, LRANGE list 0 10 will return 11 elements, that is, the rightmost item is included.
key - key list name - string - required
from - from index in range - int - required
to - to index in range - int - required
Status Code | Meaning | Notes |
---|---|---|
200 | Ok | Body is the cvs list of values |
400 | Bad Request | |
401 | Auth failed | |
404 | Not Found | |
500 | Server error |
Http method: GET
Url: /hashes/key={key}&hashKey={hasKey}
key - key hash name - string - required
hashKey - hash key field - string - required
Status Code | Meaning | Notes |
---|---|---|
200 | Ok | |
400 | Bad Request | Including when the key in hash is not found |
401 | Auth failed | |
404 | Not Found | |
500 | Server error |
Http method: POST
Url: /hashes/key={key}&hashKey={hasKey}&value={value}
key - key list name - string - required
hashKey - hash key field - string - required
value - hash value field - string - required
Status Code | Meaning | Notes |
---|---|---|
200 | Ok | |
400 | Bad Request | |
401 | Auth failed | |
500 | Server error |
func BenchmarkCache_SetGet(b *testing.B) {
cache := NewCache()
for n := 0; n < b.N; n++ {
key := "key" + strconv.Itoa(n)
cache.Set(key, "val", 1 * time.Second)
_, err := cache.Get(key)
if (err != nil){
b.Error("Failed to GET", key)
}
}
}
Result:
BenchmarkCache_SetGet-4 | 1000000 | 2032 ns/op
- Multiple set and get support on hashes
- Optimize number of locks since as for now when a new key is inserted the whole cache is blocked
- Return in response a reason what exactly is not valid on BadRequest(400)
- Implement the Ttl method in client. The method should return ttl by the given key
- More client unit tests