خطأ SQL مع `screened_ip_addresses` (يرمي API 500)

مرحباً يا أصدقاء،

يُرجع الـ API لي خطأ 500 عند استدعائه لإنشاء منشور جديد (ضمن موضوع موجود). وفي السجلات أرى:

ActiveRecord::StatementInvalid (PG::InvalidTextRepresentation: ERROR:  invalid input syntax for type inet: ""
LINE 1: ..._addresses".* FROM "screened_ip_addresses" WHERE ('' <<= ip_...
                                                             ^
)
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/rack-mini-profiler-2.0.1/lib/patches/db/pg.rb:69:in `exec_params'

فشل التعامل مع الاستثناء في وسيط استثناء التطبيق: PG::InvalidTextRepresentation: ERROR:  invalid input syntax for type inet: ""
LINE 1: ..._addresses".* FROM "screened_ip_addresses" WHERE ('' <<= ip_...
                                                             ^

إليك قائمة عناوين IP المحجوبة؛ وبالإضافة إلى لقطة الشاشة أدناه، قمت أيضًا بإضافة عنوان IP للجهاز الذي يستدعي الـ API إلى القائمة البيضاء. (أستخدم مفتاح API على مستوى النظام لاستيراد مواضيع ورسائل قديمة من برنامج المنتدى القديم.)

مجرد فضول، طلبت أيضًا من الـ API قائمة عناوين IP المحجوبة… وكانت النتائج نفسها. (https://mydiscourse.com/admin/logs/screened_ip_addresses.json)

لا أعرف ما الذي يجب التحقق منه بعد الآن. :man_shrugging:t2:

هل يعرف أحد:

1. ما الذي يسبب هذا الخطأ، و
2. كيف يمكنني إصلاحه الآن ومنع تكراره في المستقبل؟

مساعدة :slight_smile:

شكرًا!

أم أن الأمر يتعلق أكثر بـ SQL آخر يحاول الإدراج في تلك الجدول؟

هل يمكنك نشر الكود الذي تستخدمه لإجراء طلب API مع إزالة بيانات الاعتماد حتى نتمكن من رؤية كيفية إجراء طلب الـ API؟

مرحبًا @blake، شكرًا على الرد. فقط للتأكد تمامًا من أن المشكلة ليست في كودي، قمت بإعداد استدعاء API أساسي في Insomnia (مشابه لـ Postman ولكن في رأيي أبسط/أسهل). للأسف، نفس النتائج، ولكن على الأقل الآن أصبح الأمر واضحًا جدًا:

إليك الاستدعاء الذي أعددته لإنشاء منشور جديد (لقد قمت بالفعل بتقليل “الحد الأدنى لعدد الكلمات في المنشورات” إلى 1):

وهذه هي النتائج

وإليك رسالتي الخطأ التين في السجلات من استدعاءات الاختبار هاتين:

خطأ 1:

Message (15 copies reported)

ActiveRecord::StatementInvalid (PG::InvalidTextRepresentation: ERROR:  invalid input syntax for type inet: ""
LINE 1: ..._addresses".* FROM "screened_ip_addresses" WHERE ('' <<= ip_...
                                                             ^
)
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/rack-mini-profiler-2.0.1/lib/patches/db/pg.rb:69:in `exec_params'

Backtrace

rack-mini-profiler-2.0.1/lib/patches/db/pg.rb:69:in `exec_params'
rack-mini-profiler-2.0.1/lib/patches/db/pg.rb:69:in `exec_params'
activerecord-6.0.1/lib/active_record/connection_adapters/postgresql_adapter.rb:672:in `block (2 levels) in exec_no_cache'
activesupport-6.0.1/lib/active_support/dependencies/interlock.rb:48:in `block in permit_concurrent_loads'
activesupport-6.0.1/lib/active_support/concurrency/share_lock.rb:187:in `yield_shares'
activesupport-6.0.1/lib/active_support/dependencies/interlock.rb:47:in `permit_concurrent_loads'
activerecord-6.0.1/lib/active_record/connection_adapters/postgresql_adapter.rb:671:in `block in exec_no_cache'
activerecord-6.0.1/lib/active_record/connection_adapters/abstract_adapter.rb:718:in `block (2 levels) in log'
/usr/local/lib/ruby/2.6.0/monitor.rb:235:in `mon_synchronize'
activerecord-6.0.1/lib/active_record/connection_adapters/abstract_adapter.rb:717:in `block in log'

