| @@ -162,17 +162,14 @@ | ||
| 162 | 162 | if ($this->is_endpoint_in_backoff($url)) { |
| 163 | 163 | $endpoint_hash = $this->get_endpoint_hash($url); |
| 164 | 164 | $backoff_data = $this->get_backoff_state($endpoint_hash); |
| 165 | 165 | |
| 166 | - // Log the blocked request | |
| 167 | - $this->log_backoff_event( | |
| 168 | - 'API_BACKOFF_BLOCKED', | |
| 169 | - sprintf( | |
| 170 | - 'Request blocked due to active backoff. Endpoint: %s, Time remaining: %d seconds', | |
| 171 | - $this->extract_endpoint($url), | |
| 172 | - $backoff_data['time_remaining'] | |
| 173 | - ) | |
| 174 | - ); | |
| 166 | + // Deliberately NOT logged. This runs once per blocked request, so on a | |
| 167 | + // busy site in backoff it would write a log line and an error-summary | |
| 168 | + // row per request — write amplification at precisely the moment the | |
| 169 | + // site is already struggling. The state transitions below | |
| 170 | + // (triggered / cleared / counter reset) are what carry the diagnostic | |
| 171 | + // value, and they fire once each. | |
| 175 | 172 | |
| 176 | 173 | // Return WP_Error to prevent the request |
| 177 | 174 | return new WP_Error( |
| 178 | 175 | 'api_backoff_active', |
| @@ -368,9 +365,9 @@ | ||
| 368 | 365 | |
| 369 | 366 | $backoffs = []; |
| 370 | 367 | |
| 371 | 368 | // Query all backoff transients |
| 372 | - $transient_keys = $wpdb->get_col( | |
| 369 | + $transient_keys = $wpdb->get_col( // phpcs:ignore WordPress.DB.DirectDatabaseQuery -- transient enumeration on the options table — no API to scan transient rows | |
| 373 | 370 | $wpdb->prepare( |
| 374 | 371 | "SELECT option_name FROM {$wpdb->options} |
| 375 | 372 | WHERE option_name LIKE %s", |
| 376 | 373 | '_transient_' . self::BACKOFF_PREFIX . '%' |
| @@ -425,9 +422,9 @@ | ||
| 425 | 422 | self::LAST_SUCCESS_PREFIX, |
| 426 | 423 | ]; |
| 427 | 424 | |
| 428 | 425 | foreach ($prefixes as $prefix) { |
| 429 | - $transient_keys = $wpdb->get_col( | |
| 426 | + $transient_keys = $wpdb->get_col( // phpcs:ignore WordPress.DB.DirectDatabaseQuery -- transient enumeration on the options table — no API to scan transient rows | |
| 430 | 427 | $wpdb->prepare( |
| 431 | 428 | "SELECT option_name FROM {$wpdb->options} |
| 432 | 429 | WHERE option_name LIKE %s |
| 433 | 430 | OR option_name LIKE %s", |
| @@ -497,9 +494,29 @@ | ||
| 497 | 494 | * @param string $event_type Event type identifier. |
| 498 | 495 | * @param string $message Log message. |
| 499 | 496 | */ |
| 500 | 497 | private function log_backoff_event($event_type, $message) { |
| 501 | - // Backoff events are operational noise; suppress from error log. | |
| 498 | + // This was previously an empty stub, which made fleet-wide backoff behaviour | |
| 499 | + // completely invisible — we could not tell from a customer's logs whether | |
| 500 | + // backoff had triggered, blocked, or cleared. | |
| 501 | + // | |
| 502 | + // Routed through Metasync_Error_Logger rather than error_log() so it stays | |
| 503 | + // suppressed by default: CATEGORY_API_BACKOFF is in DEBUG_ONLY_CATEGORIES, | |
| 504 | + // so nothing is written unless Debug Mode is on. That keeps the original | |
| 505 | + // "operational noise" concern satisfied while making diagnosis possible. | |
| 506 | + if (!class_exists('Metasync_Error_Logger')) { | |
| 507 | + return; | |
| 508 | + } | |
| 509 | + | |
| 510 | + Metasync_Error_Logger::log( | |
| 511 | + Metasync_Error_Logger::CATEGORY_API_BACKOFF, | |
| 512 | + Metasync_Error_Logger::SEVERITY_INFO, | |
| 513 | + $message, | |
| 514 | + [ | |
| 515 | + 'event_type' => $event_type, | |
| 516 | + 'operation' => 'api_backoff', | |
| 517 | + ] | |
| 518 | + ); | |
| 502 | 519 | } |
| 503 | 520 | |
| 504 | 521 | /** |
| 505 | 522 | * Get formatted time remaining |