A command iterating every post accumulates objects in the query cache and the object cache, so a loop that works over 10,000 rows fails over 200,000 — and the failure is a fatal at an unpredictable point.
$paged = 1;
while ( true ) {
$ids = get_posts( array(
'post_type' => 'product', 'posts_per_page' => 500,
'paged' => $paged++, 'fields' => 'ids', 'no_found_rows' => true,
) );
if ( ! $ids ) { break; }
foreach ( $ids as $id ) { /* ... */ }
WP_CLIUtilswp_clear_object_cache(); // the line that matters
}
wp_clear_object_cache() is the WP-CLI helper that resets the in-memory caches without touching a persistent backend. Without it the batching only limits the query size, not the memory, and the process still grows. Note the loop pages by offset — if the body changes whether a row still matches, page by id instead or rows get skipped.