Puncte:1

Cum pot remedia „Dimensiunea de memorie permisă epuizată” la terminarea lotului?

drapel cn

Lotul a procesat toate articolele, dar în loc să afișeze mesajul de finalizare, văd eroarea „Dimensiunea memoriei permisă de 536870912 octeți epuizat”.

Când depanez codul, observ că Drupal încarcă fiecare bloc procesat la terminarea lotului (ContentEntityBase->__construct). Nu pot să-mi dau seama de ce Drupal face asta.

Structura codului:

Acesta este un modul personalizat cu clasă de formular și funcții batch în modul_personalizat.modul fişier.La trimiterea formularului, modulul apelează funcția de creare a lotului:

funcția publică submitForm(matrice &$form, FormStateInterface $form_state) {
  custom_module_make_batch();
}

Funcția custom_module_build_batch obține ID-urile blocurilor personalizate (4000 sau mai multe) și generează lotul:

funcția custom_module_make_batch()
{

  $lot = [];

  $items = get_blocks_ids();

  $lot = custom_module_generate_batch($articole);
  set_loturi($lot);
}

funcția custom_module_generate_batch($items)
{

  $operatii = [];

  $grupuri_operații = array_chunk($articole, 50);

  foreach ($operations_groups ca $key => $operations_group) {
    $operațiuni[] = [
      „custom_module_batch_op”,
      [$operations_group],
    ];
  }

  $lot = [
    'operations' => $operatii,
    'finished' => 'custom_module_batch_finished',
    'title' => 'Lot personalizat',
    'init_message' => 'Lotul începe.',
    'progress_message' => 'Procesat @current din @total părți.',
    'error_message' => 'Lotul a întâmpinat o eroare.',
  ];
  returnează $lot;
}

funcția custom_module_batch_op($operations_group, &$context) {

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

    $block->field_name = $new_value;
    
    $bloc->salvare();
  }

}

funcția custom_module_batch_finished($succes, $rezultate, $operations)
{

  $messenger = \Drupal::messenger();
  dacă ($succes) {
    // Aici am putea face ceva semnificativ cu rezultatele.
    // Afișăm doar numărul de noduri procesate...

    dacă ($total) {
      $messenger->addMessage(t('@count rezultate procesate.', ['@count' => $total]));
    } altfel {
      $messenger->addMessage(t('Nu există articole pentru migrare'));
    }

  } altfel {
    // A aparut o eroare.
    // $operations conține operațiunile care au rămas neprocesate.
    $operațiune_eroare = resetare($operații);
    $messenger->addMessage(
      t(
        „A apărut o eroare la procesarea @operation cu argumente: @args”,
        [
          '@operation' => $error_operation[0],
          '@args' => print_r($error_operation[0], TRUE),
        ]
      )
    );
  }
}
leymannx avatar
drapel ne
`$new_value` este nedefinit. Și mai bine folosiți `$block->set('field_MYFIELD', $new_value)` pentru a seta valoarea sau `$block->set('field_MYFIELD', [])` pentru a o goli.
Egor Elkin avatar
drapel cn
@leymannx mulțumesc, ok, voi folosi $block->set() $new_value - este doar un exemplu, poate fi o matrice: [ 'value' => 'Niste tex', 'format' => 'text_plat', ];
apaderno avatar
drapel us
Se pare că `get_blocks_ids()` încarcă toate elementele, ceea ce ar explica mesajul de eroare. Un apel invers în lot execută interogarea pentru a obține elementele necesare.Obținerea tuturor articolelor și predarea lor în loturi nu este modul în care ar trebui să funcționeze o operațiune în lot, deoarece operațiunile în lot sunt efectuate pentru a evita utilizarea întregii memorie disponibile și pentru a evita time-out-urile cauzate de PHP care necesită mai mult timp pentru a gestiona cererea decât cea alocată. timp.
Puncte:5
drapel us

Operațiile în lot sunt utilizate atunci când numărul de elemente de tratat este necunoscut și ar putea exista atât de multe articole încât fie încărcarea tuturor ar folosi toată memoria pe care PHP o pune la dispoziție, fie manipularea lor ar dura mai mult decât timpul pe care PHP îl oferă scriptul pentru a rula. Când datele sunt încărcate dintr-un tabel al bazei de date, chiar și datele unei entități, apelul invers al operației în lot este cel care încarcă datele, așa cum face acest cod, de exemplu.

$batch_builder = (nou BatchBuilder())
  ->setTitle(t('Ștergerea rândurilor bazei de date'))
  ->setFinishCallback('mymodule_finished_callback')
  ->addOperation('mymodule_delete_rows', []);

batch_set($batch_builder->toArray());

