26 use Psr\Log\LoggerInterface;
27 use Psr\Log\LoggerAwareInterface;
28 use Psr\Log\NullLogger;
65 'readQueryTime' => INF,
66 'writeQueryTime' => INF
107 $this->expect[$event] = isset( $this->expect[$event] )
108 ? min( $this->expect[$event],
$value )
110 if ( $this->expect[$event] ==
$value ) {
111 $this->expectBy[$event] =
$fname;
125 foreach ( $expects
as $event =>
$value ) {
136 foreach ( $this->hits
as &$val ) {
140 foreach ( $this->expect
as &$val ) {
144 $this->expectBy = [];
158 if ( $this->hits[
'conns']++ >= $this->expect[
'conns'] ) {
160 'conns',
"[connect to $server ($db)]", $this->hits[
'conns'] );
162 if ( $isMaster && $this->hits[
'masterConns']++ >= $this->expect[
'masterConns'] ) {
164 'masterConns',
"[connect to $server ($db)]", $this->hits[
'masterConns'] );
178 $name =
"{$server} ({$db}) (TRX#$id)";
179 if ( isset( $this->dbTrxHoldingLocks[
$name] ) ) {
180 $this->logger->info(
"Nested transaction for '$name' - out of sync." );
182 $this->dbTrxHoldingLocks[
$name] = [
183 'start' => microtime(
true ),
186 $this->dbTrxMethodTimes[
$name] = [];
188 foreach ( $this->dbTrxHoldingLocks
as $name => &$info ) {
190 $info[
'conns'][
$name] = 1;
205 $eTime = microtime(
true );
206 $elapsed = ( $eTime - $sTime );
208 if ( $isWrite && $n > $this->expect[
'maxAffected'] ) {
210 "Query affected $n row(s):\n" .
$query .
"\n" .
211 (
new RuntimeException() )->getTraceAsString() );
215 if ( $this->hits[
'queries']++ >= $this->expect[
'queries'] ) {
218 if ( $isWrite && $this->hits[
'writes']++ >= $this->expect[
'writes'] ) {
222 if ( !$isWrite && $elapsed > $this->expect[
'readQueryTime'] ) {
225 if ( $isWrite && $elapsed > $this->expect[
'writeQueryTime'] ) {
229 if ( !$this->dbTrxHoldingLocks ) {
232 } elseif ( !$isWrite && $elapsed < $this->eventThreshold ) {
237 foreach ( $this->dbTrxHoldingLocks
as $name => $info ) {
238 $lastQuery = end( $this->dbTrxMethodTimes[
$name] );
241 $lastEnd = $lastQuery[2];
242 if ( $sTime >= $lastEnd ) {
243 if ( ( $sTime - $lastEnd ) > $this->eventThreshold ) {
245 $this->dbTrxMethodTimes[
$name][] = [
'...delay...', $lastEnd, $sTime ];
247 $this->dbTrxMethodTimes[
$name][] = [
$query, $sTime, $eTime ];
251 if ( $sTime >= $info[
'start'] ) {
252 $this->dbTrxMethodTimes[
$name][] = [
$query, $sTime, $eTime ];
272 $name =
"{$server} ({$db}) (TRX#$id)";
273 if ( !isset( $this->dbTrxMethodTimes[
$name] ) ) {
274 $this->logger->info(
"Detected no transaction for '$name' - out of sync." );
281 if ( $writeTime > $this->expect[
'writeQueryTime'] ) {
284 "[transaction $id writes to {$server} ({$db})]",
290 if ( $affected > $this->expect[
'maxAffected'] ) {
293 "[transaction $id writes to {$server} ({$db})]",
298 $lastQuery = end( $this->dbTrxMethodTimes[
$name] );
300 $now = microtime(
true );
301 $lastEnd = $lastQuery[2];
302 if ( ( $now - $lastEnd ) > $this->eventThreshold ) {
303 $this->dbTrxMethodTimes[
$name][] = [
'...delay...', $lastEnd, $now ];
307 foreach ( $this->dbTrxMethodTimes[
$name]
as $info ) {
308 $elapsed = ( $info[2] - $info[1] );
309 if ( $elapsed >= $this->dbLockThreshold ) {
316 foreach ( $this->dbTrxMethodTimes[
$name]
as $i => $info ) {
318 $trace .= sprintf(
"%d\t%.6f\t%s\n", $i, ( $end - $sTime ),
$query );
320 $this->logger->info(
"Sub-optimal transaction on DB(s) [{dbs}]: \n{trace}", [
321 'dbs' => implode(
', ', array_keys( $this->dbTrxHoldingLocks[
$name][
'conns'] ) ),
325 unset( $this->dbTrxHoldingLocks[
$name] );
326 unset( $this->dbTrxMethodTimes[
$name] );
335 if ( $this->silenced ) {
340 "Expectation ({measure} <= {max}) by {by} not met (actual: {actual}):\n{query}\n" .
341 (
new RuntimeException() )->getTraceAsString(),
344 'max' => $this->expect[
$expect],
345 'by' => $this->expectBy[
$expect],