Score:1

ฉันจะแก้ไข "ขนาดหน่วยความจำที่อนุญาตหมดแล้ว" เมื่อแบตช์เสร็จสิ้นได้อย่างไร

ธง cn

ชุดประมวลผลรายการทั้งหมด แต่แทนที่จะแสดงข้อความเสร็จสิ้น ฉันเห็นข้อผิดพลาด "ขนาดหน่วยความจำที่อนุญาตของ 536870912 ไบต์หมดแล้ว"

เมื่อฉันดีบักโค้ด ฉันสังเกตเห็นว่า Drupal โหลดบล็อกที่ประมวลผลแต่ละบล็อกเมื่อเสร็จสิ้นแบทช์ (ContentEntityBase->__สร้าง). ฉันไม่เข้าใจว่าทำไม Drupal ถึงทำเช่นนั้น

โครงสร้างของรหัส:

นี่คือโมดูลแบบกำหนดเองที่มีคลาสฟอร์มและฟังก์ชันแบทช์ใน custom_module.module ไฟล์.เมื่อส่งแบบฟอร์ม โมดูลจะเรียกฟังก์ชันสร้างแบทช์:

ฟังก์ชันสาธารณะ submitForm (อาร์เรย์ &$form, FormStateInterface $form_state) {
  custom_module_make_batch();
}

ฟังก์ชัน custom_module_build_batch รับรหัสของบล็อกแบบกำหนดเอง (4000 หรือมากกว่า) และสร้างแบทช์:

ฟังก์ชัน custom_module_make_batch()
{

  $ชุด = [];

  $items = get_blocks_ids();

  $batch = custom_module_generate_batch($รายการ);
  batch_set($แบทช์);
}

ฟังก์ชัน custom_module_generate_batch($รายการ)
{

  $การดำเนินการ = [];

  $operations_groups = array_chunk($รายการ, 50);

  foreach ($operations_groups เป็น $key => $operations_group) {
    $การดำเนินการ[] = [
      'custom_module_batch_op',
      [$operations_group],
    ];
  }

  $ ชุด = [
    'operation' => $operation,
    'เสร็จสิ้น' => 'custom_module_batch_finished',
    'title' => 'แบทช์กำหนดเอง',
    'init_message' => 'แบทช์กำลังเริ่มต้น',
    'progress_message' => 'ประมวลผล @current จาก @total ส่วน',
    'error_message' => 'แบทช์พบข้อผิดพลาด',
  ];
  ส่งคืน $ ชุด;
}

ฟังก์ชัน custom_module_batch_op($operations_group, &$context) {

  foreach ($operations_group เป็น $key => $bid) {
  
    $block = \Drupal::service('entity.repository')->loadEntityByUuid('block_content', $bid);

    $block->field_name = $new_value;
    
    $block->save();
  }

}

ฟังก์ชัน custom_module_batch_finished($success, $results, $operations)
{

  $messenger = \Drupal::messenger();
  ถ้า ($ สำเร็จ) {
    // ที่นี่เราสามารถทำสิ่งที่มีความหมายกับผลลัพธ์ได้
    // เราเพียงแค่แสดงจำนวนโหนดที่เราประมวลผล...

    ถ้า ($ ทั้งหมด) {
      $messenger->addMessage(t('@count ผลลัพธ์ที่ประมวลผลแล้ว', ['@count' => $total]));
    } อื่น {
      $messenger->addMessage(t('ไม่มีรายการสำหรับการย้ายข้อมูล'));
    }

  } อื่น {
    // เกิดข้อผิดพลาด.
    // $operations ประกอบด้วยการดำเนินการที่ยังไม่ผ่านการประมวลผล
    $error_operation = รีเซ็ต ($ การดำเนินการ);
    $messenger->addMessage(
      เสื้อ(
        'เกิดข้อผิดพลาดขณะประมวลผล @operation พร้อมอาร์กิวเมนต์: @args'
        [
          '@operation' => $error_operation[0],
          '@args' => print_r($error_operation[0], จริง),
        ]
      )
    );
  }
}
leymannx avatar
ne flag
`$new_value` ไม่ได้กำหนด และควรใช้ `$block->set('field_MYFIELD', $new_value)` เพื่อตั้งค่าหรือ `$block->set('field_MYFIELD', [])` เพื่อล้างข้อมูล
Egor Elkin avatar
cn flag
@leymannx ขอบคุณ โอเค ฉันจะใช้ $block->set() $new_value - เป็นเพียงตัวอย่าง มันสามารถเป็นอาร์เรย์: [ 'value' => 'ข้อความบางส่วน', 'รูปแบบ' => 'ข้อความธรรมดา', ];
apaderno avatar
us flag
ดูเหมือนว่า `get_blocks_ids()` กำลังโหลดรายการทั้งหมด ซึ่งจะอธิบายข้อความแสดงข้อผิดพลาด การเรียกกลับแบบแบตช์ดำเนินการค้นหาเพื่อรับรายการที่จำเป็นการรับรายการทั้งหมดและส่งมอบเป็นแบทช์ไม่ใช่วิธีการทำงานแบบแบทช์ เนื่องจากการทำงานแบบแบทช์ทำเพื่อหลีกเลี่ยงการใช้หน่วยความจำที่มีอยู่ทั้งหมด และหลีกเลี่ยงการหมดเวลาเนื่องจาก PHP ใช้เวลาในการจัดการคำขอมากกว่าที่ได้รับมอบหมาย เวลา.
Score:5
ธง us

