Baris ini mengirimkan pesan websocket untuk setiap kolom, berdasarkan persamaan
Beberapa kolom bisa menjadi text dumps atau konfigurasi JSON yang sangat besar sehingga kita harus menambahkan beberapa filter filter yang masuk akal di sini.
Nama saluran adalah nilai dalam ETS, itu bisa sangat besar, bahkan GB.
Adapun bug itu, saya pikir itu terkait dengan pemeriksaan itu:
https://github.com/supabase/realtime/blob/894f4bb8923017467c78803711d8adbef8c090fe/server/lib/realtime/subscribers_notification.ex#L140 -L142
Dalam implementasi saat ini kunci hanya dapat diperiksa hingga 100 simbol.
Terima kasih @ abc3.
Saya harus memeriksa kode saya sendiri sebelum membuat masalah 😅
Saya akan menyiapkan beberapa tes untuk membuat ini gagal - @fracek mengonfirmasi bahwa itu adalah masalah, jadi ada baiknya mencari tahu di mana kegagalan itu terjadi.
Saya sudah memeriksa dengan panjang kunci 2500, semuanya baik-baik saja. Saya dengan senang hati akan menyelidiki bug jika Anda menunjukkan cara mereproduksinya. : blush:
Saya mulai dengan salinan realtime baru (commit 894f4bb89230
), dapatkan deps untuk server ( mix deps.get
) dan untuk contoh node js ( yarn install
). Saya memulai db dari contoh node-js ( docker-compose up db
).
Saya memulai server:
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
dan contoh node-js ( yarn run start
).
Saya memasukkan beberapa data dengan skrip berikut:
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()
dan saya dapat melihat data dialirkan ke contoh node-js. Saya mengubah panjang string menjadi 4000 ( name = 'name' * 1000
) dan berhasil. Saya meningkatkan ukuran string secara bertahap (10 dalam 10) hingga name = 'name' * 1000000
( 1_000_000
). Saya mengubah skrip untuk pertama-tama memasukkan string besar dan setelah itu string kecil, dalam hal ini node-js tidak menerima keduanya.
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()
Saya yakin masalahnya ada pada saluran yang tidak dapat menangani muatan besar.
Satu eksperimen terakhir adalah memeriksa keterlambatan replikasi. Saya menggunakan perintah berikut:
select
slot_name, pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS replicationSlotLag, active
from pg_replication_slots ;
Biasanya hasilnya adalah:
slot_name | replicationslotlag | active
-----------+--------------------+--------
test_slot | 56 bytes | t
(1 row)
Saya menjalankan skrip python saya sekali dan melihatnya tumbuh (setiap kali saya menjalankan skrip itu tumbuh sekitar 40kB). @ w3b6x9 menunjukkan kepada saya bahwa pada contoh pelanggan ukurannya sekitar 20MB. Setelah beberapa saat instance realtime dimatikan oleh os (dengan SIGKILL). Saya memulai ulang instance dan secara perlahan (sangat lambat) melewati backlog tetapi terhenti secara teratur karena penggunaan memori terus meningkat.
Sebagai ringkasan, ada dua masalah yang terlihat saat menyisipkan baris besar:
Dugaan terpelajar saya adalah bahwa ada dua masalah utama (mungkin terkait, mungkin tidak):
Tapi name = 'name' * 1000000
hanya 40MB: bingung:
Dalam kasus saya ini berhasil, saya akan menyelami lebih dalam dengan contoh @fracek .
Btw, pengujian saya menunjukkan latensi yang sangat besar ~ 10sec. Itu terlalu banyak untuk pengiriman beberapa puluh 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))
}
terima kasih telah menguji @ abc3 - bisa jadi karena ini: https://github.com/supabase/realtime/pull/120
Kami melihat perilaku ini di Prod, jadi harus mengembalikan PR. Jika masih lambat setelah menggabungkan PR ini, maka kami pasti perlu memperbaiki latensi
Saya telah membuat kesalahan dalam perhitungan saya: bukan 40 tetapi 4MB))
pengujian masukkan melalui INSERT INTO users(name) VALUES(repeat('name', 1000000))
Saya telah menemukannya!
Waktu respons dengan level debug dan tanpa:
Baris-baris ini dieksekusi beberapa detik:
https://github.com/supabase/realtime/blob/894f4bb8923017467c78803711d8adbef8c090fe/server/lib/realtime/replication.ex#L72 -L73
Saya dapat mereproduksi perbaikannya. Peristiwa masih belum disebarkan melalui websockets tetapi setidaknya instance tersebut tidak menjadi tidak responsif. Kerja bagus!
Browser membutuhkan waktu untuk memecahkan kode dan membuat string yang panjang.
Akan lebih jelas jika membuat beberapa perubahan pada contoh.
perlu menghapus ini:
https://github.com/supabase/realtime/blob/894f4bb8923017467c78803711d8adbef8c090fe/examples/next-js/pages/index.js#L28
ganti itu
https://github.com/supabase/realtime/blob/894f4bb8923017467c78803711d8adbef8c090fe/examples/next-js/pages/index.js#L44 -L53
dengan
messageReceived(channel, msg) {
console.log('got message')
}
dan membaca pesan di konsol browser
cur = conn.cursor ()
name = 'name' * 1000000
cur.execute ("INSERT INTO users (name) VALUES (% s)", (name,))
conn.commit ()
@fracek aneh bahwa ini memberi Anda masalah pada mesin Anda.
Saya pergi untuk memeriksanya saat melakukan debugging. Saya memasukkan 100 baris, dengan setiap baris bernilai 'name' * 1000000
(4MB), dalam transaksi yang sama.
Saat replikasi sedang berlangsung, saya memeriksa kedua replikasi lag (tetap konsisten sekitar 5,3MB):
dan flush lag:
_Ini mirip dengan apa yang saya lihat saat men-debug instance db pelanggan kemarin. Replikasi lag mereka akan naik dan kemudian tetap konsisten dengan lag (12MB, 16MB, 20MB, dll.) Dan memiliki flush lag antara 3-5 menit._
Setelah menunggu cukup lama saya mendapat pesan. Masing-masing berisi nilai 'name' * 1000000
(4MB). Saya juga melacak berapa lama replikasi (~ 30 menit) dibandingkan dengan mengirimkan 100 pesan yang masing-masing membawa nilai 4MB (~ 90 detik).
Saya menemukan seseorang dengan masalah Terputus dari saluran Phoenix saat mengirim banyak pesan besar dalam kerangka waktu yang kecil . Seorang pengguna menyebutkan:
- Phoenix Saya pikir menggunakan batas waktu 60-an atau lebih untuk menerima pesan detak jantung atau lebih.
- Jika butuh waktu lebih lama dari itu untuk mengirim satu pesan, maka phoenix mengira pipa itu membeku atau kewalahan atau semacamnya, jadi itu membunuhnya.
Dan pengguna lain, sasajuric
, menawarkan solusi berikut yang berhasil untuknya:
- mengompresi / mendekompresi muatan di luar serializer
- membuat serial / deserialisasi pesan menggunakan: erlang.term_to_binary dan: erlang.binary_to_term
Untuk saat ini, hanya perlu diingat tetapi Jika kami menemukan bahwa pesan tidak terkirim karena frekuensi tinggi dan muatan yang besar, maka hal pertama yang dapat kami lakukan adalah menggunakan penyambung bawaan yang lebih baru Phoenix.Socket.V2.JSONSerializer
( saat ini kami menggunakan V1
) sebelum melakukan apa yang disarankan sasajuric
.
bisa jadi karena ini: # 120
Ya, jika server realtime
terus memulai ulang dan mengambil tempat yang ditinggalkannya dengan slot replikasi permanen maka saya dapat melihatnya memakan waktu lebih lama dan lebih lama (jika pernah dalam kasus itu tidak dapat kembali berfungsi status karena kesalahan :undefined.handle_message/4
) bagi pelanggan untuk menerima pesan.
Ini mungkin normal mengingat pelanggan melakukan banyak pembaruan sekaligus ( @kiwicopple, saya pikir Anda telah menyebutkan ini di suatu tempat). # 120 telah digabungkan dan akan disebarkan untuk pelanggan segera jadi kami hanya perlu melanjutkan pemantauan.
@ abc3 : btw, contoh Nextjs Anda
Terima kasih! Saya senang kontribusi saya bermanfaat: blush:
Tolong, bagikan hasil pemantauan Anda jika terjadi kesalahan pada prod lagi.
Solusi cepat lama juga menambah waktu 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
Jadi, kode di atas dan implementasi Realtime.SubscribersNotification.notify_subscribers
akan mengirimkan beberapa salinan data serupa.
Misalnya, ketika disisipkan baris dalam tabel users
klien dengan langganan this.addChannel('realtime:*')
dan this.addChannel('realtime:public:users')
akan menerima 4 pesan.
Jika satu pesan 4MB, server akan mengirim 16MB
Komentar yang paling membantu
Saya telah menemukannya!
Waktu respons dengan level debug dan tanpa:
Baris-baris ini dieksekusi beberapa detik:
https://github.com/supabase/realtime/blob/894f4bb8923017467c78803711d8adbef8c090fe/server/lib/realtime/replication.ex#L72 -L73