Codi font en execució

Alliberar memòria al tractar molts nodes a Drupal

Treballant en Drupal a vegades ens trobem que ens cal processar grans quantitats de nodes per fer alguna tasca. Per aquests casos Drupal proveeix la Batch api o les cues segons ens convingui en cada cas. Però aquesta vegada es tractava d'exportar uns nodes a Excel i amb el temps la quantitat de dades a exportar anava augmentant (els nois no volien filtres, però òbviament els van acabar demanant, ja coneixem als usuaris...). És una tasca candidata per la Batch api, però tampoc trigava tant a exportar, el problema era un altre, ja que al llegir grans quantitats de nodes es menjava la memòria del servidor.

D'entrada esperava que canviat en loadMultiple per un load del node a cada iteració, ho feia tot més lent, però s'hauria d'alliberar memòria (l'usuari necessitava l'exportació, i jo un patch ràpid per fer-la, més endavant ja ho farem via batch vaig pensar). Doncs tampoc, el consum de memòria augmentava igualment.

Finalment vaig descobrir que el NodeStorage (EntityStorageBase de fet) manega una cache estàtica en memòria (ahà!) dels nodes que va carregant, i com que estem a la mateixa request creix desmesuradament, així que la solució passa per anar-la buidant de tant en quant...

La solució temporal (permanent a hores d'ara, ja sabeu com va...) va ser carregar els nodes en grups, processar-los, i netejar la cache estàtica.

Abans:

$audits = $this->nodeStorage->loadMultiple($nids);
/** @var NodeInterface $audit */
foreach ($audits as $audit) {
  // Escribim la fila a l'excel
}

Després d'aplicar el nostre patch:

// Partim per trossos
$chunks = array_chunk($nids, 50);

foreach ($chunks as $chunk) {
  $audits = $this->nodeStorage->loadMultiple($chunk);
  /** @var NodeInterface $audit */
  foreach ($audits as $audit) {
    // Escribim la fila a l'excel
  }

  // Alliberem memoria del tros
  $this->nodeStorage->resetCache($chunk);
}