file_scan_directory () ใช้เวลาประมาณ 10 วินาทีในการดำเนินการ


10

การใช้ xhprof ฉันสังเกตว่าfile_scan_directory()ใช้เวลานานกว่า 10 วินาทีในการดำเนินการเมื่อโหลดหน้าแรก ทำไมต้องใช้เวลานานเช่นนี้?

นี่คือผลลัพธ์ของ xhprofile:

ภาพหน้าจอ


คุณไม่สามารถ "file_scan_directory" "front page" เนื่องจากหน้าแรกเป็นรายการในตารางฐานข้อมูลไม่ใช่เส้นทางของระบบไฟล์
Letharion

@ Letharion ฉันคิดว่าคุณเข้าใจผิดคำถามของฉัน ฉันหมายถึงเวลาที่ฟังก์ชั่นนี้ใช้เมื่อโหลดหน้าแรกฉันแก้ไขคำถาม
hknik

หน้าแรกมีส่วนเกี่ยวข้องกับฟังก์ชั่นการใช้เวลาเป็นพิเศษหรือไม่? คุณสแกนไดเรกทอรีใดอยู่ ในไดเรกทอรีคืออะไร?
Letharion

Aha! ฉันคิดว่าคุณเรียกฟังก์ชั่นนี้ด้วยตัวเองและสงสัยว่าทำไมคุณไม่ให้รายละเอียดเพิ่มเติม คำตอบของ Berdir ดูสมเหตุสมผลมาก :)
Letharion

คำตอบ:


14

ดูเหมือนว่าคุณกำลังได้รับผลกระทบจากปัญหาที่รู้จักกันใน Drupal 7

ส่วนใหญ่อาจจะตีคุณไดเรกทอรีโมดูลหลีกเลี่ยงการ re-สแกนเมื่อหลายโมดูลจะหายไป มันจะเกิดขึ้นหากคุณมีโมดูลที่ขาดหายไปในการติดตั้ง ลองตรวจสอบตารางระบบของคุณ:

SELECT name, filename FROM system WHERE type = 'module' AND status = 1 ORDER BY filename

และล้างโมดูลใด ๆ ที่ยังคงเปิดใช้งานอยู่ แต่หายไปจากระบบไฟล์

โดยรวมแล้ว Drupal 7 เป็นวิธีที่เป็นมิตรกับทรัพยากรและปรับขนาดได้แล้ว Drupal 6 นอกเหนือไปจากการถดถอยที่น่าเสียดายเช่นนี้

ดูฟังก์ชั่นเหล่านั้นดูเหมือนว่าจะไม่มีโมดูลหรืออาจเป็นไฟล์เดียวของโมดูล ลองดูที่drupal_get_filename ()มันเรียก drupal_system_listing () ซึ่งเรียกใช้ฟังก์ชันนี้หากไม่พบไฟล์ที่ร้องขอ เพิ่ม dpm (func_get_args ()) ก่อนที่จะเรียก drupal_system_listing () ที่ควรบอกไฟล์ที่ไม่พบ


ไม่เลย (!) ไม่มีโมดูลหายไปจากระบบไฟล์
hknik

จากนั้นคุณต้องติดตามว่าการโทรมาจากที่ใดบางทีโมดูลที่กำหนดเองหรือ contrib กำลังทำอะไรผิดพลาด คลิกผ่านฟังก์ชั่นหลักของ file_scan_directory () และอัพเดตการโพสต์เริ่มต้นด้วยรายการฟังก์ชั่นหลัก
Berdir

มองไปที่ฟังก์ชั่นเหล่านั้นก็จะมีลักษณะเหมือนมันหายไปโมดูลหรืออาจจะเป็นไฟล์เดียวของโมดูล มีลักษณะที่ drupal_get_filename A: api.drupal.org/api/drupal/includes!bootstrap.inc/function/... มันจะเรียกฟังก์ชั่นถ้ามันไม่สามารถหาไฟล์ที่ร้องขอ เพิ่ม dpm (func_get_args ()) ก่อนที่มันจะเรียก drupal_system_listing () ที่ควรบอกคุณว่าฟังก์ชั่นใดที่ไม่ได้ค้นหา
Berdir

@Berdir ความคิดเห็นล่าสุดของคุณควรอยู่ในคำตอบของคุณเนื่องจากมีความเกี่ยวข้อง
kiamlaluno

ลิงก์ไปยัง "ปัญหาที่ทราบใน Drupal 7" และ "หลีกเลี่ยงไดเรกทอรีโมดูลการสแกนซ้ำ" จะใช้งานไม่ได้ ทั้งคู่เป็นคำตอบในอดีตของ stackexchange ใครบ้างมีการอ้างอิงอื่น ๆ ?
rfay

