Điểm:1

Làm cách nào tôi có thể sửa lỗi "Đã hết dung lượng bộ nhớ cho phép" khi kết thúc hàng loạt?

lá cờ cn

Lô đã xử lý tất cả các mục, nhưng thay vì hiển thị thông báo kết thúc, tôi thấy lỗi "Đã hết dung lượng bộ nhớ cho phép là 536870912 byte".

Khi tôi gỡ lỗi mã, tôi nhận thấy rằng Drupal tải từng khối đã xử lý khi kết thúc hàng loạt (ContentEntityBase->__construct). Tôi không thể hiểu tại sao Drupal lại làm như vậy.

Cấu trúc của mã:

Đây là một mô-đun tùy chỉnh với lớp biểu mẫu và các hàm hàng loạt trong custom_module.module tập tin.Khi gửi biểu mẫu, mô-đun gọi hàm tạo hàng loạt:

hàm công khai submitForm(mảng &$form, FormStateInterface $form_state) {
  custom_module_make_batch();
}

Hàm custom_module_build_batch nhận id của các khối tùy chỉnh (4000 trở lên) và tạo lô:

hàm custom_module_make_batch()
{

  $batch = [];

  $items = get_blocks_ids();

  $batch = custom_module_generate_batch($items);
  batch_set($batch);
}

hàm custom_module_generate_batch($items)
{

  hoạt động $ = [];

  $operations_groups = array_chunk($items, 50);

  foreach ($operations_groups dưới dạng $key => $operations_group) {
    $ Operations[] = [
      'custom_module_batch_op',
      [$operations_group],
    ];
  }

  $đợt = [
    'hoạt động' => $hoạt động,
    'đã hoàn tất' => 'custom_module_batch_finished',
    'tiêu đề' => 'Lô tùy chỉnh',
    'init_message' => 'Đợt đang bắt đầu.',
    'progress_message' => 'Đã xử lý @current trên @total phần.',
    'error_message' => 'Hàng loạt gặp lỗi.',
  ];
  trả lại lô hàng $;
}

function custom_module_batch_op($operations_group, &$context) {

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

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

}

chức năng custom_module_batch_finished($success, $results, $operations)
{

  $messenger = \Drupal::messenger();
  nếu ($ thành công) {
    // Ở đây chúng ta có thể làm điều gì đó có ý nghĩa với kết quả.
    // Chúng tôi chỉ hiển thị số lượng nút mà chúng tôi đã xử lý...

    nếu ($tổng) {
      $messenger->addMessage(t('@count kết quả đã xử lý.', ['@count' => $total]));
    } khác {
      $messenger->addMessage(t('Không có mục nào để di chuyển'));
    }

  } khác {
    // Đã xảy ra lỗi.
    // $operations chứa các hoạt động chưa được xử lý.
    $error_operation = đặt lại($operations);
    $messenger->addMessage(
      t(
        'Đã xảy ra lỗi khi xử lý @operation với các đối số : @args',
        [
          '@operation' => $error_operation[0],
          '@args' => print_r($error_operation[0], TRUE),
        ]
      )
    );
  }
}
leymannx avatar
lá cờ ne
`$new_value` không được xác định. Và tốt hơn nên sử dụng `$block->set('field_MYFIELD', $new_value)` để đặt giá trị hoặc `$block->set('field_MYFIELD', [])` để làm trống giá trị đó.
Egor Elkin avatar
lá cờ cn
@leymannx cảm ơn, ok, tôi sẽ sử dụng $block->set() $new_value - đây chỉ là một ví dụ, nó có thể là mảng: [ 'giá trị' => 'Một số tex', 'định dạng' => 'văn bản thuần túy', ];
apaderno avatar
lá cờ us
Có vẻ như `get_blocks_ids()` đang tải tất cả các mục, điều này giải thích cho thông báo lỗi. Gọi lại hàng loạt thực hiện truy vấn để lấy các mục cần thiết.Lấy tất cả các mục và giao chúng theo lô không phải là cách một hoạt động theo lô được cho là hoạt động, vì các hoạt động theo lô được thực hiện để tránh sử dụng tất cả bộ nhớ khả dụng và tránh hết thời gian chờ do PHP mất nhiều thời gian hơn để xử lý yêu cầu so với thời gian được giao thời gian.
Điểm:5
lá cờ us

Thao tác hàng loạt được sử dụng khi không xác định được số lượng mục cần xử lý và có thể có quá nhiều mục mà việc tải tất cả chúng sẽ sử dụng hết bộ nhớ mà PHP cung cấp hoặc việc xử lý chúng sẽ mất nhiều thời gian hơn thời gian mà PHP cung cấp cho kịch bản để chạy. Khi dữ liệu được tải từ một bảng cơ sở dữ liệu, thậm chí cả dữ liệu thực thể, lệnh gọi lại thao tác hàng loạt sẽ tải dữ liệu, chẳng hạn như mã này.

$batch_builder = (BatchBuilder mới())
  ->setTitle(t('Xóa hàng cơ sở dữ liệu'))
  ->setFinishCallback('mymodule_finished_callback')
  ->addOperation('mymodule_delete_rows', []);

batch_set($batch_builder->toArray());

