إذا قمت باستدعاء file_exists($path) وكان $path يشير إلى حاوية Amazon S3، فأنت لا تتعامل مع قرص محلي، بل تقوم بإجراء طلب عبر الشبكة. هذا الفحص المكون من سطر واحد يتحول إلى رحلة ذهاب وإياب (round-trip) إلى S3، مما قد يضيف عشرات الملي ثانية إلى كل طلب.
تقوم PHP بإخفاء المخزن البعيد خلف "stream wrappers". وتمر الوظائف مثل fopen() و file_exists() و is_dir() و unlink() و file_put_contents() عبر هذه الأغلفة، التي تترجم الاستدعاءات إلى عمليات S3 API. عملية الربط مباشرة:
file_exists()← HeadObjectis_dir()← ListObjectsunlink()← DeleteObjectfile_put_contents()← PutObject
يتسبب كل استدعاء في نفس زمن التأخير (latency) الخاص بطلب S3 API المقابل. عندما يقوم نص برمجي (script) بفحص عشرات الملفات، فإنه ينشئ نمطًا من نوع N+1: قفزة شبكية واحدة لكل عملية فحص.
لماذا يهم هذا الأمر الآن
في بيئة التطوير النموذجية، يكون نظام الملفات موجودًا على نفس الجهاز، لذا يعود الفحص بالنتيجة فورًا تقريبًا. أما في بيئة الإنتاج (production)، حيث توجد الأصول (assets) على S3، فإن الكود نفسه يتسبب في انهيار مفاجئ في الأداء (performance cliff). يقوم AWS SDK بتخزين بعض النتائج في الذاكرة، مما يجعل عمليات الفحص المتكررة في عملية PHP واحدة تبدو سريعة. ومع ذلك، فإن معظم عمليات نشر PHP تقوم بإنشاء عملية جديدة لكل طلب ويب، مما يؤدي إلى مسح التخزين المؤقت (cache) في كل مرة. النتيجة: رحلة ذهاب وإياب كاملة عبر الشبكة لكل مسار مختلف في كل طلب.
استغرق موقع WordPress ذات مرة عشر ثوانٍ لعرض صفحته الرئيسية. كانت استعلامات قاعدة البيانات سريعة، لكن الصفحة أطلقت 73 استدعاءً فرديًا لـ S3 فقط لتجميع المحتوى. كان كل استدعاء عبارة عن طلب بارد (cold request)، مما حول file_exists() التي تبدو غير ضارة إلى تأخير ملحوظ.
استراتيجيات التخفيف
- حفظ التخزين المؤقت عبر الطلبات – انقل التخزين المؤقت الخاص بالعملية إلى مخزن مشترك مثل Redis. عندما يكتشف طلب ما أن مفتاحًا موجودًا على S3، تقرأ الطلبات اللاحقة النتيجة المخزنة مؤقتًا بدلاً من الاتصال بـ S3 مرة أخرى.
- اعتماد سير عمل يعتمد على الملفات المحلية أولاً – اكتب الملفات إلى قرص محلي، ثم قم بمزامنتها مع S3 بشكل غير متزامن (asynchronously). مسار الطلب الرئيسي يلمس نظام الملفات المحلي فقط؛ وتنتقل تكلفة الشبكة إلى وظيفة خلفية (background job).
- مراجعة الكود المصدري – ابحث بشكل منهجي عن وظائف نظام الملفات التي قد تؤدي إلى غلاف بعيد (remote wrapper). قم بتمييز أي منها يظهر داخل حلقات تكرارية ضيقة (tight loops) أو مسارات حرجة للطلب.
- فحص بيانات APM – إذا حدث ارتفاع مفاجئ في وقت الاستجابة بينما ظلت مقاييس قاعدة البيانات مستقرة، فقم بالتدقيق في توقيتات SDK التخزين. غالبًا ما يشير الارتفاع المفاجئ في زمن تأخير SDK إلى استدعاءات S3 مخفية.
الخلاصة: الوظيفة التي تبدو وكأنها فحص محلي يستغرق ميكروثانية يمكن أن تخفي عشرات الملي ثانية من زمن تأخير الشبكة. تعامل مع file_exists() على S3 كاستدعاء خارجي، وقم بتخزين نتيجته مؤقتًا، وانقل العمليات الثقيلة بعيدًا عن مسار الطلب. وإلا، فإن زمن التأخير المخفي سيستمر في إبطاء موقعك، عبر كل طلب شبكة غير مرئي.
