A redis cache proxy service
There are myriad solutions for scaling out redis.
First, a definition. According to Wikipedia, redis is:
an open-source in-memory database project implementing a distributed, in-memory key-value store with optional durability.
Note the optional durability part: this means redis is not a database. While there are options that may be specified that will make redis resemble a database, using redis as a database will probably lead to problems (I have seen this in action, plus I have heard similiar stories from more than one company).
However, I suppose I can understand the confusion; according to the same Wikipedia article:
Redis has also been ranked the #4 NoSQL database in user satisfaction and market presence based on user reviews
The distributed part bears some discussion as well. There are ways of replicating and/or sharding redis that allow for the creation of redis clusters (of up to 1,000 nodes), but this effort attempts to create redis caches that will offload GET requests from the redis master.
The ultimate goal is that these caches should be composable, i.e. it should be possible to layer them one on top of another so as to create a federated cache. Accordingly, they will either serve GET requests from local cache memory or pass them on to the next redis cache. Ultimately these GET requests are served by the redis master itself.
The application will be deployed as follows:
The application launches a web server which responds to HTTP requests (in the case of an endpoint) or to TCP requests (in the case of an intermediate).
When a request for a specific key is received, the server checks its local cache for the specified key. If found, the value associated with the key is returned (either via HTTP [endpoints] or TCP [in the case of intermediates]). If not found, the request is forwarded on to the next server (or Redis itself) - if the key is found then the value is stored in the cache and the value is returned. If not found an HTTP 404 is returned.
At server startup a daemon process is launched, which runs from time to time and expires any cache entries older than a configurable time limit.
The size of the cache may also be specified; only this number of entries may be stored in the cache, with older entries being evicted to make space for newer entries.
Internally, redis uses its own wire protocol. See the following link for details:
http://redis.io/topics/protocol
-
Download the repo.
-
Unzip it somewhere.
-
Change directory into the repo: cd assignment
-
Type the following to run the tests: make test
[It may take a few minutes for the docker images to download.]
The results should look as follows (times are GMT):
$ make test
docker-compose up -d redis
Creating network "rediscache_redis-caching" with the default driver
Creating rediscache_redis_1 ...
Creating rediscache_redis_1 ... done
docker-compose up golang
rediscache_redis_1 is up-to-date
Creating rediscache_golang_1 ...
Creating rediscache_golang_1 ... done
Attaching to rediscache_golang_1
golang_1 | Reformatting source code ...
golang_1 | Vetting source code ...
golang_1 | Testing source code ...
golang_1 | 2018/04/14 20:57:57 Running setUpTestData
golang_1 | === RUN TestHealthCheck
golang_1 | --- PASS: TestHealthCheck (0.00s)
golang_1 | === RUN TestCacheHit
golang_1 | --- PASS: TestCacheHit (0.00s)
golang_1 | === RUN TestCacheMiss
golang_1 | --- PASS: TestCacheMiss (0.00s)
golang_1 | === RUN TestGetExistingRedisKey
golang_1 | --- PASS: TestGetExistingRedisKey (0.00s)
golang_1 | === RUN TestGetNonexistentRedisKey
golang_1 | --- PASS: TestGetNonexistentRedisKey (0.00s)
golang_1 | === RUN TestGetExpiredCacheKey
golang_1 | --- PASS: TestGetExpiredCacheKey (5.40s)
golang_1 | === RUN TestGetExpiredRedisKey
golang_1 | --- PASS: TestGetExpiredRedisKey (6.00s)
golang_1 | === RUN TestGetTouchedCacheKey
golang_1 | --- PASS: TestGetTouchedCacheKey (11.41s)
golang_1 | PASS
golang_1 | 2018/04/14 20:58:19 Running tearDownTestData
golang_1 | ok redis-cache 22.828s
rediscache_golang_1 exited with code 0
docker-compose down
Stopping rediscache_redis_1 ... done
Removing rediscache_golang_1 ... done
Removing rediscache_redis_1 ... done
Removing network rediscache_redis-caching
$
The various libraries are bundled in a vendor directory.
This is perhaps not ideal but they would need to be downloaded in any case, and it is always nice to have all dependencies bundled with source code.
The dependencies are sourced as follows:
$ GOPATH=`pwd`/vendor/ go get -d -v .
For testing, redis-cli
is needed. Set up test data as follows:
$ redis-cli
127.0.0.1:6379> SET keyt valuet
OK
127.0.0.1:6379> exit
$
The HTTP version (default) requires curl
for testing.
The TCP version requires nc
(netcat) for testing.
docker-compose up -d redis
docker-compose up golang
docker-compose down
Build redis_lru_cache
(this will save time later).
$ make
<...>
redis_lru_cache has been compiled
$
Verify redis is running correctly as follows:
$ docker-compose up -d redis
<...>
And:
$ cat test | nc localhost 6379
$6
valuet
$
Test as follows:
$ docker-compose up golang-tcp
<...>
In a new console:
$ cat test | nc localhost 7001
valuet$
Test as follows:
$ docker-compose up golang-http
<...>
In a new console:
$ curl http://localhost/keyt
valuet$