chức năng mymodule_delete_rows(&$context) {
  $connection = \Drupal::database();

  if (trống($context['sandbox'])) {
    $context['sandbox']['progress'] = 0;
    $context['sandbox']['current_id'] = 0;
    $context['sandbox']['max'] = $connection
      -> truy vấn ('ĐẾM CHỌN (DISTINCT [id]) TỪ {ví dụ}')
      ->fetchField();
    $context['results'] = 0;
  }

  giới hạn $ = 5;
  $result = $connection
    -> chọn ('ví dụ')
    ->điều kiện('id', $context['sandbox']['current_id'], '>')
    ->orderBy('id')
    -> phạm vi (0, giới hạn $)
    ->thực thi();

  foreach ($kết quả là $row) {
    $context['sandbox']['progress']++;
    $context['kết quả']++;
    $context['sandbox']['current_id'] = $row->id;
    
    if (!empty($row->title) && is_numeric($row->title[0])) {
      $connection->delete('ví dụ')
        ->điều kiện('id', $row->id)
        ->thực thi()
    }
  }
  if ($context['sandbox']['progress'] != $context['sandbox']['max']) {
    $context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];
  }
}

function mymodule_finished_callback($success, $results, $operations, $elapsed) {
  nếu ($ thành công) {
    $message = \Drupal::translation()->formatPlural($results, '@count hàng đã xóa.', '@count hàng đã xóa.');
  }
  khác {
    $message = t('Đã hoàn tất có lỗi.');
  }
  \Drupal::messenger()->addMessage($message);
}

Các giá trị liên quan được đặt trong cuộc gọi lại hoạt động hàng loạt là những giá trị sau:

  • $context['đã hoàn thành'] yêu cầu Drupal ngừng gọi lại hoạt động hàng loạt, khi giá trị của nó là 1 hoặc $context['đã hoàn thành'] không được thiết lập
  • $context['kết quả'] được chuyển đến cuộc gọi lại được gọi khi các hoạt động hàng loạt được thực hiện, như tham số thứ hai của nó
  • $context['nội dung'] là một tin nhắn văn bản được hiển thị trong trang tiến trình

Tôi sẽ không tạo một mảng chứa giá trị cho từng mục được xử lý, vì điều đó sẽ sử dụng tất cả bộ nhớ khả dụng khi có nhiều mục được xử lý.

Thẩm quyền giải quyết

Egor Elkin avatar
lá cờ cn
"Mã dường như sử dụng tất cả bộ nhớ được phép bởi vì trước tiên nó tải tất cả các thực thể và sau đó chuyển ID của chúng tới lệnh gọi lại hoạt động. Đó không phải là cách thực hiện các hoạt động hàng loạt." - không, hàm get_blocks_ids() nhận id khối từ DB, để tối ưu hóa tốc độ và mức sử dụng bộ nhớ - Vui lòng đọc lại chủ đề, tôi nhận được "Kích thước bộ nhớ cho phép là 536870912 byte đã hết" khi kết thúc đợt, khi tập lệnh xử lý tất cả các mục Dù sao cũng cảm ơn vì những suy nghĩ của bạn @apaderno
apaderno avatar
lá cờ us
Tuy nhiên, bộ nhớ cũng bị cạn kiệt vì bạn đang tải ID thực thể và tạo một thao tác cứ sau 50 thực thể; với 4000 thực thể, điều đó có nghĩa là một mảng gồm 80 hoạt động hàng loạt thay vì một hoạt động hàng loạt.
Egor Elkin avatar
lá cờ cn
Tương tự với hoạt động hàng loạt, tôi đã thử nghiệm biến thể này. Và một lần nữa: xử lý hàng loạt tất cả các mục thành công, lỗi xuất hiện khi tải trang kết thúc hàng loạt.
apaderno avatar
lá cờ us
Nếu bạn đang tạo một mảng chứa giá trị cho từng mục được xử lý, điều đó sẽ làm tăng bộ nhớ đã sử dụng.
Egor Elkin avatar
lá cờ cn
Chính xác.Điều này không giải quyết được vấn đề của tôi nhưng dù sao, hãy giảm mức sử dụng bộ nhớ - cảm ơn và tăng tốc độ tải hàng loạt
apaderno avatar
lá cờ us
Thật không may, lỗi *bộ nhớ cạn kiệt* xuất hiện khi mã cố phân bổ byte cuối cùng còn lại của bộ nhớ khả dụng, nhưng mã tiêu thụ phần lớn bộ nhớ có thể là mã từ một mô-đun khác, bao gồm cả mô-đun lõi Drupal. Với mã hiển thị trong câu hỏi, câu trả lời duy nhất có thể là *Mã thực hiện lệnh gọi lại thao tác hàng loạt sai cách.* Mã này không thể liệt kê tất cả các nguyên nhân có thể gây ra tình trạng cạn kiệt bộ nhớ.
sonfd avatar
lá cờ in
Lưu ý rằng các cuộc gọi lại thao tác và kết thúc không thể nằm trong tệp `mymodule.install` (bạn có thể thử thực hiện thao tác này vì bạn đang tạo lô của mình từ `hook_install`).

Đăng câu trả lời

Hầu hết mọi người không hiểu rằng việc đặt nhiều câu hỏi sẽ mở ra cơ hội học hỏi và cải thiện mối quan hệ giữa các cá nhân. Ví dụ, trong các nghiên cứu của Alison, mặc dù mọi người có thể nhớ chính xác có bao nhiêu câu hỏi đã được đặt ra trong các cuộc trò chuyện của họ, nhưng họ không trực giác nhận ra mối liên hệ giữa câu hỏi và sự yêu thích. Qua bốn nghiên cứu, trong đó những người tham gia tự tham gia vào các cuộc trò chuyện hoặc đọc bản ghi lại các cuộc trò chuyện của người khác, mọi người có xu hướng không nhận ra rằng việc đặt câu hỏi sẽ ảnh hưởng—hoặc đã ảnh hưởng—mức độ thân thiện giữa những người đối thoại.