ddev-redis
ddev-redis copied to clipboard
Feature: Swappable Redis-Compatible KV stores
With Redis changing its license recently, a slew of Redis-compatible KV stores were forked/created/released (in addition to other promising ones that already existed). Decent enough background on that here.
The thought here is to make this more of a ddev kv command rather than just Redis (though it can stay as ddev redis), which could allow users to simply toggle between different Redis-compatible KV stores and their application code would presumably not need to change at all - it would still point to redis:6379 (or the unix socket equivalent).
This would make it easier to experiment with the various stores, without needing to create additional ddev addons or other project-specific custom dockerfiles.
In theory it should be as easy as just changing the docker image in use...
Here's DragonflyDB's Docker Compose installation instructions
Basically just download this docker-compose.yaml file and run docker compose up and then connect to it with redis-cli - it Just Works (tm) with localhost:6379 defaults.
version: '3.8'
services:
dragonfly:
image: 'docker.dragonflydb.io/dragonflydb/dragonfly'
ulimits:
memlock: -1
ports:
- "6379:6379"
# For better performance, consider `host` mode instead `port` to avoid docker NAT.
# `host` mode is NOT currently supported in Swarm Mode.
# https://docs.docker.com/compose/compose-file/compose-file-v3/#network_mode
# network_mode: "host"
volumes:
- dragonflydata:/data
volumes:
dragonflydata:
(Interesting that they have an explicit note to use a host network for better performance! Probably relevant to ddev/ddev-redis#33, though likely not possible to implement in the context of DDEV).
I have to assume the rest of the Redis-compatible KV stores are similarly seamless drop-in replacements.
https://github.com/valkey-io/valkey - Linux Foundation Redis Fork https://github.com/Snapchat/KeyDB - Snapchat's redis fork, which now has multithreading, active replicatoin and more. A bit stale/unmaintained though https://github.com/dragonflydb/dragonfly - Very promising mutlithreaded Redis-compatible store https://github.com/microsoft/Garnet - From microsoft, and has very promising benchmark stats and features https://github.com/nalgeon/redka - Somewhat Redis-compatible, that sits on SQLite. Very interesting. https://github.com/apache/kvrocks - From apache. Distributed via async replication https://github.com/Tencent/Tendis
And PLENTY more. The point is not to officially find and support them all, but to make it easy to swap them in and out within the same redis DDEV container.
Perhaps the image could be defined in a redis-specific config file or project environment variable? Or just put all the docker compose files in the same directory and a ddev redis backend {dbname} command could toggle between them. Or something else - I'm sure there's a good, easy, flexible, robust way to implement this.
If there's any interest in this, I'll fiddle around to see how it might work.
As expected, this works for using the dragonflydb image instead of redis. I simply changed the image, and commented-out the deploy: section as that seems to be for docker swarm and breaks with dragonflydb. My wordpress app, redis-cli, redis-benchmark all connect automatically. It isn't using any config files, but that could surely be solved easily enough.
#ddev-generated
volumes:
redis:
name: ddev-${DDEV_SITENAME}-redis
labels:
com.ddev.site-name: ${DDEV_SITENAME}
services:
redis:
container_name: ddev-${DDEV_SITENAME}-redis
hostname: ddev-${DDEV_SITENAME}-redis
# image: redis:7.4-alpine
# command: /etc/redis/conf/redis.conf
image: docker.dragonflydb.io/dragonflydb/dragonfly
volumes:
- ".:/mnt/ddev_config"
- "ddev-global-cache:/mnt/ddev-global-cache"
- "./redis:/etc/redis/conf"
- "redis:/data"
expose:
- 6379
networks:
- default
# deploy:
# resources:
# limits:
# cpus: "2.5"
# memory: "768M"
# reservations:
# cpus: "1.5"
# memory: "512M"
restart: "no"
labels:
com.ddev.site-name: ${DDEV_SITENAME}
com.ddev.approot: $DDEV_APPROOT
swapping in image: ghcr.io/microsoft/garnet works equally seamlessly. So, I see no reason why we couldn't make this happen...
redis-benchmark performance is vastly worse for those than redis, but that's surely just due to not using any configuration at all.
As suggested in the OP, I think the most approachable way to implement this would be something like a ddev redis backend command that can swap between the kv backends. Regardless of how it is done, I suppose it would need to restart the container in order build the appropriate image. Surely just the redis container could be restarted rather than the whole ddev project
Some options for selecting the image:
- Use a single
docker_compose.redis.yamlfile, which contains a placeholder for the image that gets populated via an environment variable such as${DDEV_REDIS_IMAGE}. The command could just re-set the env variable before restarting the container/ - Use different docker compose files for each backend, and then switch between them. Perhaps the command could rename/move them so that there's only ever one there in the
.ddevroot directory?
The latter is probably preferrable as it would allow for different docker configs for each backend - not sure if that's necessary or not? Probably is, especially if you want to do something like replication across different containers to simulate a cluster of app/redis/db servers.
Either way, I actually don't see much reason why the addon couldn't "officially" support the most popular backends - redis, dragonfly, valkey, garnet etc... Just include their docker compose files or image name, depending on which switching mechanism is implemented. People could request more be added, or an env var extension could be implemented.
The details are less important at this point than the willingness to see something like this.
I see that there's an active PR for making ddev get more extensible/flexible. Best to wait for it to get merged before proceeding with anything here.
https://github.com/ddev/ddev/pull/6406
Nick. Again - thanks for the detailed and st8-to-the-point writeup. I'd rather build out separate extensions for both DragonflyDB and Valkey - and let users choose which one to use. Less overhead in maintaining three extensions than having one-in-all solution.
@rfay I'd like to get your thoughts on this? :)
That's fine. I had assumed there would be less effort required to maintain one addon, but defer to your knowledge of ddev. It was worth bringing up at least and poking around a bit with the idea.
It is a worthy thought to see if we could consolidate all the redis clones (and redis versions?) into one add-on.
I'd love to see a review of @stasadev 's
- https://github.com/ddev/ddev/pull/6406
wrt this idea. Could you try it out and see if you think that PR would give you enough customization capability (and persistence of customization) and do a review there?
Related PR that goes with that DDEV PR:
- https://github.com/ddev/ddev-redis/pull/24
The main hurdle I see to adding something like this to this addon is how to define and handle each backend's config file(s). It has to be assumed that none of them are actually compatible with each other, it's just the api and port that are compatible. In fact, when I used valkey, it failed when I used the existing redis config files, even though valkey is apparently a straight fork of redis. Changing to the original version of valkey didn't help.
I've also already had issues with a getting a custom config file working for a few of the backends, but I'm sure that can be solved. But it's not something that we should offload on users.
So, it would probably be best to (upon request/PR contribution) add "official" support for different backends where the required base config file(s) and image string are added to a common parent directory, and then the addon exposes a command to just toggle which backend will be used when starting the containers.
I'll follow up when I've had time to look at the addon get settings pr, to understand it and see how it might be useful here. It would be a good "real world" test case for it to help refine it's implementation, if needed.
I just shared thoughts on that PR. I think .env.redis variables would be a good way to handle this feature request, but dont think the way that it is currently implemented is appropriate. Ive made some detailed observations/suggestions there. We'll see where it goes
Working on this in:
- #42