Les files d'attente sont un excellent outil qui s'adapte presque parfaitement. Le fer ne résiste pas? Nous venons d'ajouter des nœuds au cluster. Lorsqu'une file d'attente est présente dans un projet, il est tentant d'implémenter de plus en plus de fonctionnalités avec son aide.
Nous parlerons des écueils de cette voie dans cet article.
Tôt ou tard, lors de l'utilisation des files d'attente, l'utilisateur est confronté à la question de leur utilisation en conjonction avec un service, une base de données, etc.
La commande est terminée, vous devez envoyer une notification par SMS à l'utilisateur.
Une nouvelle commande est arrivée, vous devez envoyer une notification push aux exécuteurs testamentaires.
Le travail est terminé, vous devez radier l'argent du compte du client.
Dans tous les exemples ci-dessus, les changements dans une entité commerciale sont enregistrés dans la base de données (ou un service avec une base de données), et il y a une grande tentation d'envoyer des notifications en utilisant des files d'attente.
Qu'avons-nous dans cette situation? Structure de code initiale et la plus simple:
Le service (notre programme) enregistre les modifications des données dans la base de données.
Le service place ensuite le travail dans la file d'attente.
En fait, dans ce cas, vous devez implémenter un déclencheur d'événement pour modifier l'enregistrement de données.
Et dans le cas général, il s'avère qu'ici nous avons deux enregistrements dans deux bases de données différentes: les services et les files d'attente.
Passons maintenant au monde réel et considérons les situations qui peuvent survenir:
Tout va bien. La base de données est disponible, la base de données de la file d'attente est disponible;
, ;
, ;
, .
: , , .
, , ... . .
, .
, ( , , ), , :
.
.
.
.
, , .
:
, . 3 4 ( ).
. .
.
, . : , . , , .
, (, , http- /).
, .
/, ( queue
) .
, , ( ):
/* */
UPDATE
"orders"
SET
"status" = 'complete'
WHERE
"order_id" = $1
RETURNING
*
/* */
WITH "o" AS (
UPDATE
"orders"
SET
"status" = 'complete'
WHERE
"order_id" = $1
RETURNING
*
),
"q" AS (
INSERT INTO
"queue"
(
"key",
"data"
)
SELECT
"o.order_id",
"o.status"
FROM
"o"
)
SELECT
*
FROM
"o"
: queue
, , orders
.
, queue
, , . , .
:
queue
.
.
.
/
, , . , - ( , ..), :
.
, , , , O_APPEND
- .
( ) ,
.
( ) .
, , , , .
Comme vous pouvez le voir, il existe peu d'options pour résoudre le problème. Si nous voulons garder le système simple (principe KISS), alors l'introduction d'un démon supplémentaire et des messages de cache / journal dans la base de données ou le fichier / base de données local augmentera légèrement la complexité. Dans le même temps, il est très important de garder le gestionnaire idempotent, car en cas d'échec au moment du transfert des tâches du cache local vers la file d'attente générale, des doublons peuvent apparaître.
Une solution généralisée consiste à utiliser un commit en deux phases.