この行は、平等に基づいて、すべての列にWebSocketメッセージを送信しています
一部の列は巨大なテキストダンプまたはJSON構成になる可能性があるため、ここにいくつかの適切なフィルターフィルターを追加する必要があります。
チャネルの名前はETSの価値であり、GBでさえ、非常に大きくなる可能性があります。
そのバグに関しては、私はそれがそのチェックに関連していると思います:
https://github.com/supabase/realtime/blob/894f4bb8923017467c78803711d8adbef8c090fe/server/lib/realtime/subscribers_notification.ex#L140 -L142
現在の実装では、最大100個のシンボルのキーしかチェックできません。
@ abc3に感謝します。
問題を作成する前に自分のコードを確認する必要があります😅
これを失敗させるためにいくつかのテストを設定します- @ fracekはそれが問題であることを確認したので、失敗が発生している場所を把握する価値があります。
キー2500の長さで確認しましたが、すべて問題ありません。 バグの再現方法を教えていただければ、喜んで調査させていただきます。 :赤面:
リアルタイムの新しいコピー(commit 894f4bb89230
)から始めて、サーバー( mix deps.get
)とノードjsの例( yarn install
)のdepsを取得します。 node-jsの例( docker-compose up db
)からdbを開始します。
サーバーを起動します。
PORT=4000 \
HOSTNAME=localhost \
DB_USER=postgres \
DB_HOST=localhost \
DB_PASSWORD=postgres \
DB_NAME=postgres \
DB_PORT=5432 \
DB_PORT=5432 \
SLOT_NAME=TEST_SLOT \
mix phx.server
およびnode-jsの例( yarn run start
)。
次のスクリプトでいくつかのデータを挿入します。
import psycopg2
import time
def main():
conn = psycopg2.connect(
host="localhost",
database="postgres",
user="postgres",
password="postgres"
)
cur = conn.cursor()
name = 'name'
cur.execute("INSERT INTO users(name) VALUES(%s)", (name, ))
conn.commit()
main()
そして、node-jsの例にストリーミングされたデータを見ることができます。 文字列の長さを4000( name = 'name' * 1000
)に変更すると、機能します。 name = 'name' * 1000000
( 1_000_000
)まで、文字列のサイズを段階的に(10分の10)増やします。 最初に大きな文字列を挿入し、その後小さな文字列を挿入するようにスクリプトを変更しました。この場合、node-jsはどちらも受信しません。
import psycopg2
import time
def main():
conn = psycopg2.connect(
host="localhost",
database="postgres",
user="postgres",
password="postgres"
)
cur = conn.cursor()
name = 'name' * 1000000
cur.execute("INSERT INTO users(name) VALUES(%s)", (name, ))
conn.commit()
name = 'name'
cur.execute("INSERT INTO users(name) VALUES(%s)", (name, ))
conn.commit()
main()
問題は、チャネルが大きなペイロードを処理できないことにあると思います。
最後の実験は、レプリケーションラグを確認することです。 次のコマンドを使用します。
select
slot_name, pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS replicationSlotLag, active
from pg_replication_slots ;
通常、結果は次のとおりです。
slot_name | replicationslotlag | active
-----------+--------------------+--------
test_slot | 56 bytes | t
(1 row)
Pythonスクリプトを1回実行すると、スクリプトが大きくなるのがわかります(スクリプトを実行するたびに、約40kBずつ大きくなります)。 @ w3b6x9は、顧客インスタンスではこのサイズが約20MBであることを示しました。 しばらくすると、リアルタイムインスタンスがOSによって強制終了されます(SIGKILLを使用)。 インスタンスを再起動すると、ゆっくりと(非常にゆっくりと)バックログが実行されますが、メモリ使用量が増え続けるため、定期的に強制終了されます。
要約すると、大きな行を挿入するときに目に見える問題が2つあります。
私の知識に基づく推測では、2つの主要な問題があります(おそらく関連しているかもしれませんが、そうでないかもしれません):
しかし、 name = 'name' * 1000000
はわずか40MBです:confused:
私の場合はうまくいきますが、 @ fracekの例でさらに深く掘り下げます。
ところで、私のテストでは、約10秒という非常に大きな遅延が示されています。 数十MBを配信するには多すぎます。
export default async (req, res) => {
const values = Array(1000000).fill("name").join('')
const text = `INSERT INTO stress(value) VALUES('${values}') RETURNING id`
const q = await pool.query(text)
res.json(JSON.stringify(q.rows))
}
@ abc3をテストしていただきありがとうhttps :
Prodでこの動作が見られたため、PRを元に戻す必要がありました。 このPRをマージした後もまだ遅い場合は、レイテンシーを修正する必要があります
計算を間違えました:40ではなく4MB))
このINSERT INTO users(name) VALUES(repeat('name', 1000000))
介して挿入をテストします
見つけた!
デバッグレベルがある場合とない場合の応答時間:
これらの行は数秒実行されます。
https://github.com/supabase/realtime/blob/894f4bb8923017467c78803711d8adbef8c090fe/server/lib/realtime/replication.ex#L72 -L73
修正を再現できます。 イベントはまだWebSocketを介して伝播されませんが、少なくともインスタンスが応答しなくなることはありません。 よくやった!
ブラウザは、長い文字列をデコードしてレンダリングするのに少し時間がかかります。
例にいくつかの変更を加えると、より明確になります。
これを削除する必要があります:
https://github.com/supabase/realtime/blob/894f4bb8923017467c78803711d8adbef8c090fe/examples/next-js/pages/index.js#L28
それを置き換えます
https://github.com/supabase/realtime/blob/894f4bb8923017467c78803711d8adbef8c090fe/examples/next-js/pages/index.js#L44 -L53
と
messageReceived(channel, msg) {
console.log('got message')
}
ブラウザコンソールでメッセージを読みます
cur = conn.cursor()
name = 'name' * 1000000
cur.execute( "INSERT INTO users(name)VALUES(%s)"、(name、))
conn.commit()
@fracekこれがあなたのマシンに問題を与えていたのは奇妙です。
デバッグ中にチェックしてみました。 同じトランザクションで、各行の値が'name' * 1000000
(4MB)の100行を挿入しました。
レプリケーションが進行中であるため、両方のレプリケーションラグを確認しました(約5.3MBで一貫性を保ちました)。
とフラッシュラグ:
_これは、昨日お客様のdbインスタンスをデバッグしているときに見たものと似ています。 それらのレプリケーションラグは上昇し、その後、ラグ(12MB、16MB、20MBなど)で一貫性を保ち、3〜5分の間のフラッシュラグが発生します。_
しばらく待ってからメッセージが届きました。 それぞれに'name' * 1000000
(4MB)の値が含まれていました。 また、それぞれが4MBの値(〜90秒)を運ぶ100個のメッセージを送信するのと比較して、レプリケーションにかかった時間(〜30分)を追跡しました。
短い時間枠で複数の大きなメッセージを送信すると、Phoenixチャネルから切断されるという問題のある人を見つけ
- フェニックスは、ハートビートなどのメッセージを受信するために60秒程度のタイムアウトを使用すると思います。
- 1つのメッセージを送信するのにそれよりも時間がかかる場合、フェニックスはパイプが凍結しているか、圧倒されているか何かであると見なし、パイプを強制終了します。
そして、別のユーザーsasajuric
、彼に役立つ次のソリューションを提供しました。
- シリアライザーの外部でペイロードを圧縮/解凍する
- :erlang.term_to_binaryおよび:erlang.binary_to_termを使用したメッセージのシリアル化/逆シリアル化
今のところ、覚えておくべきことですが、高頻度でペイロードが大きいためにメッセージが送信されていないことがわかった場合、最初にできることは、新しい組み込みのシリアライザーPhoenix.Socket.V2.JSONSerializer
( sasajuric
提案したことを実行する前に、現在V1
)を使用しています。
これが原因である可能性があります:#120
ええ、 realtime
サーバーが再起動し続け、永続的なレプリケーションスロットで中断したところから再開すると、サーバーがますます長くかかることがわかります(動作に戻ることができなかった場合)顧客がメッセージを受信するための:undefined.handle_message/4
エラーによる状態)。
顧客が一度に大量の更新を実行することを考えると、これは正常なことかもしれません(
@ abc3 :ところで、ルーティング時間#118のチャートを使用したNextjsの例は素晴らしく、デバッグ中に非常に役立ちました。 再度、感謝します!
ありがとう! 私の貢献がお役に立ててうれしいです:blush:
製品で問題が発生した場合は、監視結果を共有してください。
古いクイックソリューションも時間を追加しますhttps://github.com/supabase/realtime/issues/8#issuecomment-564551365
https://github.com/supabase/realtime/blob/af6344c7746e8a8af6a11a9b498721c1f97e339b/server/lib/realtime_web/channels/realtime_channel.ex#L21 -L24
したがって、上記のコードと実装Realtime.SubscribersNotification.notify_subscribers
は、同様のデータのコピーをいくつか送信します。
たとえば、テーブルusers
行を挿入すると、 this.addChannel('realtime:*')
とthis.addChannel('realtime:public:users')
サブスクリプションを持つクライアントは4つのメッセージを受信します。
1つのメッセージが4MBの場合、サーバーは16MBを送信します
最も参考になるコメント
見つけた!
デバッグレベルがある場合とない場合の応答時間:
これらの行は数秒実行されます。
https://github.com/supabase/realtime/blob/894f4bb8923017467c78803711d8adbef8c090fe/server/lib/realtime/replication.ex#L72 -L73