خطأ 2:

Message (15 copies reported)

Failed to handle exception in exception app middleware : PG::InvalidTextRepresentation: ERROR:  invalid input syntax for type inet: ""
LINE 1: ..._addresses".* FROM "screened_ip_addresses" WHERE ('' <<= ip_...
                                                             ^


Backtrace

rack-mini-profiler-2.0.1/lib/patches/db/pg.rb:69:in `exec_params'
rack-mini-profiler-2.0.1/lib/patches/db/pg.rb:69:in `exec_params'
activerecord-6.0.1/lib/active_record/connection_adapters/postgresql_adapter.rb:672:in `block (2 levels) in exec_no_cache'
activesupport-6.0.1/lib/active_support/dependencies/interlock.rb:48:in `block in permit_concurrent_loads'
activesupport-6.0.1/lib/active_support/concurrency/share_lock.rb:187:in `yield_shares'
activesupport-6.0.1/lib/active_support/dependencies/interlock.rb:47:in `permit_concurrent_loads'
activerecord-6.0.1/lib/active_record/connection_adapters/postgresql_adapter.rb:671:in `block in exec_no_cache'
activerecord-6.0.1/lib/active_record/connection_adapters/abstract_adapter.rb:718:in `block (2 levels) in log'
/usr/local/lib/ruby/2.6.0/monitor.rb:235:in `mon_synchronize'
activerecord-6.0.1/lib/active_record/connection_adapters/abstract_adapter.rb:717:in `block in log'

لقد أنشأت هذا المستخدم قبل لحظة، لذا أتساءل ما إذا كانت هذه مشكلة تتعلق بمستويات الثقة بطريقة ما، أو شيء آخر غير مصرح به بالكامل للمستخدم الجديد. سأقوم ببعض التجارب مع ذلك لأرى ما إذا كان بإمكاني معرفة ما سيسمح للمنشور بالمرور عبر API.

ولكن على أي حال، يبدو أنني قد كشفت عن نوع من الأخطاء… ؟!

إليك كيف تمكنت من تجاوز الخطأ… بدأت بالتجربة:

  1. قمت بتغيير المستخدم في استدعاء الـ API إلى ‘system’ وعمل ذلك.
  2. لذا فكرت، هاه، وقمت بتغيير المستخدم إلى مستخدم ثالث لم أجربه من قبل. وعمل ذلك أيضًا.
  3. ثم عدت بتغيير المستخدم إلى اسم المستخدم الأصلي، مما يعني أنني كنت سأكرر استدعاء الـ API تمامًا الذي تسبب في خطأ 500 من قبل. إلا أنه هذه المرة عمل. :man_shrugging:t2:

إذا ظهر مرة أخرى، سأحاول معرفة كيفية إعادة إنتاجه بشكل موثوق. وفي غضون ذلك، ربما يمكن لشخص يعرف كود Discourse أفضل مني :wink: أن يلقي نظرة على ذلك الجزء ويتأكد من عدم وجود أخطاء واضحة تقفز للعين.

هه، هناك بالتأكيد شيء غريب يحدث هنا. لقد شغّلت سكريبتي مرة أخرى، وبدأ في إنشاء مواضيع بكميات كبيرة ونشر رسائل… ومرة أخرى، هذه المرة مع مستخدم مختلف، أحصل على خطأ 500.

لقد أخذت اسم المستخدم هذا إلى Insomnia واختبرت الـ API… كما هو متوقع، خطأ 500. غيّرت إلى ‘system’ فتمت العملية بنجاح… ثم عدت إلى اسم المستخدم الأصلي وفشل مرة أخرى بخطأ 500!

