ddev / ddev-redis-7

Redis 7 service for DDEV
Apache License 2.0
3 stars 6 forks source link

Feature: Swappable Redis-Compatible KV stores #31

Open nickchomey opened 2 months ago

nickchomey commented 2 months ago

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 #30, 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.

nickchomey commented 2 months ago

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:

  1. Use a single docker_compose.redis.yaml file, 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/
  2. 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 .ddev root 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.

nickchomey commented 2 months ago

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

seebeen commented 2 months ago

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? :)

nickchomey commented 2 months ago

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.

rfay commented 2 months ago

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

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:

nickchomey commented 2 months ago

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.

nickchomey commented 2 months ago

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