การดำเนินการแบบแบตช์จะใช้เมื่อไม่ทราบจำนวนรายการที่ต้องจัดการ และอาจมีรายการจำนวนมากที่การโหลดทั้งหมดจะใช้หน่วยความจำ PHP ที่มีอยู่ทั้งหมด หรือการจัดการจะใช้เวลามากกว่าเวลาที่ PHP ให้กับ สคริปต์ที่จะเรียกใช้ เมื่อข้อมูลถูกโหลดจากตารางฐานข้อมูล แม้แต่ข้อมูลเอนทิตี การเรียกกลับของการดำเนินการแบบแบตช์จะโหลดข้อมูล เช่น รหัสนี้ทำ เป็นต้น

$batch_builder = (BatchBuilder ใหม่ ())
  ->setTitle(t('การลบแถวฐานข้อมูล'))
  ->setFinishCallback('mymodule_finished_callback')
  ->addOperation('mymodule_delete_rows', []);

batch_set($batch_builder->toArray());

ฟังก์ชัน mymodule_delete_rows (&$ บริบท) {
  $connection = \Drupal::database();

  ถ้า (ว่าง ($ บริบท ['แซนด์บ็อกซ์'])) {
    $context['sandbox']['ความคืบหน้า'] = 0;
    $context['sandbox']['current_id'] = 0;
    $context['sandbox']['max'] = $connection
      ->query('เลือกนับ(DISTINCT [id]) จาก {example}')
      ->fetchField();
    $บริบท['ผลลัพธ์'] = 0;
  }

  $จำกัด = 5;
  $result = $การเชื่อมต่อ
    ->เลือก('ตัวอย่าง')
    ->condition('id', $context['sandbox']['current_id'], '>')
    ->orderBy('รหัส')
    ->ช่วง(0, $จำกัด)
    ->ดำเนินการ ();

  foreach (ผลลัพธ์ $ เป็น $ แถว) {
    $context['sandbox']['ความคืบหน้า']++;
    $บริบท['ผลลัพธ์']++;
    $context['sandbox']['current_id'] = $row->id;
    
    ถ้า (!empty($row->title) && is_numeric($row->title[0])) {
      $connection->delete('ตัวอย่าง')
        ->condition('id', $row->id)
        ->ดำเนินการ ()
    }
  }
  ถ้า ($context['sandbox']['progress'] != $context['sandbox']['max']) {
    $context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];
  }
}

ฟังก์ชัน mymodule_finished_callback ($ สำเร็จ, $ ผลลัพธ์, $ การดำเนินการ, $ ผ่านไป) {
  ถ้า ($ สำเร็จ) {
    $message = \Drupal::translation()->formatPlural($results, '@count แถวที่ถูกลบ', '@count แถวที่ถูกลบ');
  }
  อื่น {
    $message = t('เสร็จสิ้นโดยมีข้อผิดพลาด');
  }
  \Drupal::messenger()->addMessage($message);
}

ค่าที่เกี่ยวข้องที่ตั้งค่าในการเรียกกลับของการดำเนินการแบทช์คือค่าต่อไปนี้:

  • $context['เสร็จสิ้น'] บอกให้ Drupal หยุดเรียกการเรียกกลับการดำเนินการแบบแบตช์ เมื่อค่าของมันคือ 1 หรือ $context['เสร็จสิ้น'] ไม่ได้ตั้งค่า
  • $บริบท['ผลลัพธ์'] จะถูกส่งผ่านไปยังการเรียกกลับที่เรียกใช้เมื่อการดำเนินการแบทช์เสร็จสิ้น เป็นพารามิเตอร์ที่สอง
  • $บริบท['ข้อความ'] เป็นข้อความที่แสดงในหน้าความคืบหน้า