المستخدم الذي يتسبب في خطأ 500 (وجميع المستخدمين الذين تم استيرادهم ما عدا ‘system’) هم من المستوى TL1، وقد قمت بتغيير الحدود بحيث يمكن للمستخدمين من المستوى TL1 النشر و…

يشعر هذا بالتأكيد بأنه يتعلق بحدود المعدل (rate limiting) بطريقة ما؟ أشك في أن nginx أو شيء آخر في السلسلة قد يكون هو سبب خطأ 500، لكنني أزلت جميع حدود المعدل من nginx، وخطأ 500 يظهر بوضوح على أنه خطأ SQL في سجلات الأخطاء.

لاحظت أن خطأ SQL يتعطل حول عمود يشير إلى عنوان IP… هل يمكن أن يكون هناك شيء غريب يحدث حول كيفية عمل حدود المعدل قد يسبب هذه المشكلة؟ لقد حاولت تسجيل الدخول باستخدام VPN (لتغيير عنوان IP) لكنني ما زلت أحصل على خطأ 500.

في الوقت نفسه، أرى أن المشكلة تكمن في rack-mini-profiler وهو ليس ضروريًا. دعنا نرى ما إذا كان بإمكاني إيقاف تشغيله وما إذا كان ذلك سيحل المشكلة. … لا. الآن لم أعد أرى الـ mini-profiler في حساب المسؤول، لكنني ما زلت أحصل على نفس خطأ HTTP 500 مع نفس الأخطاء في سجل الأخطاء :frowning:

تحديث: بتغيير المستخدم إلى TL3، يبدو أن المشكلة تختفي. العودة إلى TL1 تسبب في حدوث المشكلة مرة أخرى. أنا أختبر هذه النظرية الآن… لا. أحيانًا يعمل ذلك، لكن في أحيان أخرى حتى لو قمت بتعديل مستوى المستخدم (TL) يدويًا، يبدو أن المستخدم “عالق”.

@blake أو أي شخص آخر من @staff… مساعدة! :wink: ما الذي يتبقى لنجربه؟ هل هناك شيء يمكنني تفريغه بالكامل هنا (متعلق بالكود الذي يسبب الخطأ) وإعادة ضبطه على الإعدادات الافتراضية؟

يبدو لي أن هناك مشكلة محتملة في الشبكة، فبناءً على تتبع المكدس، لا يتم أحيانًا رؤية عنوان IP للمستخدم، بطريقة ما؟ أم أن لديك بيانات قاعدة بيانات سيئة في لوحة الإدارة، أو السجلات، أو عناوين IP التي تم فحصها؟

متفق - <<= تعني “المحتوى في الطرف الأيسر موجود ضمن الطرف الأيمن”، لذا يبدو أن عنوان IP فاسد أو فارغ يصل إلى التطبيق.

(أنا مهتم بكيفية عدم توفر أي عنوان IP للتطبيق؛ تخميني الوحيد هو أن الطلب يأتي عبر منفذ يونكس دون وجود رأس معلومات IP مُوجَّه)

أتفق معك، لكنني لا أستطيع فهم سبب وجود عنوان IP أحيانًا وغيابه أحيانًا أخرى. في سكريبت الاستيراد الخاص بي، أحصل على خطأ 500 بعد 5-7 مكالمات API ناجحة على الأقل قبل ذلك. وهذا هو السبب في أنني أعتقد أن هناك بيانات تالفة في قاعدة البيانات بطريقة ما.

نعم، الأمر غريب. لكن في الوقت نفسه، إذا قمت بالاستدعاء باسم مستخدم “سيء”، يفشل الأمر؛ وإذا قمت بالاستدعاء باسم “system” أو اسم مستخدم آخر، يعمل الأمر. لذا أعتقد أننا نشير إلى بيانات تالفة في قاعدة بياناتي، ربما.

هل يمكن لأحد أن يعطيني بعض أوامر سطر الأوامر Ruby البسيطة التي يمكنني كتابتها لمسح/إعادة تعيين أي جداول قاعدة بيانات قد تكون تالفة؟

