يرسل هذا السطر رسالة websocket لكل عمود ، بناءً على المساواة
يمكن أن تكون بعض الأعمدة عبارة عن مقالب نصية ضخمة أو تكوين JSON ، لذا يجب أن نضيف بعض المرشحات المعقولة هنا.
اسم القنوات له قيمة في خدمات الاختبارات التربوية ، يمكن أن يكون ضخمًا حقًا ، حتى غيغابايت.
بالنسبة إلى هذا الخطأ ، أعتقد أنه مرتبط بهذا الفحص:
https://github.com/supabase/realtime/blob/894f4bb8923017467c78803711d8adbef8c090fe/server/lib/realtime/subscribers_notification.ex#L140 -L142
في التنفيذ الحالي ، يمكن التحقق من مفاتيح تصل إلى 100 رمز فقط.
شكرا جزيلا @ abc3.
يجب أن أتحقق من الكود الخاص بي قبل إنشاء المشكلات 😅
سأقوم بإعداد بعض الاختبارات لفشل ذلك - أكد fracek أنها مشكلة ، لذا من المفيد معرفة مكان حدوث الفشل.
لقد تحققت من طول المفتاح 2500 ، كل شيء على ما يرام. يسعدني أن أحقق في الخطأ إذا أوضحت كيفية إعادة إنتاجه. :احمر خدود:
أبدأ بنسخة جديدة من الوقت الفعلي (الالتزام 894f4bb89230
) ، احصل على deps للخادم ( mix deps.get
) وللمثال العقدة js ( yarn install
). أبدأ db من مثال node-js ( docker-compose up 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
) وهي تعمل. أقوم بزيادة حجم السلسلة تدريجياً (10 في 10) حتى name = 'name' * 1000000
( 1_000_000
). لقد غيرت البرنامج النصي لإدخال سلسلة كبيرة أولاً وبعد ذلك سلسلة صغيرة ، في هذه الحالة لا تتلقى 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 الخاص بي مرة واحدة وأرى أنه ينمو (في كل مرة أقوم بتشغيل البرنامج النصي ينمو بحوالي 40 كيلوبايت). أظهر لي @ w3b6x9 أن هذا الحجم في حالة العميل يبلغ حوالي 20 ميغابايت. بعد فترة من الوقت يتم قتل مثيل الوقت الفعلي بواسطة نظام التشغيل (باستخدام SIGKILL). أقوم بإعادة تشغيل المثيل ويمر ببطء (ببطء شديد) خلال التراكم ولكنه يتلاشى بانتظام لأن استخدام الذاكرة يتزايد باستمرار.
للتلخيص ، هناك مشكلتان ظاهرتان عند إدراج صفوف كبيرة:
تخميني المتعلم هو أن هناك قضيتين رئيسيتين (ربما مرتبطتان ، وربما لا):
لكن name = 'name' * 1000000
40 ميغابايت فقط: مرتبك:
في حالتي تعمل ، سأغوص بشكل أعمق مع مثال
راجع للشغل ، يُظهر اختباري زمن انتقال كبير جدًا يبلغ حوالي 10 ثوانٍ. إنه كثير جدًا لتوصيل بضع عشرات ميغا بايت.
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://github.com/supabase/realtime/pull/120
كنا نشهد هذا السلوك في Prod ، لذلك كان علينا إعادة العلاقات العامة. إذا كان لا يزال بطيئًا بعد دمج هذا العلاقات العامة ، فنحن بالتأكيد بحاجة إلى إصلاح وقت الاستجابة
لقد أخطأت في حسابي: ليس 40 ولكن 4 ميجابايت))
اختبار الإدراج من خلال هذا INSERT INTO users(name) VALUES(repeat('name', 1000000))
لقد وجدت!
وقت الاستجابة بمستوى التصحيح وبدون:
هذه الصفوف تنفذ بضع ثوان:
https://github.com/supabase/realtime/blob/894f4bb8923017467c78803711d8adbef8c090fe/server/lib/realtime/replication.ex#L72 -L73
يمكنني إعادة إنتاج الإصلاح. لا تزال الأحداث لا يتم نشرها عبر مآخذ الويب ولكن على الأقل لا يصبح المثيل غير مستجيب. أحسنت!
يحتاج المتصفح إلى بعض الوقت لفك تشفير السلاسل الطويلة وعرضها.
سيكون من الواضح إذا قمت بإجراء بعض التغييرات في المثال.
بحاجة لإزالة هذا:
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 ()
الاسم = 'الاسم' * 1000000
cur.execute ("INSERT INTO users (name) VALUES (٪ s)"، (name،))
conn.commit ()
fracek غريب أن هذا كان يعطيك مشكلات على جهازك.
ذهبت للتحقق من ذلك أثناء تصحيح الأخطاء. أدخلت 100 صف ، مع كل صف بقيمة 'name' * 1000000
(4 ميجابايت) ، في نفس المعاملة.
نظرًا لأن النسخ المتماثل قيد التقدم ، فقد تحققت من تأخر النسخ المتماثل (ظل ثابتًا عند حوالي 5.3 ميغابايت):
وتأخر التدفق:
_ هذا مشابه لما رأيته أثناء تصحيح أخطاء مثيل db الخاص بالعميل أمس. سيتصاعد تأخر النسخ الخاص بهم ثم يظل ثابتًا في تأخر (12 ميجابايت ، 16 ميجابايت ، 20 ميجابايت ، إلخ) ويكون له فجوة في أي مكان بين 3-5 دقائق.
بعد الانتظار لفترة طويلة تلقيت الرسائل. كل واحد يحتوي على 'name' * 1000000
(4 ميجابايت). لقد تتبعت أيضًا المدة التي استغرقها النسخ المتماثل (حوالي 30 دقيقة) مقارنة بإرسال 100 رسالة تحمل كل منها قيمة 4 ميجابايت (90 ثانية تقريبًا).
لقد وجدت شخصًا يعاني من المشكلة غير متصل بقناة Phoenix عند إرسال رسائل كبيرة متعددة في إطار زمني قصير . ذكر أحد المستخدمين:
- أعتقد أن فينيكس يستخدم مهلة الستينيات أو نحو ذلك لتلقي رسالة نبضات القلب أو نحو ذلك.
- إذا استغرق الأمر وقتًا أطول من ذلك لإرسال رسالة واحدة ، فيعتقد طائر الفينيق أن الأنبوب متجمد أو متكدس أو شيء من هذا القبيل ، لذا فهو يقتله.
وقدم مستخدم آخر ، sasajuric
، الحل التالي الذي نجح معه:
- ضغط / فك ضغط الحمولة خارج المسلسل
- تسلسل / إلغاء تسلسل الرسائل باستخدام: erlang.term_to_binary و: erlang.binary_to_term
في الوقت الحالي ، هناك شيء يجب أخذه في الاعتبار ولكن إذا اكتشفنا أن الرسائل لا يتم إرسالها بسبب التردد العالي والحمولة الضخمة ، فإن أول شيء يمكننا فعله هو استخدام المسلسل المدمج الأحدث Phoenix.Socket.V2.JSONSerializer
( نحن نستخدم حاليًا V1
) قبل القيام بما اقترحه sasajuric
.
قد يكون راجعا إلى هذا: # 120
نعم ، إذا استمر خادم realtime
في إعادة التشغيل والاختيار من حيث توقف مع فتحة النسخ المتماثل الدائم ، فيمكنني أن أرى أنه يستغرق وقتًا أطول وأطول (إذا كان ذلك في حالة عدم تمكنه من العودة إلى العمل الدولة بسبب أخطاء :undefined.handle_message/4
) للعميل لتلقي الرسائل.
قد يكون هذا أمرًا طبيعيًا نظرًا لأن العميل يقوم بإجراء عمليات تفريغ ضخمة من التحديثات في وقت واحد (أعتقد أنك ذكرت هذا في مكان ما kiwicopple ) تم دمج رقم 120 وسيتم نشره للعميل قريبًا ، لذا سيتعين علينا فقط مواصلة المراقبة.
@ abc3 : راجع للشغل ، مثال Nextjs الخاص بك بتصحيح الأخطاء. شكرا لك مرة أخرى!
شكرا! أنا سعيد لأن مساهمتي مفيدة: أحمر الخدود:
من فضلك ، شارك نتيجة المراقبة الخاصة بك إذا حدث خطأ ما في prod مرة أخرى.
يضيف الحل السريع القديم الوقت أيضًا 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 رسائل.
إذا كانت الرسالة الواحدة 4 ميجا بايت ، فسيرسل الخادم 16 ميجا بايت
التعليق الأكثر فائدة
لقد وجدت!
وقت الاستجابة بمستوى التصحيح وبدون:
هذه الصفوف تنفذ بضع ثوان:
https://github.com/supabase/realtime/blob/894f4bb8923017467c78803711d8adbef8c090fe/server/lib/realtime/replication.ex#L72 -L73