4

มีสาเหตุหลายประการที่ทำให้ปัญหานี้เกิดขึ้นและเพื่อความผิดหวังที่ยิ่งใหญ่ของฉันตอนนี้ฉันพบว่าตัวเองค่อนข้างมีความรู้เกี่ยวกับเหตุผลเหล่านั้น ถ้าคุณเพิ่งสังเกตเห็นปัญหานี้หลังจากอัพเกรด Drupal core ไปที่ 7.33+ นี่อาจเป็นความผิดพลาดในโมดูลใด ๆ แม้ว่าคุณจะไม่ได้อัพเกรดโมดูลนั้นก็ตาม

โมดูลถูกลบออกจากฐานรหัส

คุณอาจต้องการตรวจสอบข้อผิดพลาดที่รู้จักที่ @Berdir กล่าวถึงโดยเฉพาะหากคุณเพิ่งลบโมดูล "ไม่ได้ใช้" ออกจากฐานรหัส หากต้องการทราบว่าคุณมีโมดูลที่เปิดใช้งาน แต่ถูกลบออกจากระบบไฟล์คุณสามารถเรียกใช้สคริปต์เช่นที่กล่าวถึงที่นี่หรือใช้ทุ่นระเบิดซึ่งเขียนขึ้นสำหรับการติดตั้งแบบหลายไซต์บนระบบที่มี drush เพื่อให้ทำงานได้ จากไดเรกทอรีฐาน Drupal:

find sites -maxdepth 1 -iname '*.*' -type d | sed -rne 's:sites/(.+):echo \1; drush @\1 sqlq "select filename from system where status = 1" | grep "/" | sed -rne "s_(.+)_test -f \\1 || echo \\1_p" | bash:p' | bash

หรือต่อไปนี้:

while read -r file; do [ -f "$file" ] || echo "$file is missing."; done < <(drush sqlq "SELECT filename FROM system WHERE status = 1")

หากคุณพบโมดูลที่ถูกลบออกจากฐานรหัสให้ทำตามคำแนะนำในประเด็นที่ @Berdir พูดถึง

ข้อผิดพลาดในการเข้ารหัส

