Search code examples
phpsymfonysymfony4symfony-messengersymfony-cache

How to share the application cache between multiple Symfony instances (shared cache pool)?


My symfony application has multiple instances that are running in seperate docker containers.

And I have configured my app.cache to use redis:

framework:
    cache:
        app: cache.adapter.redis

I have the same prefix_seed:

framework:
    cache:
        prefix_seed: 'dev'

As a result I'm getting in redis something like this:

1605259288.470950 [0 172.18.0.28:55044] "MGET" "HnMEIyUlZ+:workers.restart_requested_timestamp"
1605259288.471680 [0 172.18.0.28:55044] "SET" "HnMEIyUlZ+:workers.restart_requested_timestamp" "d:1605259288.471522;"
1605259314.483389 [0 172.18.0.29:42884] "MGET" "8TMgMtnOAG:workers.restart_requested_timestamp"

As you can see from the above 2 different instances are trying to fetch value from redis by the same key workers.restart_requested_timestamp but the prefix is different even with the same prefix_seed.

In this example I'm using messenger component, and I want to stop workers running everywhere by stop-workers command (through the shared redis). But generally speaking this is related to cache configuration.

How to overcome this and tell both applications to use same pool? What is the configuration for this?


Solution

  • Finally, I found the solution. There's an option to create your own cache pool with mapped adapter to it. The main trick here is to pass the tag with namespace to your adapter (name is also should be cache.pool):

    framework:
        cache:
            pools:
                cache.redis_shared_pool:
                    adapter: app.cache_shared_redis_adapter
    
    services:
        app.cache_shared_redis_adapter:
            parent: 'cache.adapter.redis'
            tags:
                - { name: 'cache.pool', namespace: 'shared' }
    

    That's it! All your keys in redis will be prefixed with shared:. Now you should pass your @cache.redis_shared_pool to any service you want.

    And as for the messenger component, we should override the services (but I'm not sure this is the best way):

    console.command.messenger_stop_workers:
        class: Symfony\Component\Messenger\Command\StopWorkersCommand
        arguments:
           $restartSignalCachePool: "@cache.redis_shared_pool"
    
    messenger.listener.stop_worker_on_restart_signal_listener:
        class: Symfony\Component\Messenger\EventListener\StopWorkerOnRestartSignalListener
        arguments:
           $cachePool: "@cache.redis_shared_pool"