الأداء
لماذا يبطئ الـ NestJS API عندك
رقمان يفسّران معظم الأمر: كم query نفّذ الطلب الواحد، وكم استغرق أبطؤها. 60 query صغيرة مشكلة بنية لا مشكلة index.
"الموقع بطيء" ليست تشخيصاً، بل شكوى. أول ما يجب أن تعرفه هو أين يضيع الوقت: قبل وصول أول بايت من الخادم، أم بعده في المتصفح. القياسان مختلفان تماماً، وعلاجهما مختلف تماماً، وأكثر ما يُهدر من وقت في تسريع المواقع يُهدر في علاج الطرف الخطأ.
curl -o /dev/null -s -w 'dns:%{time_namelookup} connect:%{time_connect} ttfb:%{time_starttransfer} total:%{time_total}\n' https://example.com/الرقم الذي يهمّك هنا هو time_starttransfer، أي زمن أول بايت. إذا كان تحت 300 ميلي ثانية فمشكلتك في الواجهة الأمامية: صور ثقيلة، ملفات CSS تحجب العرض، خطوط تُحمَّل من نطاق ثالث. وإذا كان ثانية أو أكثر فالمشكلة داخل PHP وقاعدة البيانات، ولن يصلحها ضغط الصور مهما بالغت فيه.
كرّر القياس على صفحة رئيسية، وصفحة مقال، وصفحة أرشيف، ومرّة وأنت مسجّل الدخول. الفرق بين هذه الأربعة يقول لك الكثير: ذاكرة الصفحات تخدم الزائر المجهول ولا تخدم المستخدم المسجّل، فإذا كانت الصفحة سريعة للزائر وبطيئة لك فأنت تنظر إلى زمن التوليد الحقيقي، غير مغطّى بذاكرة مؤقتة.
ثبّت Query Monitor على نسخة تطوير أو فعّله لمستخدم مدير واحد فقط. هو الأداة الوحيدة التي تربط استعلاماً بطيئاً بالإضافة التي أطلقته، وهذه الرابطة هي كل التشخيص. ما تبحث عنه ثلاثة أرقام: عدد الاستعلامات، وزمنها الكلي، وأبطأ استعلام مفرد. صفحة سليمة في ووردبريس تنجز عملها في عشرات قليلة من الاستعلامات. إذا رأيت 400 استعلاماً في صفحة واحدة فأنت أمام إضافة تستدعي get_post_meta داخل حلقة دون تحميل مسبق.
جدول wp_options المتضخّم. كل طلب صفحة يحمّل صفوف autoload كاملة قبل أن يبدأ أي عمل مفيد:
SELECT SUM(LENGTH(option_value)) AS bytes
FROM wp_options WHERE autoload IN ('yes','on');النتيجة الصحية أقل من مئة كيلوبايت. رأيت مواقع تحمّل ثمانية ميغابايت في كل طلب لأن إضافة قديمة كتبت سجلّاتها في خيار تلقائي التحميل. اعرف أكبر الصفوف بالاسم قبل أن تحذف شيئاً، وافحص أيضاً حجم العوابر المنتهية (transients):
SELECT option_name, LENGTH(option_value) AS bytes
FROM wp_options WHERE autoload IN ('yes','on')
ORDER BY bytes DESC LIMIT 20;استعلامات meta_query. جدول wp_postmeta مفهرس على meta_key لا على meta_value. أي فرز أو ترشيح على القيمة يعني مسحاً كاملاً للجدول، ويزداد سوءاً خطّياً مع نمو المحتوى. متاجر ووكومرس تصطدم بهذا أولاً. الحل ليس فهرساً جديداً في الغالب، بل نقل الحقل الذي تُرشّح عليه إلى تصنيف (taxonomy) أو جدول خاص.
wp-cron على كل طلب. ووردبريس افتراضياً يفحص مهامه المجدولة في كل زيارة، ومهمة ثقيلة واحدة تعني أن زائراً عشوائياً هو من يدفع ثمنها. عطّله واستبدله بمهمة نظام حقيقية:
// wp-config.php
define('DISABLE_WP_CRON', true);*/5 * * * * cd /home/user/public_html && wp cron event run --due-nowadmin-ajax.php ونبض لوحة التحكم. إذا كانت لوحة التحكم وحدها هي البطيئة، افتح تبويب الشبكة وانظر إلى الطلبات المتكرّرة. إضافات كثيرة تستطلع الخادم كل خمس عشرة ثانية بلا سبب.
طلبات HTTP خارجية داخل مسار الطلب. إضافة تتحقق من ترخيصها، أو تجلب تغريدات، أو تسأل خادم إعلانات — وكل ذلك يحدث بين ضغطة الزائر وظهور الصفحة. Query Monitor يعرض هذه الطلبات في تبويب مستقل. أي طلب خارجي متزامن هو نقطة فشل ومصدر تأخير في آن.
الترتيب مقصود. الذاكرة المؤقتة تخفي المشكلة ولا تحلّها، وموقع بطيء مع ذاكرة يبقى بطيئاً لأول زائر بعد كل تحديث، ولكل مستخدم مسجّل، ولكل صفحة لم تُسخَّن بعد. بعد أن تُصلح ما سبق، ركّبها بهذا الترتيب:
أعد قياس curl نفسه بعد كل تغيير، لا بعدها كلها. تغيير لا يتحرّك معه الرقم هو تغيير لم تكن تحتاجه، وأنت الآن تعرف ذلك بدل أن تظنّه. واحذر من اختبار الحمل على استضافة مشتركة: أدوات مثل ab وhey تُشبِع نواة المعالج المخصّصة لحسابك، فتقيس حدّ الحساب لا سرعة الموقع، وتزعج جيرانك على الخادم بلا فائدة.
الأداء
رقمان يفسّران معظم الأمر: كم query نفّذ الطلب الواحد، وكم استغرق أبطؤها. 60 query صغيرة مشكلة بنية لا مشكلة index.
الأمن
4 ثغرات في NestJS خلال 7 أشهر، وكلها ثغرة واحدة: الـ matcher والـ handler اختلفا على العنوان نفسه. مكان التحقق هو حيث تُقرأ البيانات.
SEO
يدمج Next.js الـ metadata مفتاحاً مفتاحاً، فالـ route الذي يغفل alternates يرث canonical الخاص بالـ layout. وعندنا كان يشير بكل صفحة إلى الرئيسية.