หากไม่ใช่สิ่งนั้นสถานการณ์ของคุณน่าจะเกิดจากข้อผิดพลาดในการเขียนโค้ดเช่นไฟล์ที่ถูกลบออก แต่ยังคงถูกเพิ่มโดยการเรียก drupal_add_js (จากความคิดเห็นที่ 19 ในปัญหา # 1082892) หรือพิมพ์ผิดที่โชคร้ายในโมดูลหรือธีม เช่นimagecache_actions(ดูhttps://drupal.org/node/2381357 )

ไม่ว่าในกรณีใดหากต้องการทราบสาเหตุที่เกิดขึ้นคุณต้องทราบว่าไฟล์ใดที่ Drupal หาไม่เจอ ดังนั้นตามความคิดเห็น Berdir คุณสามารถตัดชั่วคราวdrupal_get_filenameในโดยการเพิ่มบันทึกข้อความหรือโทรก่อนที่จะเรียกร้องให้bootstrap.inc drupal_system_listing()หากคุณติดตั้งโมดูล Devel ไว้dpmจะสามารถใช้งานได้ ถ้าไม่คุณสามารถใช้drupal_set_messageหรือ syslog ตัวอย่าง:

dpm(func_get_args());
drupal_set_message(implode(', ', func_get_args()));
syslog(LOG_WARNING, implode(', ', func_get_args()));

เมื่อคุณรู้ว่า Drupal กำลังมองหาอะไรมันเป็นโอกาสที่ดีที่คุณจะสามารถรู้ได้ว่าจะไปที่ไหนจากที่นั่น ปัญหาของฉันเกิดจากการเรียกเพื่อรวมไฟล์จากโมดูลที่ไม่มีอยู่imagcache_actions(หมายเหตุการพิมพ์ผิด) ดังนั้นฉันค้นหาimagecache_actionsใน codebase ของฉัน (เช่นgrep -r imagcache_actions .) และพบว่ารุ่น 1.4 ของการimagecache_canvasactions.moduleใช้งาน module_load_include นอกการเรียกใช้ฟังก์ชันใด ๆ ในขอบเขตไฟล์ด้วยการพิมพ์ผิด อีกครั้งข้อผิดพลาดนี้จะปรากฏเฉพาะหลังจากอัปเดตเป็น Drupal 7.33+ ฉันพบว่าปัญหาได้ถูกสร้างขึ้นเพื่อimagecache_actionsใช้งานโปรแกรมแก้ไขและกลับมาทำธุรกิจอีกครั้ง


2

ฉันมีปัญหาที่คล้ายกันมาก - file_scan_directory()กำลังฆ่าไซต์ เปิดnode_modulesโฟลเดอร์huuge ที่ฝังอยู่ในชุดรูปแบบที่ฉันกำหนดสำหรับgulpการสแกนแคชแต่ละครั้ง การย้ายไฟล์เหล่านี้ออกจากโฟลเดอร์ธีม (และอัปเดตบางพา ธ ในไฟล์ gulpfile ของฉัน) ดูเหมือนจะแก้ไขให้ฉัน อีกวิธีหนึ่ง: ฉันคิดว่าคุณสามารถแฮ็คfile.inc:

'nomask' => '/(\.\.?|CVS|node_modules)$/', // https://www.drupal.org/node/2329453#comment-9360519


0

file_scan_directory()เป็นฟังก์ชันเวียนซึ่งไฟล์ทั้งหมดที่ตรงกับไดเรกทอรีที่กำหนด มันใช้is_dir()และopendir()โทร PHP ซึ่งอาจแพงที่สุดในแง่ของการโทรระบบ I / O Bootstrap แบบง่ายของ Drupal (เช่นtime drush ev "") อาจใช้file_scan_directoryเวลาสองสามพันครั้ง (ขึ้นอยู่กับความซับซ้อนของลำดับชั้นโฟลเดอร์ Drupal เช่นจำนวนโมดูลและโฟลเดอร์)

ในกรณีของฉันฉันมี ~ 1500 โทรไปfile_scan_directory(24 วินาทีรวมประกอบด้วย 2 สายจากdrupal_system_listingในcommon.incนั้นสายอื่น ๆ ที่แยกจากสายเรียกซ้ำไปfile_scan_directoryมันด้วยตัวเอง

เพื่อปรับปรุงประสิทธิภาพการโทร I / O คุณจะต้องใช้การแคชไฟล์ สิ่งนี้สามารถทำได้โดยการติดตั้งและเปิดใช้งาน OPCache ( opcache.enable=1) และปรับแต่งการตั้งค่า (ดู: วิธีการใช้ PHP OPCache? ) แนะนำให้ใช้การแคชตามหน่วยความจำเช่น memcached / Redis

เมื่อใช้ติดต่อบรรทัดคำสั่ง (เช่นdrush) opcache.enable_cli=1คุณควรเปิดใช้งาน

หลังจากการเปลี่ยนแปลงคุณสามารถตรวจสอบ syscalls ที่มากขึ้นโดยใช้ debuggers ที่มีอยู่

เช่น

  • บน Linux ใช้strace(กดCtrl- Cเพื่อเสร็จสิ้น):

    sudo strace -c -fp $(pgrep -n php)
  • ใน Unix ใช้dtrace(ใช้โพรบคง DTrace ของ PHP ), เช่น

    sudo dtrace -n 'inline string NAME = "php"; syscall:::entry /(NAME == strstr(NAME, execname)) || (execname == strstr(execname, NAME))/ { @num[probefunc] = count(); }'

คุณสามารถพิจารณาการเพิ่มประสิทธิภาพdrupal_system_listing()หรือfile_scan_directory()ดำเนินการแคชแบบคงที่เช่น

--- a/includes/file.inc
+++ b/includes/file.inc
@@ -2104,6 +2104,8 @@ function file_download_access($uri) {
  *   'filename', and 'name' members corresponding to the matching files.
  */
 function file_scan_directory($dir, $mask, $options = array(), $depth = 0) {
+  static $dirs = array();
+
   // Merge in defaults.
   $options += array(
     'nomask' => '/(\.\.?|CVS)$/',
@@ -2120,7 +2122,12 @@ function file_scan_directory($dir, $mask, $options = array(), $depth = 0) {
       if (!preg_match($options['nomask'], $filename) && $filename[0] != '.') {
         $uri = "$dir/$filename";
         $uri = file_stream_wrapper_uri_normalize($uri);
-        if (is_dir($uri) && $options['recurse']) {
+
+        if (empty($dirs[$uri])) {
+          $dirs[$uri] = is_dir($uri);
+        }
+
+        if ($dirs[$uri] && $options['recurse']) {
           // Give priority to files in this folder by merging them in after any subdirectory files.
           $files = array_merge(file_scan_directory($uri, $mask, $options, $depth + 1), $files);

หรือแคชfile_scan_directoryโทรออกจากdrupal_system_listing()นั้นตรวจสอบแพทช์ต่อไปนี้มีอยู่ที่: file_scan_directory ควรจะเก็บไว้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.