24use Psr\Log\LoggerAwareInterface;
25use Psr\Log\LoggerInterface;
26use Psr\Log\NullLogger;
68 'readQueryRows' => INF,
69 'readQueryTime' => INF,
70 'writeQueryTime' => INF
95 $this->silenced = $value;
111 $this->expect[$event] = isset( $this->expect[$event] )
112 ? min( $this->expect[$event], $value )
114 if ( $this->expect[$event] == $value ) {
115 $this->expectBy[$event] = $fname;
131 foreach ( $expects as $event => $value ) {
146 foreach ( $this->hits as &$val ) {
150 foreach ( $this->expect as &$val ) {
154 $this->expectBy = [];
183 if ( $this->hits[
'conns']++ >= $this->expect[
'conns'] ) {
185 'conns',
"[connect to $server ($db)]", $this->hits[
'conns'] );
187 if ( $isMaster && $this->hits[
'masterConns']++ >= $this->expect[
'masterConns'] ) {
189 'masterConns',
"[connect to $server ($db)]", $this->hits[
'masterConns'] );
203 $name =
"{$server} ({$db}) (TRX#$id)";
204 if ( isset( $this->dbTrxHoldingLocks[$name] ) ) {
205 $this->logger->warning(
"Nested transaction for '$name' - out of sync." );
207 $this->dbTrxHoldingLocks[$name] = [
208 'start' => microtime(
true ),
211 $this->dbTrxMethodTimes[$name] = [];
213 foreach ( $this->dbTrxHoldingLocks as $name => &$info ) {
215 $info[
'conns'][$name] = 1;
230 $eTime = microtime(
true );
231 $elapsed = ( $eTime - $sTime );
233 if ( $isWrite && $n > $this->expect[
'maxAffected'] ) {
234 $this->logger->warning(
235 "Query affected $n row(s):\n" . self::queryString( $query ) .
"\n" .
236 (
new RuntimeException() )->getTraceAsString() );
237 } elseif ( !$isWrite && $n > $this->expect[
'readQueryRows'] ) {
238 $this->logger->warning(
239 "Query returned $n row(s):\n" . self::queryString( $query ) .
"\n" .
240 (
new RuntimeException() )->getTraceAsString() );
244 if ( $this->hits[
'queries']++ >= $this->expect[
'queries'] ) {
247 if ( $isWrite && $this->hits[
'writes']++ >= $this->expect[
'writes'] ) {
251 if ( !$isWrite && $elapsed > $this->expect[
'readQueryTime'] ) {
254 if ( $isWrite && $elapsed > $this->expect[
'writeQueryTime'] ) {
258 if ( !$this->dbTrxHoldingLocks ) {
261 } elseif ( !$isWrite && $elapsed < $this->eventThreshold ) {
266 foreach ( $this->dbTrxHoldingLocks as $name => $info ) {
267 $lastQuery = end( $this->dbTrxMethodTimes[$name] );
270 $lastEnd = $lastQuery[2];
271 if ( $sTime >= $lastEnd ) {
272 if ( ( $sTime - $lastEnd ) > $this->eventThreshold ) {
274 $this->dbTrxMethodTimes[$name][] = [
'...delay...', $lastEnd, $sTime ];
276 $this->dbTrxMethodTimes[$name][] = [ $query, $sTime, $eTime ];
280 if ( $sTime >= $info[
'start'] ) {
281 $this->dbTrxMethodTimes[$name][] = [ $query, $sTime, $eTime ];
301 $name =
"{$server} ({$db}) (TRX#$id)";
302 if ( !isset( $this->dbTrxMethodTimes[$name] ) ) {
303 $this->logger->warning(
"Detected no transaction for '$name' - out of sync." );
310 if ( $writeTime > $this->expect[
'writeQueryTime'] ) {
313 "[transaction $id writes to {$server} ({$db})]",
319 if ( $affected > $this->expect[
'maxAffected'] ) {
322 "[transaction $id writes to {$server} ({$db})]",
327 $lastQuery = end( $this->dbTrxMethodTimes[$name] );
329 $now = microtime(
true );
330 $lastEnd = $lastQuery[2];
331 if ( ( $now - $lastEnd ) > $this->eventThreshold ) {
332 $this->dbTrxMethodTimes[$name][] = [
'...delay...', $lastEnd, $now ];
336 foreach ( $this->dbTrxMethodTimes[$name] as $info ) {
337 $elapsed = ( $info[2] - $info[1] );
338 if ( $elapsed >= $this->dbLockThreshold ) {
345 foreach ( $this->dbTrxMethodTimes[$name] as $i => $info ) {
346 list( $query, $sTime, $end ) = $info;
348 "%d\t%.6f\t%s\n", $i, ( $end - $sTime ), self::queryString( $query ) );
350 $this->logger->warning(
"Sub-optimal transaction on DB(s) [{dbs}]: \n{trace}", [
351 'dbs' => implode(
', ', array_keys( $this->dbTrxHoldingLocks[$name][
'conns'] ) ),
355 unset( $this->dbTrxHoldingLocks[$name] );
356 unset( $this->dbTrxMethodTimes[$name] );
365 if ( $this->silenced ) {
369 $this->logger->warning(
370 "Expectation ({measure} <= {max}) by {by} not met (actual: {actual}):\n{query}\n" .
371 (
new RuntimeException() )->getTraceAsString(),
374 'max' => $this->expect[
$expect],
375 'by' => $this->expectBy[
$expect],
377 'query' => self::queryString( $query )