لا أعتقد أن هناك أي خطأ في قاعدة بياناتك، لأن الخطأ يتعلق بالتحقق مما إذا كان هناك عنوان IP فارغ في جدول عناوين IP المفلترة، وليس بوجود عناوين IP فارغة في قاعدة بياناتك.

المثير للاهتمام هنا هو أن هذا الكود يتحقق من وجود عنوان IP قبل إجراء طلب SQL الذي يتسبب في الخطأ 500.

هل يمكنك وصف كيفية إعداد مثيل Discourse لديك؟ هل تم استيراده من ملف استيراد؟ هل أنت على أحدث إصدار؟ كم ذاكرة عشوائية (RAM) يحتوي عليه؟ هل اتبعت هذا الدليل؟

هل يمكنك التحقق من إعداد الموقع هذا: max new accounts per registration ip؟ لست متأكدًا من أهميته في هذه الحالة، لكن ربما يكون هو السبب في المشكلة.

في سكريبت الدفعة الخاص بك، هل يمكنك إبطائه وإضافة وقفة لمدة ثانية واحدة بين الطلبات ورؤية ما إذا كان ذلك يحدث فرقًا؟

ما هو الغرض من هذه استدعاءات API؟ إذا كان الأمر يتعلق بسكريبت لمرة واحدة لبناء مستخدمين ومنشورات مستوردة، فربما يكون سكريبت استيراد يعمل على الخادم الفعلي دون إجراء استدعاءات API هو الخيار الأفضل؟

آسف على كل هذه الأسئلة، لكننا لم نواجه هذه المشكلة من قبل، ولم يتم تحديث الكود المحيط بـ screened_ip_addresses منذ فترة طويلة. لا أقول إننا لا نواجه خطأً في قاعدة الكود في مكان ما، ولكن بعد نظرة سريعة، لا يبدو أن هناك أي شيء بارز في الوقت الحالي.

بالتأكيد — إنه يعمل بالكامل عبر Docker، على Digital Ocean. لقد اتبعت ذلك الدليل الممتاز حرفيًا.

بالتأكيد، لقد قمت للتو بتغيير هذا الإعداد من القيمة الافتراضية 3 إلى 99999. لا فرق، لا يزال الخطأ 500 يظهر.

جربت ذلك، لا فرق. تجدر الإشارة إلى أنني ما زلت أحصل على خطأ 500 مع حساب “سيء” واحد فقط، من خلال Insomnia. لذا في هذه المرحلة، يبدو وكأن هذا الحساب واحد ملوث، وحتى لو قمت فقط بإجراء استدعاء واجهة برمجة التطبيقات “إنشاء رسالة” واحد به (بدون أي استدعاءات قبل أو بعده)، ما زلت أحصل على خطأ 500. لكن نعم، سكريبت الاستيراد الخاص بي يحصل أيضًا على خطأ 500 :wink:

نعم، أنا مبرمج ذو خبرة لكنني لا أعرف RoR/Ruby على الإطلاق، لذا لا يمكنني استخدام الخيارات الجاهزة التي توفرونها، على الرغم من أنني أدرك أنها على الأرجح متفوقة على قيامي يدويًا باستعراض منتدياتي الحالية وإنشاء مستخدمين، وما إلى ذلك، عبر واجهة برمجة التطبيقات. لذا منشوري في سوق التطبيقات… سأحب جدًا أن يعمل كل هذا بنفسي، لكن لدي أيضًا موعد نهائي صارم :wink:

أتفهم ذلك تمامًا، وأقدر اهتمامك بهذا الأمر.

