The patch committed in #2851552: On rebuild, xmlsitemap_node fills node static cache, exhausts memory has nasty side-effects:

--- a/xmlsitemap_node/xmlsitemap_node.module
+++ b/xmlsitemap_node/xmlsitemap_node.module
@@ -42,7 +42,7 @@ function xmlsitemap_node_xmlsitemap_index_links($limit) {
  *   An array of node IDs.
  */
 function xmlsitemap_node_xmlsitemap_process_node_links(array $nids) {
-  $nodes = node_load_multiple($nids);
+  $nodes = node_load_multiple($nids, array(), TRUE);
   foreach ($nodes as $node) {
     $link = xmlsitemap_node_create_link($node);
     xmlsitemap_link_save($link, array($link['type'] => $node));

Resetting an entity_load cache bin in Drupal 7 can have more drastic consequences than in previous versions of Drupal, as we can have persistent load caches in place of the PHP static memory cache.

Instead, wherever possible, only flush the necessary entities from the cache, using entity_get_controller($entity_type)->resetCache(array($entity_id));

The previously-committed patch purges the entire node cache for no reason. That patch should be reverted and replaced by code which flushes only the specific nodes which are being processed. That deals with the memory concerns without touching the cached nodes which are not in the $nids array.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

jweowu created an issue. See original summary.

jweowu’s picture

Issue summary: View changes
jweowu’s picture

Also see #1963964: Don't use $reset flag with node_load() which I posted a patch for 5 years ago, and which remains uncommitted :(

Please don't ever commit code which passes the $reset flag to node_load(). If you ever see that in a proposed patch you should assume it's a bug, unless you can establish that flushing specific cache entries is somehow insufficient.

jweowu’s picture

jweowu’s picture

Status: Active » Needs review
FileSize
852 bytes
pifagor’s picture

I think we should not change $nodes = node_load_multiple($nids, array(), TRUE); to
foreach ($nids as $nid)
$node = node_load($nid);

jweowu’s picture

As I've commented on in #2851552: On rebuild, xmlsitemap_node fills node static cache, exhausts memory regarding the original commit:

this change merely reduced the chance of exceeding memory limits -- with an arbitrarily large $nids argument, that node_load_multiple() could still do exactly that.

That is why my patch avoids that.

You could refactor to node_load_multiple() in batches of a fixed/safe size (feel free to do that; I felt it was an unnecessary complication, but I don't object to it), but it's not safe to assume you can load any number of nodes -- after all, that's precisely what was causing somebody memory issues in the first place.

Edit: Not to mention that your suggested revision restores the code that purges the entire node load cache, which is the precise thing I'm trying to eliminate here. That TRUE in the arguments to node_load_multiple() is a massive fail, and should never be used.

jweowu’s picture

pifagor’s picture

Edit: Not to mention that your suggested revision restores the code that purges the entire node load cache, which is the precise thing I'm trying to eliminate here.

No, look please patch https://www.drupal.org/files/issues/2018-07-26/xmlsitemap-entity_load_ca...
entity_get_controller('node')->resetCache(array($node->nid));

jweowu’s picture

node_load_multiple($nids, array(), TRUE); purges the entire node cache. You absolutely cannot keep that.

jweowu’s picture

You could split $nids into batches of a known size, and use node_load_multiple($batch) if you really wish to call node_load_multiple. You mustn't use $reset though.

pifagor’s picture

Of course, I agree with you, because it was worth changing the code line on $nodes = node_load_multiple($nids, array(), FALSE);
In order to optimize my work, I will review your decision again.
Thanks for the info.

pifagor’s picture

New patch

jweowu’s picture

Status: Needs review » Needs work

FWIW I'm not a big fan of the variable names.

$new_nids is neither "new" (they're the same node IDs as $nids), nor does it directly contain "nids" (being an array of arrays of nids); and $value is almost always a meaningless name.

Something like foreach ($nids_chunks as $chunk) would be more meaningful in context.

I'm utterly perplexed by count($nids)/(count($nids)/15). That's an extraordinarily elaborate way of writing 15 (not to mention that you're dividing by zero if $nids is empty, but that's a bit of a side-issue in this instance).

node_load_multiple($value, array()); should just be node_load_multiple($value); (or node_load_multiple($chunk); if you were to use my suggested name).

And as you're using chunks, you could move entity_get_controller('node')->resetCache(array($node->nid)); outside of the loop, and change it to entity_get_controller('node')->resetCache($chunk);

pifagor’s picture

pifagor’s picture

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, 15: xmlsitemap-entity_load_cache_reset-2986733-15.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

pifagor’s picture

Status: Needs work » Needs review
FileSize
1.16 KB
jweowu’s picture

Status: Needs review » Needs work

count($nids)/(count($nids)/15) is crazy code. I don't know how you came up with that, or what you think it's doing, but aside from the division by zero, it's literally the same thing as 15. Do the algebra:

(A / (A/B)) == (BA / A) == B

It's just an inexplicably convoluted way of writing 15. I said as much in my previous reply.

Remove the new if (count($nids) >= 1) test -- you don't want to protect the crazy code from dividing by zero; you want to remove the crazy code entirely, by changing count($nids)/(count($nids)/15) to 15.

pifagor’s picture

Status: Needs work » Needs review
FileSize
1.13 KB

I'm sorry that I missed this remark. I fixed the patch.

jweowu’s picture

Personally I would still ditch the if (count($nids) >= 1) test, as it doesn't serve much purpose -- if $nids is empty then $nids_chunks will also be an empty array, and the foreach loop around that empty array would do nothing; so I don't think it's important to be testing count($nids). I'm not particularly bothered by it though.

I'd change the comment // Load no more than 15 nodes. to // Load no more than 15 nodes at a time., as the current wording sounds a bit like all the other nodes will be completely ignored.

Everything else now looks good to me.

pifagor’s picture

I'd change the comment // Load no more than 15 nodes. to // Load no more than 15 nodes at a time., as the current wording sounds a bit like all the other nodes will be completely ignored.

Done.

Personally I would still ditch the if (count($nids) >= 1) test, as it doesn't serve much purpose -- if $nids is empty then $nids_chunks will also be an empty array, and the foreach loop around that empty array would do nothing;

This is required for automatic tests, otherwise the patch will not be tested. And this check does not hurt.

jweowu’s picture

> This is required for automatic tests, otherwise the patch will not be tested.

I don't understand why that would be.

I know that you added this check in #18 after your patch in #15 failed testing; but the reason it failed testing was that you had that division-by-zero error in the crazy code (which you have removed in #20).

With the cause of the division-by-zero now gone, I'd be very surprised if removing that check caused any testing problems.

> And this check does not hurt.

Agreed. I'm just mostly sure it also doesn't help :)

I don't mind either way, though, so the patch looks good to me! (I'd mark it RTBC but I've not actually tested it.)

alex_optim’s picture

Status: Needs review » Reviewed & tested by the community

Patch looks not bad.

pifagor’s picture

  • pifagor committed 046c34f on 7.x-2.x
    Issue #2986733 by pifagor, jweowu, alex_optim: Don't use $reset flag...
pifagor’s picture

Status: Reviewed & tested by the community » Fixed

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.