26use Psr\Log\LoggerInterface;
27use Psr\Log\LoggerAwareInterface;
28use Psr\Log\NullLogger;
65 'readQueryRows' => INF,
66 'readQueryTime' => INF,
67 'writeQueryTime' => INF
92 $this->silenced = $value;
108 $this->expect[$event] = isset( $this->expect[$event] )
109 ? min( $this->expect[$event], $value )
111 if ( $this->expect[$event] == $value ) {
112 $this->expectBy[$event] = $fname;
128 foreach ( $expects as $event => $value ) {
143 foreach ( $this->hits as &$val ) {
147 foreach ( $this->expect as &$val ) {
151 $this->expectBy = [];
180 if ( $this->hits[
'conns']++ >= $this->expect[
'conns'] ) {
182 'conns',
"[connect to $server ($db)]", $this->hits[
'conns'] );
184 if ( $isMaster && $this->hits[
'masterConns']++ >= $this->expect[
'masterConns'] ) {
186 'masterConns',
"[connect to $server ($db)]", $this->hits[
'masterConns'] );
200 $name =
"{$server} ({$db}) (TRX#$id)";
201 if ( isset( $this->dbTrxHoldingLocks[$name] ) ) {
202 $this->logger->warning(
"Nested transaction for '$name' - out of sync." );
204 $this->dbTrxHoldingLocks[$name] = [
205 'start' => microtime(
true ),
208 $this->dbTrxMethodTimes[$name] = [];
210 foreach ( $this->dbTrxHoldingLocks as $name => &$info ) {
212 $info[
'conns'][$name] = 1;
227 $eTime = microtime(
true );
228 $elapsed = ( $eTime - $sTime );
230 if ( $isWrite && $n > $this->expect[
'maxAffected'] ) {
231 $this->logger->warning(
232 "Query affected $n row(s):\n" . self::queryString( $query ) .
"\n" .
233 (
new RuntimeException() )->getTraceAsString() );
234 } elseif ( !$isWrite && $n > $this->expect[
'readQueryRows'] ) {
235 $this->logger->warning(
236 "Query returned $n row(s):\n" . self::queryString( $query ) .
"\n" .
237 (
new RuntimeException() )->getTraceAsString() );
241 if ( $this->hits[
'queries']++ >= $this->expect[
'queries'] ) {
244 if ( $isWrite && $this->hits[
'writes']++ >= $this->expect[
'writes'] ) {
248 if ( !$isWrite && $elapsed > $this->expect[
'readQueryTime'] ) {
251 if ( $isWrite && $elapsed > $this->expect[
'writeQueryTime'] ) {
255 if ( !$this->dbTrxHoldingLocks ) {
258 } elseif ( !$isWrite && $elapsed < $this->eventThreshold ) {
263 foreach ( $this->dbTrxHoldingLocks as $name => $info ) {
264 $lastQuery = end( $this->dbTrxMethodTimes[$name] );
267 $lastEnd = $lastQuery[2];
268 if ( $sTime >= $lastEnd ) {
269 if ( ( $sTime - $lastEnd ) > $this->eventThreshold ) {
271 $this->dbTrxMethodTimes[$name][] = [
'...delay...', $lastEnd, $sTime ];
273 $this->dbTrxMethodTimes[$name][] = [ $query, $sTime, $eTime ];
277 if ( $sTime >= $info[
'start'] ) {
278 $this->dbTrxMethodTimes[$name][] = [ $query, $sTime, $eTime ];
298 $name =
"{$server} ({$db}) (TRX#$id)";
299 if ( !isset( $this->dbTrxMethodTimes[$name] ) ) {
300 $this->logger->warning(
"Detected no transaction for '$name' - out of sync." );
307 if ( $writeTime > $this->expect[
'writeQueryTime'] ) {
310 "[transaction $id writes to {$server} ({$db})]",
316 if ( $affected > $this->expect[
'maxAffected'] ) {
319 "[transaction $id writes to {$server} ({$db})]",
324 $lastQuery = end( $this->dbTrxMethodTimes[$name] );
326 $now = microtime(
true );
327 $lastEnd = $lastQuery[2];
328 if ( ( $now - $lastEnd ) > $this->eventThreshold ) {
329 $this->dbTrxMethodTimes[$name][] = [
'...delay...', $lastEnd, $now ];
333 foreach ( $this->dbTrxMethodTimes[$name] as $info ) {
334 $elapsed = ( $info[2] - $info[1] );
335 if ( $elapsed >= $this->dbLockThreshold ) {
342 foreach ( $this->dbTrxMethodTimes[$name] as $i => $info ) {
343 list( $query, $sTime, $end ) = $info;
345 "%d\t%.6f\t%s\n", $i, ( $end - $sTime ), self::queryString( $query ) );
347 $this->logger->warning(
"Sub-optimal transaction on DB(s) [{dbs}]: \n{trace}", [
348 'dbs' => implode(
', ', array_keys( $this->dbTrxHoldingLocks[$name][
'conns'] ) ),
352 unset( $this->dbTrxHoldingLocks[$name] );
353 unset( $this->dbTrxMethodTimes[$name] );
362 if ( $this->silenced ) {
366 $this->logger->warning(
367 "Expectation ({measure} <= {max}) by {by} not met (actual: {actual}):\n{query}\n" .
368 (
new RuntimeException() )->getTraceAsString(),
371 'max' => $this->expect[
$expect],
372 'by' => $this->expectBy[
$expect],
374 'query' => self::queryString( $query )