キューは、ほぼ完全に拡張できる優れたツールです。鉄は対応しませんか?クラスターにノードを追加しました。プロジェクトにキューが存在する場合、その助けを借りてますます多くの機能を実装したくなります。
この記事では、このパスの落とし穴について説明します。
遅かれ早かれ、キューを使用するとき、ユーザーはキューを何らかのサービスやデータベースなどと組み合わせて使用するという問題に直面します。
注文が完了したら、SMS通知をユーザーに送信する必要があります。
新しい注文が到着しました。エグゼキュータにプッシュ通知を送信する必要があります。
仕事は終わりました、あなたはクライアントの口座からお金を帳消しにする必要があります。
上記のすべての例で、ビジネスエンティティの変更はデータベース(またはデータベースを備えたサービス)に記録され、キューを使用して通知を送信したいという大きな誘惑があります。
この状況で私たちは何を持っていますか?初期の最も単純なコード構造:
サービス(私たちのプログラム)は、データの変更をデータベースに記録します。
次に、サービスはジョブをキューに入れます。
実際、この場合、データレコードを変更するためにイベントトリガーを実装する必要があります。
そして、一般的なケースでは、ここでは2つの異なるデータベース(サービスとキュー)に2つのレコードがあることがわかります。
それでは、現実の世界に飛び込んで、どのような状況が発生する可能性があるかを考えてみましょう。
すべて順調。DBが利用可能で、キューDBが利用可能です。
, ;
, ;
, .
: , , .
, , ... . .
, .
, ( , , ), , :
.
.
.
.
, , .
:
, . 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
- .
( ) ,
.
( ) .
, , , , .
ご覧のとおり、問題を解決するためのオプションはほとんどありません。システムをシンプルに保ちたい場合(KISSの原則)、データベースまたはローカルファイル/データベースに追加のデーモンとキャッシュ/ログメッセージを導入すると、複雑さがわずかに増加します。同時に、ローカルキャッシュから一般キューへのタスクの転送時に障害が発生した場合、重複が発生する可能性があるため、ハンドラーをべき等に保つことが非常に重要です。
一般的な解決策は、2フェーズコミットを使用することです。