ฉันจะไม่สร้างอาร์เรย์ที่มีค่าสำหรับแต่ละรายการที่จัดการ เนื่องจากจะใช้หน่วยความจำที่มีอยู่ทั้งหมด เมื่อรายการที่จัดการมีมาก

อ้างอิง

Egor Elkin avatar
cn flag
"ดูเหมือนว่าโค้ดจะใช้หน่วยความจำที่อนุญาตทั้งหมด เนื่องจากจะโหลดเอนทิตีทั้งหมดก่อน แล้วจึงส่ง ID ไปยังการเรียกกลับการดำเนินการ นั่นไม่ใช่วิธีดำเนินการแบบแบตช์" - ไม่ ฟังก์ชัน get_blocks_ids() รับ block id จาก DB เพื่อเพิ่มประสิทธิภาพความเร็วและการใช้หน่วยความจำ - โปรดอ่านหัวข้ออีกครั้ง ฉันได้รับ "ขนาดหน่วยความจำที่อนุญาตของ 536870912 ไบต์หมดแล้ว" เมื่อสคริปต์ประมวลผลรายการทั้งหมด อย่างไรก็ตาม ขอบคุณสำหรับความคิดของคุณ @apaderno
apaderno avatar
us flag
ถึงกระนั้น หน่วยความจำก็หมดลงเช่นกัน เนื่องจากคุณกำลังโหลดรหัสเอนทิตีและสร้างการดำเนินการทุกๆ 50 เอนทิตี ด้วยเอนทิตี 4000 รายการ นั่นหมายถึงอาร์เรย์ของการดำเนินการแบบแบตช์ 80 รายการแทนที่จะเป็นการดำเนินการแบบแบตช์เดียว
Egor Elkin avatar
cn flag
เช่นเดียวกับการทำงานแบบชุดเดียว ฉันทดสอบตัวแปรนี้ และอีกครั้ง: แบทช์ประมวลผลรายการทั้งหมดสำเร็จ ข้อผิดพลาดปรากฏขึ้นเมื่อโหลดหน้าเสร็จสิ้นแบทช์
apaderno avatar
us flag
หากคุณกำลังสร้างอาร์เรย์ที่มีค่าสำหรับแต่ละรายการที่จัดการ นั่นจะเป็นการเพิ่มหน่วยความจำที่ใช้
Egor Elkin avatar
cn flag
ถูกต้อง.สิ่งนี้ไม่ได้แก้ปัญหาของฉัน แต่อย่างไรก็ตาม ลดการใช้หน่วยความจำ - ขอบคุณ และเพิ่มความเร็วในการโหลดเป็นชุด
apaderno avatar
us flag
ขออภัย ข้อผิดพลาด *หน่วยความจำหมด* ปรากฏขึ้นเมื่อโค้ดพยายามจัดสรรไบต์สุดท้ายที่เหลืออยู่ของหน่วยความจำที่มีอยู่ แต่โค้ดที่ใช้หน่วยความจำส่วนใหญ่อาจเป็นโค้ดจากโมดูลอื่น รวมถึงโมดูลหลักของ Drupal จากรหัสที่แสดงในคำถาม คำตอบเดียวที่เป็นไปได้คือ *รหัสใช้การเรียกกลับของการดำเนินการแบบแบตช์ในทางที่ผิด* ไม่สามารถระบุสาเหตุที่เป็นไปได้ทั้งหมดของหน่วยความจำหมด
sonfd avatar
in flag
โปรดทราบว่าการดำเนินการและการเรียกกลับที่เสร็จสิ้นแล้วไม่สามารถอยู่ในไฟล์ `mymodule.install` (ซึ่งคุณอาจลองทำได้เนื่องจากคุณกำลังสร้างแบตช์ของคุณจาก `hook_install`)

โพสต์คำตอบ

คนส่วนใหญ่ไม่เข้าใจว่าการถามคำถามมากมายจะปลดล็อกการเรียนรู้และปรับปรุงความสัมพันธ์ระหว่างบุคคล ตัวอย่างเช่น ในการศึกษาของ Alison แม้ว่าผู้คนจะจำได้อย่างแม่นยำว่ามีคำถามกี่ข้อที่ถูกถามในการสนทนา แต่พวกเขาไม่เข้าใจความเชื่อมโยงระหว่างคำถามและความชอบ จากการศึกษาทั้ง 4 เรื่องที่ผู้เข้าร่วมมีส่วนร่วมในการสนทนาด้วยตนเองหรืออ่านบันทึกการสนทนาของผู้อื่น ผู้คนมักไม่ตระหนักว่าการถามคำถามจะมีอิทธิพลหรือมีอิทธิพลต่อระดับมิตรภาพระหว่างผู้สนทนา