إذن، إليك شيء يمكنني تقديمه وقد يساعد كثيرًا: بما أن التثبيت جاهز، ولم أقم بأي تخصيص يذكر، وَأن الخلل قابل للتكرار بسهولة دون الحاجة إلى كودي (فقط استخدم Insomnia)، وَلَمْ أقوم بعد بإطلاق المنتديات، فيمكنني إعطاؤك تسجيل الدخول الجذري (root) لمثيل Digital Ocean، ومفتاح واجهة برمجة التطبيقات الخاص بي، وما إلى ذلك، ولا أمانع على الإطلاق في أن تتدخل هناك. منتديات Discourse الخاصة بي حاليًا عبارة عن عدد كبير من التصنيفات الفارغة وبعض رسائل المقدمة الخاصة الأخرى التي قمنا بإعدادها، لكنها في الأساس فارغة ولا يوجد فيها مستخدمون حقيقيون بعد (فقط المشرفون). لذا لا بأس إذا أردت اختبار الأشياء، وإنشاء/حذف مواضيع ورسائل، وما إلى ذلك.

سيكون هذا بالتأكيد أسرع طريقة لرؤية الخلل بنفسك عن قرب. وبما أنك ستكون هناك بصفتك root، فيمكنك أيضًا التلاعب بأي أشياء منخفضة المستوى في Discourse تريدها، لمعرفة سبب حدوث هذا.

E

ماذا يحدث إذا قمت بتعطيل SSO؟

لا يوجد فرق، ما زال يظهر خطأ 500.

فكرة أخرى: هل من الممكن أن كل حساب نجح معه يكون مسؤولاً؟ أعرف أن بعض حدود المعدل على مستوى التطبيق يتم تجاوزها للمسؤولين.

بشكل عام، ومع ذلك، من الأسهل بكثير ببساطة كتابة سكريبت استيراد بدلاً من ذلك. هذا الموضوع بأكمله هو ما يبدو عليه “كثير” :wink:

ممم، ظننت أنني وجدت شيئًا… المستخدم “المسمّم” الذي كنت أختبره، وهو george21، كان في مستوى TL0. لذا قمت بتغييره إلى TL1 وعندها عمل الأمر. حسنًا! ربما هذا هو السبب! لذا عدت بتغيير george21 إلى TL0… والآن لم يعد “مسمّمًا” — يمكنه إجراء استدعاء API حتى كمستوى TL0.

الآن سأشغل سكريبت الاستيراد مرة أخرى، واها! الآن george21 يُرجِئ خطأ 500 في سكريبت الاستيراد. وعندما أحاول ذلك في Insomnia، يفشل. لذا سأعيد george21 إلى TL1 و… نعم، يمكنه تنفيذ استدعاء HTTP.

إذن إليك ما يبدو أنني أستطيع تكراره:

  1. إذا تم إجراء سلسلة من استدعاءات API (?) فإن ذلك يتسبب بطريقة ما في فشل استدعاء API لاحق لمستخدم TL0.
  2. تغيير مستخدم TL0 إلى TL1 يسمح باستدعاء API بالمرور.
  3. وغريبًا، حتى بعد تغيير نفس المستخدم مرة أخرى إلى TL0، لا يزال استدعاء API يمر.
  4. تشغيل السكريبت مرة أخرى يعمل بشكل جيد حتى يفشل مرة أخرى مع مستخدم TL0 آخر.

لاحظ ما يلي:

  1. soweit أعرف، تم رفع جميع الحدود الدنيا وغيرها لمستوى TL0 (أي أنني حاولت إزالة كل عائق قد يمنع مستخدم TL0 من النشر)، و
  2. حتى لو كانت هذه مشكلة تتعلق بنوع من حدود المعدل الداخلية لمستخدمي TL0، فإن API لا ينبغي أن يُرجِئ خطأ 500 ويضع خطأ SQL في سجل الأخطاء. لذا أعتقد أنه يمكننا القول في هذه المرحلة أن هناك عيبًا ما في مكان ما بالتأكيد.

نعم، أمم، أعرف، وقد شرحتُ سبب عدم كتابة سكريبت استيراد خاص بي (بناءً على الأمثلة المقدمة) أربع مرات بالفعل. :wink: ومن هنا جاء تغيير نهجي.

وفي الوقت نفسه، أستمر في المساهمة هنا للمساعدة في العثور على هذا العيب وإصلاحه. اليوم يؤثر على سكريبت الاستيراد الخاص بي. غداً قد يؤثر على سكريبت مهم آخر لديك على موقعك يحتاج إلى API…