Image de couverture : Arrêtez de poller vos API : invalidation de cache en temps réel avec PostgreSQL LISTEN/NOTIFY en PHP 8.4
tech

Arrêtez de poller vos API : invalidation de cache en temps réel avec PostgreSQL LISTEN/NOTIFY en PHP 8.4

01 June 2026
3 min de lecture
13 vues
Sébastien Muler

Arrêtez de poller vos API : invalidation de cache en temps réel avec PostgreSQL LISTEN/NOTIFY en PHP 8.4

Si votre application interroge un endpoint central toutes les N secondes pour détecter des changements, vous n'êtes pas seul — et vous gaspillez probablement des millions de requêtes HTTP par jour. C'est exactement le problème qu'a résolu l'équipe de TrendVidStream, décrit dans un article publié sur DEV Community. Leur solution : LISTEN/NOTIFY, une fonctionnalité native de PostgreSQL disponible depuis la version 9.0, et pourtant quasi inconnue dans la majorité des stacks PHP.

Voici ce que cette approche apporte concrètement, et comment l'intégrer dans une application PHP 8.4 / Symfony.


Le problème du polling : simple à mettre en place, coûteux à l'échelle

Dans une architecture multi-régions ou multi-nœuds, le réflexe naturel pour synchroniser un cache local est le polling : chaque nœud interroge périodiquement une source centrale pour savoir si quelque chose a changé.

Cette approche fonctionne tant que les données évoluent lentement. Mais dès que la fréquence de changement augmente — statuts de licences révoquées, scores de popularité recalculés, thumbnails re-encodés — le polling devient un gouffre :

  • Fenêtre de staleness incompressible : abaisser l'intervalle de poll en dessous de 15 secondes sature rapidement l'origin.
  • Requêtes inutiles : la grande majorité des appels ne retournent aucun changement.
  • Couplage fort : chaque consommateur doit connaître et interroger l'endpoint central.

TrendVidStream a mesuré 4,2 millions de requêtes HTTP gaspillées par jour sur huit régions, pour une fenêtre de staleness encore comprise entre 15 et 30 secondes. Le polling ne passait tout simplement pas à l'échelle.


LISTEN/NOTIFY : la messagerie pub/sub intégrée à PostgreSQL

PostgreSQL embarque nativement un mécanisme de publish/subscribe asymétrique :

  • NOTIFY channel, payload : envoie un message sur un canal nommé.
  • LISTEN channel : abonne une connexion à ce canal ; elle reçoit les messages en temps réel.

Le payload est une chaîne libre (en pratique, du JSON). La notification est transactionnelle : elle n'est émise que si la transaction qui la contient est commitée. Pas de message fantôme en cas de rollback.

Émettre une notification depuis PostgreSQL

Un trigger sur la table de métadonnées suffit à propager automatiquement chaque changement :

CREATE OR REPLACE FUNCTION notify_metadata_change()
RETURNS trigger AS $$
BEGIN
  PERFORM pg_notify(
    'metadata_updates',
    json_build_object(
      'id',     NEW.id,
      'region', NEW.region,
      'status', NEW.status
    )::text
  );
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER trg_metadata_change
AFTER INSERT OR UPDATE ON video_metadata
FOR EACH ROW EXECUTE FUNCTION notify_metadata_change();

Chaque modification de video_metadata déclenche automatiquement une notification sur le canal metadata_updates.

S'abonner depuis PHP 8.4

PHP ne dispose pas de support natif non-bloquant pour LISTEN, mais une connexion PDO dédiée avec une boucle de lecture suffit pour un worker CLI (ou un Symfony Messenger consumer) :

<?php

declare(strict_types=1);

$dsn = 'pgsql:host=localhost;dbname=streaming;port=5432';
$pdo = new PDO($dsn, 'app_user', 'secret', [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
]);

$pdo->exec('LISTEN metadata_updates');

while (true) {
    // pg_get_notify() retourne la prochaine notification ou null
    $result = $pdo->query("SELECT pg_get_notify() AS notif")->fetchColumn();

    if ($result !== null) {
        $payload = json_decode($result, true, flags: JSON_THROW_ON_ERROR);
        invalidateCache($payload['id'], $payload['region']);
    }

    usleep(50_000); // 50 ms entre les polls sur la connexion locale
}

Note : pg_get_notify() interroge le buffer local de la connexion PostgreSQL — pas le réseau. Le coût est négligeable comparé à un HTTP round-trip inter-régions.

Intégration avec Symfony Messenger

Dans une architecture Symfony, la bonne pratique est d'encapsuler ce worker dans un Messenger transport dédié ou un Command supervisé par supervisord / systemd. Le handler reçoit un message MetadataInvalidated et délègue l'invalidation au service de cache :

#[AsMessageHandler]
final class MetadataInvalidatedHandler
{
    public function __construct(
        private readonly CacheInvalidator $invalidator
    ) {}

    public function __invoke(MetadataInvalidated $message): void
    {
        $this->invalidator->invalidate($message->videoId, $message->region);
    }
}

Ce que ça change en production

L'article original rapporte des résultats mesurables après migration :

Métrique Avant (polling) Après (LISTEN/NOTIFY)
Requêtes HTTP/jour ~4,2 millions ~0 (hors heartbeat)
Fenêtre de staleness 15 – 30 s < 1 s
Charge origin élevée quasi nulle

La latence de propagation descend en dessous de la seconde, même entre régions, car la notification transite par la connexion PostgreSQL persistante plutôt que par un cycle request/response HTTP.

Pourquoi pas Redis Pub/Sub ou Kafka ?

La réponse de TrendVidStream est pragmatique : ils avaient déjà PostgreSQL. Ajouter Redis ou Kafka pour ce seul cas d'usage aurait signifié une infrastructure supplémentaire à opérer, monitorer et sécuriser. LISTEN/NOTIFY offre ici un excellent rapport puissance/complexité pour des volumes de notifications modérés (quelques milliers par seconde au maximum).

Pour des volumes massifs ou des topologies d'abonnements très complexes, Kafka reste pertinent. Mais pour la majorité des applications PHP/Symfony qui ont déjà PostgreSQL en base principale, LISTEN/NOTIFY est souvent la solution la plus simple et la plus cohérente.


Conclusion

LISTEN/NOTIFY est l'une de ces fonctionnalités PostgreSQL qui dorment dans la documentation depuis des années et qui résolvent élégamment des problèmes que l'on croyait nécessiter un broker dédié. Si votre stack PHP 8.4 / Symfony s'appuie sur PostgreSQL et que vous polling une API ou un endpoint pour invalider votre cache, cette approche mérite sérieusement d'être évaluée.

L'implémentation complète — triggers, worker PHP, gestion des reconnexions et supervision — est détaillée dans l'article original d'Ahmet Gedik sur DEV Community.

Vous souhaitez intégrer ce pattern dans votre application Symfony ? Contactez l'équipe MulerTech pour un audit ou un accompagnement.

Partager cet article