26 use Psr\Log\LoggerInterface;
27 use Psr\Log\LoggerAwareInterface;
28 use Psr\Log\NullLogger;
65 'readQueryRows' => INF,
66 'readQueryTime' => INF,
67 'writeQueryTime' => INF
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;
126 foreach ( $expects
as $event =>
$value ) {
137 foreach ( $this->hits
as &$val ) {
141 foreach ( $this->expect
as &$val ) {
145 $this->expectBy = [];
159 if ( $this->hits[
'conns']++ >= $this->expect[
'conns'] ) {
161 'conns',
"[connect to $server ($db)]", $this->hits[
'conns'] );
163 if ( $isMaster && $this->hits[
'masterConns']++ >= $this->expect[
'masterConns'] ) {
165 'masterConns',
"[connect to $server ($db)]", $this->hits[
'masterConns'] );
179 $name =
"{$server} ({$db}) (TRX#$id)";
180 if ( isset( $this->dbTrxHoldingLocks[
$name] ) ) {
181 $this->logger->warning(
"Nested transaction for '$name' - out of sync." );
183 $this->dbTrxHoldingLocks[
$name] = [
184 'start' => microtime(
true ),
187 $this->dbTrxMethodTimes[
$name] = [];
189 foreach ( $this->dbTrxHoldingLocks
as $name => &$info ) {
191 $info[
'conns'][
$name] = 1;
206 $eTime = microtime(
true );
207 $elapsed = ( $eTime - $sTime );
209 if ( $isWrite && $n > $this->expect[
'maxAffected'] ) {
210 $this->logger->warning(
211 "Query affected $n row(s):\n" .
$query .
"\n" .
212 (
new RuntimeException() )->getTraceAsString() );
213 } elseif ( !$isWrite && $n > $this->expect[
'readQueryRows'] ) {
214 $this->logger->warning(
215 "Query returned $n row(s):\n" .
$query .
"\n" .
216 (
new RuntimeException() )->getTraceAsString() );
220 if ( $this->hits[
'queries']++ >= $this->expect[
'queries'] ) {
223 if ( $isWrite && $this->hits[
'writes']++ >= $this->expect[
'writes'] ) {
227 if ( !$isWrite && $elapsed > $this->expect[
'readQueryTime'] ) {
230 if ( $isWrite && $elapsed > $this->expect[
'writeQueryTime'] ) {
234 if ( !$this->dbTrxHoldingLocks ) {
237 } elseif ( !$isWrite && $elapsed < $this->eventThreshold ) {
242 foreach ( $this->dbTrxHoldingLocks
as $name => $info ) {
243 $lastQuery = end( $this->dbTrxMethodTimes[
$name] );
246 $lastEnd = $lastQuery[2];
247 if ( $sTime >= $lastEnd ) {
248 if ( ( $sTime - $lastEnd ) > $this->eventThreshold ) {
250 $this->dbTrxMethodTimes[
$name][] = [
'...delay...', $lastEnd, $sTime ];
252 $this->dbTrxMethodTimes[
$name][] = [
$query, $sTime, $eTime ];
256 if ( $sTime >= $info[
'start'] ) {
257 $this->dbTrxMethodTimes[
$name][] = [
$query, $sTime, $eTime ];
277 $name =
"{$server} ({$db}) (TRX#$id)";
278 if ( !isset( $this->dbTrxMethodTimes[
$name] ) ) {
279 $this->logger->warning(
"Detected no transaction for '$name' - out of sync." );
286 if ( $writeTime > $this->expect[
'writeQueryTime'] ) {
289 "[transaction $id writes to {$server} ({$db})]",
295 if ( $affected > $this->expect[
'maxAffected'] ) {
298 "[transaction $id writes to {$server} ({$db})]",
303 $lastQuery = end( $this->dbTrxMethodTimes[
$name] );
305 $now = microtime(
true );
306 $lastEnd = $lastQuery[2];
307 if ( ( $now - $lastEnd ) > $this->eventThreshold ) {
308 $this->dbTrxMethodTimes[
$name][] = [
'...delay...', $lastEnd, $now ];
312 foreach ( $this->dbTrxMethodTimes[
$name]
as $info ) {
313 $elapsed = ( $info[2] - $info[1] );
314 if ( $elapsed >= $this->dbLockThreshold ) {
321 foreach ( $this->dbTrxMethodTimes[
$name]
as $i => $info ) {
323 $trace .= sprintf(
"%d\t%.6f\t%s\n", $i, ( $end - $sTime ),
$query );
325 $this->logger->warning(
"Sub-optimal transaction on DB(s) [{dbs}]: \n{trace}", [
326 'dbs' => implode(
', ', array_keys( $this->dbTrxHoldingLocks[
$name][
'conns'] ) ),
330 unset( $this->dbTrxHoldingLocks[
$name] );
331 unset( $this->dbTrxMethodTimes[
$name] );
340 if ( $this->silenced ) {
344 $this->logger->warning(
345 "Expectation ({measure} <= {max}) by {by} not met (actual: {actual}):\n{query}\n" .
346 (
new RuntimeException() )->getTraceAsString(),
349 'max' => $this->expect[
$expect],
350 'by' => $this->expectBy[
$expect],