funcția mymodule_delete_rows(&$context) {
  $conexiune = \Drupal::database();

  if (gol ($context['sandbox'])) {
    $context['sandbox']['progress'] = 0;
    $context['sandbox']['current_id'] = 0;
    $context['sandbox']['max'] = $conexiune
      ->query('SELECT COUNT(DIstinct [id]) FROM {example}')
      ->fetchField();
    $context['rezultate'] = 0;
  }

  $limita = 5;
  $rezultat = $conexiune
    ->select('exemplu')
    ->condition('id', $context['sandbox']['current_id'], '>')
    ->orderBy('id')
    ->interval(0, $limită)
    ->execute();

  foreach ($rezultat ca $rând) {
    $context['sandbox']['progres']++;
    $context['rezultate']++;
    $context['sandbox']['current_id'] = $row->id;
    
    dacă (!gol($rând->titlu) && este_numeric($rând->titlu[0])) {
      $connection->delete('exemplu')
        ->condition('id', $row->id)
        ->execute()
    }
  }
  if ($context['sandbox']['progres'] != $context['sandbox']['max']) {
    $context['finished'] = $context['sandbox']['progres'] / $context['sandbox']['max'];
  }
}

function mymodule_finished_callback($succes, $rezultate, $operations, $elapsed) {
  dacă ($succes) {
    $message = \Drupal::translation()->formatPlural($results, '@count row deleted.', '@count rows deleted.');
  }
  else {
    $message = t('S-a terminat cu o eroare.');
  }
  \Drupal::messenger()->addMessage($mesaj);
}

Valorile relevante stabilite în apelarea operațiunilor batch sunt următoarele:

  • $context['terminat'] îi spune lui Drupal să nu mai apeleze la operațiunile de lot, când valoarea sa este 1 sau $context['terminat'] nu este setat
  • $context['rezultate'] este transmis callback-ului invocat atunci când operațiunile batch sunt efectuate, ca al doilea parametru al acestuia
  • $context['mesaj'] este un mesaj text afișat în pagina de progres

Nu aș construi o matrice care să conțină o valoare pentru fiecare element manipulat, deoarece aceasta ar folosi toată memoria disponibilă, atunci când elementele manipulate sunt mult.

Referinţă

Egor Elkin avatar
drapel cn
„Codul pare să folosească toată memoria permisă pentru că mai întâi încarcă toate entitățile și apoi le transmite ID-urile către apelarea operației. Nu așa ar trebui să fie implementate operațiunile batch.” - nu, funcția get_blocks_ids() obține ID-uri de bloc din DB, pentru a optimiza viteza și utilizarea memoriei - Vă rugăm să citiți din nou subiectul, primesc „Dimensiunea memoriei permisă de 536870912 octeți epuizat” la terminarea lotului, când scriptul a procesat toate elementele Oricum, mulțumesc pentru gândurile tale @apaderno
apaderno avatar
drapel us
Cu toate acestea, memoria se epuizează și pentru că încărcați ID-urile entităților și creați o operație la fiecare 50 de entități; cu 4000 de entități, asta înseamnă o matrice de 80 de operațiuni în lot în loc de o singură operațiune în lot.
Egor Elkin avatar
drapel cn
La fel și cu operarea cu un singur lot, am testat această variantă. Și din nou: lotul procesează toate articolele cu succes, eroarea apare la încărcarea paginii de terminare a lotului.
apaderno avatar
drapel us
Dacă creați o matrice care conține o valoare pentru fiecare element gestionat, aceasta ar crește memoria utilizată.
Egor Elkin avatar
drapel cn
Corect.Acest lucru nu îmi rezolvă problema, dar oricum, reduceți utilizarea memoriei - mulțumesc și grăbiți încărcarea lotului
apaderno avatar
drapel us
Din păcate, eroarea *memorie epuizată* apare atunci când codul încearcă să aloce ultimul octet rămas din memorie disponibilă, dar codul care consumă cea mai mare parte a memoriei ar putea fi cod dintr-un alt modul, inclusiv un modul de bază Drupal. Având în vedere codul afișat în întrebare, singurul răspuns posibil este *Codul implementează o operațiune de apel invers în mod greșit.* Nu poate enumera toate cauzele posibile ale epuizării memoriei.
sonfd avatar
drapel in
Rețineți că operațiunea și apelurile inverse finalizate nu pot fi într-un fișier `mymodule.install` (ceea ce puteți încerca să faceți pentru că vă generați lotul din `hook_install`).

Postează un răspuns

Majoritatea oamenilor nu înțeleg că a pune multe întrebări deblochează învățarea și îmbunătățește legătura interpersonală. În studiile lui Alison, de exemplu, deși oamenii își puteau aminti cu exactitate câte întrebări au fost puse în conversațiile lor, ei nu au intuit legătura dintre întrebări și apreciere. În patru studii, în care participanții au fost implicați în conversații ei înșiși sau au citit transcrieri ale conversațiilor altora, oamenii au avut tendința să nu realizeze că întrebarea ar influența – sau ar fi influențat – nivelul de prietenie dintre conversatori.