Make WordPress Core

Ticket #55206: test-wp-memory-leaks.php

File test-wp-memory-leaks.php, 3.4 KB (added by sllimrovert, 5 years ago)

failing unit tests

Line 
1<?php
2
3/**
4 * Class WP_Memory_Leak_Tests
5 *
6 * This class tests two cases that cause memory leaks in WordPress that could
7 * lead to crashes, particularly in CLI jobs that work on larger batches. For
8 * each of the cases ( one for the wpdb class and one for the global
9 * $wp_object_cache ), we perform some seemingly innocuous task many times -
10 * enough times to require that PHP allocate more memory because of a specific
11 * action.
12 *
13 * Neither of the tests here show a particularly large memory increase, but I've
14 * personally had both occur for me on large jobs hitting WP API functions. The
15 * one with $wpdb->queries particularly has a tendency to blow up.
16 */
17class WP_Memory_Leak_Tests extends \WP_UnitTestCase {
18
19 /**
20 * This tests a condition which exposes a memory leak in the WPDB class.
21 * If 'SAVEQUERIES' is defined as truthy, then the $wpdb->queries property
22 * can grow indefinitely.
23 */
24 public function test_WPDB_Memory_Leak() {
25
26 // Once a constant is defined, it can't be undefined
27 define( 'SAVEQUERIES', true );
28
29 // I'll just start my cron job to read the import file I've got. It's
30 // got a decent number of records.
31 $number_of_records = 1000;
32
33 global $wpdb;
34 $memory = memory_get_usage( true );
35 $peak = memory_get_peak_usage( true );
36 foreach ( [ 'first', 'second' ] as $pass ) {
37 // first pass through, we'll apply a fix for this memory leak.
38 // second pass through, we'll bypass the fix and the tests will fail.
39 for ( $i = 1; $i <= $number_of_records; $i ++ ) {
40 if ( 'first' === $pass ) {
41 $wpdb->queries = [];
42 }
43
44 // for this test, we'll do direct calls to $wpdb
45 $wpdb->query( $wpdb->prepare( "SELECT * FROM $wpdb->posts WHERE ID = %d", $i ) );
46 }
47 $this->assertEquals( $memory, memory_get_usage( true ), "$pass pass" );
48 $this->assertEquals( $peak, memory_get_peak_usage( true ), "$pass pass" );
49 }
50
51 }
52
53 /**
54 * This tests a condition which exposes a memory leak in wp cache API. If
55 * a large batch job attempts to do a lot of something that ends up caching
56 * things ( like, for example, get_post or wp_insert_post ), then unless
57 * the cache is flushed regularly, the memory usage grows indefinitely.
58 */
59 public function test_WP_Cache_Memory_Leak() {
60
61 // I'll just start my cron job to read the import file I've got. It's
62 // got a decent number of records.
63 $number_of_records = 1000;
64
65 global $wpdb;
66 $memory = memory_get_usage( true );
67 $peak = memory_get_peak_usage( true );
68 foreach ( [ 'first', 'second' ] as $pass ) {
69 // first pass through, we'll apply a fix for this memory leak.
70 // second pass through, we'll bypass the fix and the tests will fail.
71 for ( $i = 1; $i <= $number_of_records; $i ++ ) {
72 if ( 'first' === $pass ) {
73 wp_cache_flush();
74 }
75
76 // Because our last test defined 'SAVEQUERIES', we need to
77 // always apply this fix, otherwise that memory leak manifests.
78 // With us doing a core API function `wp_insert_post`, the number
79 // of queries is quite large and memory __really__ grows.
80 $wpdb->queries = [];
81
82 // let's say we're inserting posts, maybe from an excel file.
83 // this caches some things, so $wp_object_cache grows.
84 wp_insert_post([
85 'post_type' => 'post',
86 'post_title' => "post $i",
87 'post_content' => "pass $pass"
88 ]);
89 }
90 $this->assertEquals( $memory, memory_get_usage( true ), "$pass pass" );
91 $this->assertEquals( $peak, memory_get_peak_usage( true ), "$pass pass" );
92 }
93
94 }
95
96}