Symfony already supports MongoDB for storing sessions and managing locks. In Symfony 8.2, you can also use it to cache data and queue messages with Messenger.

MongoDB Cache Adapters

Jérôme Tamarelle
Contributed by Jérôme Tamarelle in #65824

If your application already uses MongoDB, you can now use it for your cache pools too. The Cache component adds two adapters, MongoDbAdapter and MongoDbTagAwareAdapter, which store each cache item as a document. The tag-aware one stores the tags inside that document, so invalidating a tag runs a single delete query.

Both need the mongodb/mongodb library. In Symfony applications, use a mongodb:// (or mongodb+srv://) DSN as the provider of your pools:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# config/packages/cache.yaml
framework:
    cache:
        # the database is read from the DSN path; the collection defaults to "cache_items"
        default_mongodb_provider: '%env(MONGODB_URL)%'

        pools:
            product.cache:
                adapter: cache.adapter.mongodb_tag_aware

            # when a pool defines a MongoDB DSN as its provider, Symfony
            # uses the MongoDB adapter automatically
            report.cache:
                provider: 'mongodb://localhost:27017/app?collection_name=reports'

Outside Symfony applications, create the adapter with a DSN or with the MongoDB\Client, Database or Collection object you already use, and call setup() once to create a TTL index so MongoDB removes expired items by itself:

1
2
3
4
5
6
use MongoDB\Client;
use Symfony\Component\Cache\Adapter\MongoDbAdapter;

$client = new Client('mongodb://localhost:27017');
$cache = new MongoDbAdapter($client->getCollection('app', 'cache_items'), defaultLifetime: 3600);
$cache->setup();

MongoDB Messenger Transport

Alessandro Lai
Contributed by Alessandro Lai in #62128

Symfony 8.2 adds a MongoDB transport for Messenger, based on the facile-it/mongodb-messenger-transport package. First, install it:

1
$ composer require symfony/mongodb-messenger

Then, use a mongodb:// (or mongodb+srv://) DSN in your transport:

1
2
# .env
MESSENGER_TRANSPORT_DSN=mongodb://user:pass@localhost:27017/app?collection_name=messenger_messages&queue_name=default

The transport stores messages as documents in a MongoDB collection. Consumers claim messages atomically, so two workers never get the same message. If a worker crashes, its message is delivered again after redeliver_timeout (one hour by default). Run messenger:setup-transports to create the index used by workers to find the pending messages.

You can also dispatch messages inside a MongoDB transaction, so that an order and its OrderPlaced message are saved together or not at all. Pass the transaction's session to Messenger with MongoDbSessionStamp:

1
2
3
4
5
6
7
8
9
use MongoDB\Driver\Session;
use Symfony\Component\Messenger\Bridge\MongoDb\Stamp\MongoDbSessionStamp;
use function MongoDB\with_transaction;

$session = $this->client->startSession();
with_transaction($session, function (Session $session) use ($order): void {
    $this->orders->insertOne($order, ['session' => $session]);
    $this->bus->dispatch(new OrderPlaced($order['_id']), [new MongoDbSessionStamp($session)]);
});

Listening to Multiple MongoDB Queues

Jérôme Tamarelle
Contributed by Jérôme Tamarelle in #66450

The MongoDB transport stores all queues in the same collection, so a worker can consume several of them with a single request to the server, instead of one request per queue. First, define one transport per queue to route messages to each of them:

1
2
3
4
5
6
7
8
9
10
# config/packages/messenger.yaml
framework:
    messenger:
        transports:
            emails: 'mongodb://localhost:27017/app?queue_name=emails'
            invoices: 'mongodb://localhost:27017/app?queue_name=invoices'

        routing:
            App\Message\SendWelcomeEmail: emails
            App\Message\GenerateInvoice: invoices

Both transports use the same collection (messenger_messages by default), and each message document stores the name of its queue. So, a worker connected through any of these transports can read the messages of both queues. Pass the transport name to the messenger:consume command and the queues to its --queues option:

1
$ php bin/console messenger:consume emails --queues=emails --queues=invoices

Messages are consumed in the order they became available, with no priority between queues. All queues must live in the same collection, and failed messages are retried in the queue they came from.

Published in #Living on the edge