حسنًا، قد لا يكون هذا “خطأ” بحد ذاته وقد يكون مصممًا بهذه الطريقة، ولكن لدي بعض الاستفسارات حول كيفية تحديث الحضور على جانب جافاسكريبت. الملف app/assets/javascripts/discourse/app/services/presence.js
ما فهمته حتى الآن هو أن عميل جافاسكريبت:
سيقوم بتحديد وتيرة التحديث إلى مرة واحدة في الثانية عندما تكون قائمة التحديثات غير فارغة
ثم طلب واحد كل 30 ثانية عندما لا تكون هناك تغييرات
ما لاحظته هو أنه إذا حدث خطأ في الخادم لأي سبب، فسيستمر جافاسكريبت في إرسال طلبات كل ثانية. كما أنه ينشئ أخطاء في وحدة التحكم Uncaught (in promise) السطر 565.
ألا ينبغي أن يتوقف عند نقطة ما بعد N أخطاء لتجنب إرهاق الخادم؟
كيفية إعادة الإنتاج؟
قم بمحاكاة خطأ في طريقة update في presence_controller.rb. (في حالتي، قمت بعرض الحالة 405 لمجرد العرض التوضيحي)
def update
# عميل جافاسكريبت مصمم لتحديد وتيرة الطلبات إلى طلب واحد في الثانية
# عندما لا يتم إجراء أي تغييرات، فإنه يقوم بطلب واحد كل 30 ثانية
RateLimiter.new(nil, "update-presence-#{current_user.id}", 20, 10.seconds).performed!
render json: { error: "Not authorized" }, status: 405
return
...
ثم على العميل، انتقل إلى أي شاشة تحتوي على إدارة الحضور. اخترت شاشة الرد على الموضوع وبدأت في الرد.
حتى بدون التحديث المجدول كل 30 ثانية، إذا كان هناك أي خطأ من الواجهة الخلفية، فسيحاول الخوارزمية مرة أخرى كل ثانية لأن قائمة الانتظار للأحداث لن تكون فارغة:
في _scheduleNextUpdate
} else if (this._queuedEvents.length > 0) {
this._cancelTimer();
cancel(this._debounceTimer);
this._debounceTimer = debounce(
this,
this._throttledUpdateServer,
this._presenceDebounceMs // أعتقد أن هذا هو ثانية واحدة
);
في الواقع، في طريقة _updateServer → يتم إعادة وضع الأحداث في قائمة الانتظار عند حدوث خطأ:
} catch (e) {
// Put the failed events back in the queue for next time
this._queuedEvents.unshift(...queue);
كان هذا مجرد شيء لاحظته لاحقًا… عندما تسير الأمور على ما يرام (مما يعني عدم وجود أخطاء)، هل من الضروري حقًا الاستمرار في تحديث الخادم كل 30 ثانية؟
} else if (
!this._nextUpdateTimer &&
this._presentChannels.size > 0 &&
!isTesting()
) {
this._nextUpdateTimer = discourseLater(
this,
this._throttledUpdateServer,
PRESENCE_INTERVAL_S * 1000 // هذا هو 30 ثانية
);
}
حسناً، أعتقد أنني فهمت لماذا يحتاج إلى “تسجيل الدخول” مرة أخرى كل 30 ثانية.
هناك مهمة في الخلفية يتم تشغيلها كل دقيقة PresenceChannelAutoLeave والتي ستجعل الأعضاء المنتهين صلاحيتهم يغادرون القناة.
كما تم التعليق عليه في presence_channel.rb لطريقة present(user:, client_id:):
# ضع علامة على عميل المستخدم على أنه موجود في هذه القناة. يجب أن يكون client_id فريدًا لكل
# علامة تبويب في المتصفح. يجب استدعاء هذه الطريقة بشكل متكرر (مرة واحدة على الأقل كل DEFAULT_TIMEOUT)
# أثناء وجود المستخدم في القناة.
def present(user:, client_id:)
لا أزال أتساءل عن جزء الخطأ الذي يتم تشغيله كل ثانية.
كنا نتعامل بالفعل مع أخطاء 429 ‘محدود المعدل’ بشكل صحيح. ولكن بالنسبة للأخطاء الأخرى غير المتوقعة، أضفت بعض منطق التراجع الأسي (بحد أقصى 30 ثانية). شكراً على لفت انتباهنا @AhmedLoud