MediaWiki  master
mctest.php
Go to the documentation of this file.
1 <?php
26 
27 require_once __DIR__ . '/Maintenance.php';
28 
35 class McTest extends Maintenance {
36  public function __construct() {
37  parent::__construct();
38  $this->addDescription(
39  "Makes several operation requests on every cache server and shows a report.\n" .
40  "This tests both per-key and batched *Multi() methods as well as WRITE_BACKGROUND.\n" .
41  "\"IB\" means \"immediate blocking\" and \"DB\" means \"deferred blocking.\""
42  );
43  $this->addOption( 'cache', 'Use servers from this $wgObjectCaches store', true, true );
44  $this->addOption( 'class', 'Override the store "class" parameter', false, true );
45  $this->addOption( 'i', 'Number of iterations', false, true );
46  $this->addArg( 'server[:port]', 'Cache server to test, with optional port', false );
47  }
48 
49  public function execute() {
50  $config = $this->getConfig();
51  $objectCaches = $config->get( MainConfigNames::ObjectCaches );
52 
53  $cacheType = $this->getOption( 'cache', $config->get( MainConfigNames::MainCacheType ) );
54  $iterations = $this->getOption( 'i', 100 );
55  $classOverride = $this->getOption( 'class' );
56  $server = $this->getArg( 0 );
57 
58  if ( !isset( $objectCaches[$cacheType] ) ) {
59  $this->fatalError( "No configured '$cacheType' cache" );
60  }
61 
62  if ( $classOverride !== null ) {
63  if ( !is_subclass_of( $classOverride, BagOStuff::class ) ) {
64  $this->fatalError( "Invalid class '$classOverride' for cache" );
65  }
66  $class = $classOverride;
67  } else {
68  $class = $objectCaches[$cacheType]['class'];
69  }
70 
71  if ( $server !== null ) {
72  $servers = [ $server ];
73  } else {
74  // Note that some caches, like apcu, do not have a server list
75  $servers = $objectCaches[$cacheType]['servers'] ?? [ null ];
76  }
77 
78  // Use longest server string for output alignment
79  $maxSrvLen = max( array_map( 'strlen', $servers ) );
80 
81  $this->output( "Warming up connections to cache servers..." );
83  $cacheByServer = [];
84  foreach ( $servers as $server ) {
85  $conf = $objectCaches[$cacheType];
86  if ( $server !== null ) {
87  $conf['servers'] = [ $server ];
88  $host = $server;
89  } else {
90  $host = 'localhost';
91  }
92  $cacheByServer[$host] = new $class( $conf );
93  $cacheByServer[$host]->get( 'key' );
94  }
95  $this->output( "done\n" );
96  $this->output( "Single and batched operation profiling/test results:\n" );
97 
98  $valueByKey = [];
99  for ( $i = 1; $i <= $iterations; $i++ ) {
100  $valueByKey["test$i"] = 'S' . str_pad( (string)$i, 2048 );
101  }
102 
103  foreach ( $cacheByServer as $host => $mcc ) {
104  $this->output( str_pad( $host, $maxSrvLen ) . "\n" );
105  $this->benchmarkSingleKeyOps( $mcc, $valueByKey );
106  $this->benchmarkMultiKeyOpsImmediateBlocking( $mcc, $valueByKey );
107  $this->benchmarkMultiKeyOpsDeferredBlocking( $mcc, $valueByKey );
108  }
109  }
110 
115  private function benchmarkSingleKeyOps( BagOStuff $mcc, array $valueByKey ) {
116  $add = 0;
117  $set = 0;
118  $incr = 0;
119  $get = 0;
120  $delete = 0;
121 
122  $keys = array_keys( $valueByKey );
123  $count = count( $valueByKey );
124 
125  // Clear out any old values
126  $mcc->deleteMulti( $keys );
127 
128  $time_start = microtime( true );
129  foreach ( $valueByKey as $key => $value ) {
130  if ( $mcc->add( $key, $value ) ) {
131  $add++;
132  }
133  }
134  $addMs = intval( 1e3 * ( microtime( true ) - $time_start ) );
135 
136  $time_start = microtime( true );
137  foreach ( $valueByKey as $key => $value ) {
138  if ( $mcc->set( $key, $value ) ) {
139  $set++;
140  }
141  }
142  $setMs = intval( 1e3 * ( microtime( true ) - $time_start ) );
143 
144  $time_start = microtime( true );
145  foreach ( $valueByKey as $key => $value ) {
146  if ( $mcc->get( $key ) === $value ) {
147  $get++;
148  }
149  }
150  $getMs = intval( 1e3 * ( microtime( true ) - $time_start ) );
151 
152  $time_start = microtime( true );
153  foreach ( $keys as $key ) {
154  if ( $mcc->delete( $key ) ) {
155  $delete++;
156  }
157  }
158  $delMs = intval( 1e3 * ( microtime( true ) - $time_start ) );
159 
160  $time_start = microtime( true );
161  foreach ( $keys as $index => $key ) {
162  if ( $mcc->incrWithInit( $key, $mcc::TTL_INDEFINITE, $index ) === $index ) {
163  $incr++;
164  }
165  }
166  $incrMs = intval( 1e3 * ( microtime( true ) - $time_start ) );
167 
168  $this->output(
169  " add: $add/$count {$addMs}ms " .
170  "set: $set/$count {$setMs}ms " .
171  "get: $get/$count ({$getMs}ms) " .
172  "delete: $delete/$count ({$delMs}ms) " .
173  "incr: $incr/$count ({$incrMs}ms)\n"
174  );
175  }
176 
181  private function benchmarkMultiKeyOpsImmediateBlocking( BagOStuff $mcc, array $valueByKey ) {
182  $keys = array_keys( $valueByKey );
183  $iterations = count( $valueByKey );
184 
185  $time_start = microtime( true );
186  $mSetOk = $mcc->setMulti( $valueByKey ) ? '✓' : '✗';
187  $mSetMs = intval( 1e3 * ( microtime( true ) - $time_start ) );
188 
189  $time_start = microtime( true );
190  $found = $mcc->getMulti( $keys );
191  $mGetMs = intval( 1e3 * ( microtime( true ) - $time_start ) );
192  $mGetOk = 0;
193  foreach ( $found as $key => $value ) {
194  $mGetOk += ( $value === $valueByKey[$key] );
195  }
196 
197  $time_start = microtime( true );
198  $mChangeTTLOk = $mcc->changeTTLMulti( $keys, 3600 ) ? '✓' : '✗';
199  $mChangeTTTMs = intval( 1e3 * ( microtime( true ) - $time_start ) );
200 
201  $time_start = microtime( true );
202  $mDelOk = $mcc->deleteMulti( $keys ) ? '✓' : '✗';
203  $mDelMs = intval( 1e3 * ( microtime( true ) - $time_start ) );
204 
205  $this->output(
206  " setMulti (IB): $mSetOk {$mSetMs}ms " .
207  "getMulti (IB): $mGetOk/$iterations {$mGetMs}ms " .
208  "changeTTLMulti (IB): $mChangeTTLOk {$mChangeTTTMs}ms " .
209  "deleteMulti (IB): $mDelOk {$mDelMs}ms\n"
210  );
211  }
212 
217  private function benchmarkMultiKeyOpsDeferredBlocking( BagOStuff $mcc, array $valueByKey ) {
218  $keys = array_keys( $valueByKey );
219  $iterations = count( $valueByKey );
220  $flags = $mcc::WRITE_BACKGROUND;
221 
222  $time_start = microtime( true );
223  $mSetOk = $mcc->setMulti( $valueByKey, 0, $flags ) ? '✓' : '✗';
224  $mSetMs = intval( 1e3 * ( microtime( true ) - $time_start ) );
225 
226  $time_start = microtime( true );
227  $found = $mcc->getMulti( $keys );
228  $mGetMs = intval( 1e3 * ( microtime( true ) - $time_start ) );
229  $mGetOk = 0;
230  foreach ( $found as $key => $value ) {
231  $mGetOk += ( $value === $valueByKey[$key] );
232  }
233 
234  $time_start = microtime( true );
235  $mChangeTTLOk = $mcc->changeTTLMulti( $keys, 3600, $flags ) ? '✓' : '✗';
236  $mChangeTTTMs = intval( 1e3 * ( microtime( true ) - $time_start ) );
237 
238  $time_start = microtime( true );
239  $mDelOk = $mcc->deleteMulti( $keys, $flags ) ? '✓' : '✗';
240  $mDelMs = intval( 1e3 * ( microtime( true ) - $time_start ) );
241 
242  $this->output(
243  " setMulti (DB): $mSetOk {$mSetMs}ms " .
244  "getMulti (DB): $mGetOk/$iterations {$mGetMs}ms " .
245  "changeTTLMulti (DB): $mChangeTTLOk {$mChangeTTTMs}ms " .
246  "deleteMulti (DB): $mDelOk {$mDelMs}ms\n"
247  );
248  }
249 }
250 
251 $maintClass = McTest::class;
252 require_once RUN_MAINTENANCE_IF_MAIN;
Class representing a cache/ephemeral data store.
Definition: BagOStuff.php:85
get( $key, $flags=0)
Get an item.
deleteMulti(array $keys, $flags=0)
Delete a batch of items.
add( $key, $value, $exptime=0, $flags=0)
Insert an item if it does not already exist.
getMulti(array $keys, $flags=0)
Get a batch of items.
setMulti(array $valueByKey, $exptime=0, $flags=0)
Set a batch of items.
delete( $key, $flags=0)
Delete an item if it exists.
set( $key, $value, $exptime=0, $flags=0)
Set an item.
incrWithInit( $key, $exptime, $step=1, $init=null, $flags=0)
Increase the value of the given key (no TTL change) if it exists or create it otherwise.
changeTTLMulti(array $keys, $exptime, $flags=0)
Change the expiration of multiple items.
Abstract maintenance class for quickly writing and churning out maintenance scripts with minimal effo...
Definition: Maintenance.php:66
addArg( $arg, $description, $required=true, $multi=false)
Add some args that are needed.
output( $out, $channel=null)
Throw some output to the user.
getArg( $argId=0, $default=null)
Get an argument.
addDescription( $text)
Set the description text.
addOption( $name, $description, $required=false, $withArg=false, $shortName=false, $multiOccurrence=false)
Add a parameter to the script.
getOption( $name, $default=null)
Get an option, or return the default.
fatalError( $msg, $exitCode=1)
Output a message and terminate the current script.
Maintenance script that makes several 'set', 'incr' and 'get' requests on every memcached server and ...
Definition: mctest.php:35
__construct()
Default constructor.
Definition: mctest.php:36
execute()
Do the actual work.
Definition: mctest.php:49
A class containing constants representing the names of configuration variables.
$maintClass
Definition: mctest.php:251