Changeset 3412283
- Timestamp:
- 12/05/2025 02:32:34 PM (4 months ago)
- Location:
- dynamic-front-end-heartbeat-control
- Files:
-
- 35 added
- 14 edited
-
tags/1.2.996 (added)
-
tags/1.2.996/LICENSE (added)
-
tags/1.2.996/admin (added)
-
tags/1.2.996/admin/affix.php (added)
-
tags/1.2.996/admin/ajax-handler.php (added)
-
tags/1.2.996/admin/asset-manager.php (added)
-
tags/1.2.996/admin/heartbeat-config.php (added)
-
tags/1.2.996/admin/unclogger-menu.php (added)
-
tags/1.2.996/css (added)
-
tags/1.2.996/css/dfhcsl-admin.css (added)
-
tags/1.2.996/defibrillator (added)
-
tags/1.2.996/defibrillator/cli-helper.php (added)
-
tags/1.2.996/defibrillator/db-health.php (added)
-
tags/1.2.996/defibrillator/load-estimator.php (added)
-
tags/1.2.996/defibrillator/rest-api.php (added)
-
tags/1.2.996/defibrillator/unclogger-db.php (added)
-
tags/1.2.996/defibrillator/unclogger.php (added)
-
tags/1.2.996/engine (added)
-
tags/1.2.996/engine/interval-helper.php (added)
-
tags/1.2.996/engine/server-load.php (added)
-
tags/1.2.996/engine/server-response.php (added)
-
tags/1.2.996/engine/system-load-fallback.php (added)
-
tags/1.2.996/heartbeat-async.php (added)
-
tags/1.2.996/heartbeat-controller.php (added)
-
tags/1.2.996/js (added)
-
tags/1.2.996/js/chart.js (added)
-
tags/1.2.996/js/dfhcsl-admin.js (added)
-
tags/1.2.996/js/heartbeat.js (added)
-
tags/1.2.996/js/heartbeat.min.js (added)
-
tags/1.2.996/readme.txt (added)
-
tags/1.2.996/settings.php (added)
-
tags/1.2.996/visitor (added)
-
tags/1.2.996/visitor/cookie-helper.php (added)
-
tags/1.2.996/visitor/manager.php (added)
-
tags/1.2.996/widget.php (added)
-
trunk/admin/asset-manager.php (modified) (1 diff)
-
trunk/defibrillator/db-health.php (modified) (7 diffs)
-
trunk/defibrillator/load-estimator.php (modified) (2 diffs)
-
trunk/engine/interval-helper.php (modified) (4 diffs)
-
trunk/engine/server-load.php (modified) (6 diffs)
-
trunk/engine/server-response.php (modified) (9 diffs)
-
trunk/engine/system-load-fallback.php (modified) (1 diff)
-
trunk/heartbeat-async.php (modified) (4 diffs)
-
trunk/heartbeat-controller.php (modified) (6 diffs)
-
trunk/js/heartbeat.js (modified) (1 diff)
-
trunk/js/heartbeat.min.js (modified) (1 diff)
-
trunk/readme.txt (modified) (3 diffs)
-
trunk/visitor/cookie-helper.php (modified) (8 diffs)
-
trunk/visitor/manager.php (modified) (4 diffs)
Legend:
- Unmodified
- Added
- Removed
-
dynamic-front-end-heartbeat-control/trunk/admin/asset-manager.php
r3310561 r3412283 25 25 .collapsible-header .toggle-indicator{float:right;font-size:1.2em;line-height:1;font-weight:bold} 26 26 #dfehc-optimizer-form button{margin-right:5px;margin-bottom:5px} 27 .database-health-status{display:inline-block;width:20px;height:20px;border-radius:50%;background-color:var(--c);animation:heartbeat 1s linear infinite;box-shadow:0 0 10px var(--c)} 28 @keyframes heartbeat{50%{box-shadow:0 0 25px var(--c)}} 27 .database-health-status{display:inline-block;width:22px;height:22px;border-radius:7px;background:radial-gradient(circle at 30% 22%,rgba(255,255,255,.5) 0,transparent 40%),radial-gradient(ellipse at top,rgba(255,255,255,.28) 12%,transparent 55%),linear-gradient(to bottom,rgba(255,255,255,.05),rgba(0,0,0,.22)),radial-gradient(ellipse at top,var(--c) 72%,transparent 73%),radial-gradient(ellipse at bottom,var(--c) 72%,transparent 73%);background-size:100% 100%,100% 38%,100% 100%,100% 38%,100% 38%;background-position:center,top,center,top,bottom;background-repeat:no-repeat;animation:heartbeat 2.3s ease-in-out infinite;box-shadow:0 0 8px var(--c),0 5px 12px rgba(0,0,0,.32)} 29 28 .dfehc-loader-overlay{display:none;position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(255,255,255,.85);z-index:9999;text-align:center} 30 29 .dfehc-loader-content{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)} -
dynamic-front-end-heartbeat-control/trunk/defibrillator/db-health.php
r3396626 r3412283 10 10 } 11 11 12 function dfehc_scoped_key(string $base): string { 13 return $base . '_' . dfehc_blog_id(); 14 } 15 16 function dfehc_cache_group(): string { 17 return \defined('DFEHC_CACHE_GROUP') ? (string) \DFEHC_CACHE_GROUP : 'dfehc'; 18 } 19 20 function dfehc_cache_get(string $key) { 21 if (\function_exists('\wp_using_ext_object_cache') && \wp_using_ext_object_cache()) { 22 return \wp_cache_get($key, dfehc_cache_group()); 23 } 24 return \get_site_transient($key); 25 } 26 27 function dfehc_cache_set(string $key, $value, int $ttl): void { 28 if (\function_exists('\wp_using_ext_object_cache') && \wp_using_ext_object_cache()) { 29 \wp_cache_set($key, $value, dfehc_cache_group(), $ttl); 30 return; 31 } 32 \set_site_transient($key, $value, $ttl); 12 if (!\function_exists(__NAMESPACE__ . '\\dfehc_host_token')) { 13 function dfehc_host_token(): string { 14 static $t = ''; 15 if ($t !== '') { 16 return $t; 17 } 18 $host = @\php_uname('n') 19 ?: (\defined('WP_HOME') 20 ? WP_HOME 21 : (\function_exists('\home_url') ? \home_url() : 'unknown')); 22 $salt = \defined('DB_NAME') ? (string) DB_NAME : ''; 23 return $t = \substr(\md5((string) $host . $salt), 0, 10); 24 } 25 } 26 27 if (!\function_exists(__NAMESPACE__ . '\\dfehc_scoped_key')) { 28 function dfehc_scoped_key(string $base): string { 29 return $base . '_' . dfehc_blog_id() . '_' . dfehc_host_token(); 30 } 31 } 32 33 if (!\function_exists(__NAMESPACE__ . '\\dfehc_cache_group')) { 34 function dfehc_cache_group(): string { 35 return \defined('DFEHC_CACHE_GROUP') ? (string) \DFEHC_CACHE_GROUP : 'dfehc'; 36 } 37 } 38 39 if (!\function_exists(__NAMESPACE__ . '\\dfehc_cache_get')) { 40 function dfehc_cache_get(string $key) { 41 if (\function_exists('\wp_using_ext_object_cache') && \wp_using_ext_object_cache()) { 42 return \wp_cache_get($key, dfehc_cache_group()); 43 } 44 return \get_site_transient($key); 45 } 46 } 47 48 if (!\function_exists(__NAMESPACE__ . '\\dfehc_cache_set')) { 49 function dfehc_cache_set(string $key, $value, int $ttl): void { 50 $jitter = \function_exists('\random_int') ? \random_int(0, 5) : 0; 51 $ttl = \max(1, $ttl + $jitter); 52 53 if (\function_exists('\wp_using_ext_object_cache') && \wp_using_ext_object_cache()) { 54 \wp_cache_set($key, $value, dfehc_cache_group(), $ttl); 55 return; 56 } 57 \set_site_transient($key, $value, $ttl); 58 } 33 59 } 34 60 … … 70 96 $sources = []; 71 97 $metrics = [ 72 'revision_count' => 0,73 'trashed_posts_count' => 0,74 'expired_transients_count' => 0,75 'order_count' => 0,76 'product_count' => 0,77 'page_count' => 0,78 'customer_count' => 0,79 'user_count' => 0,80 'db_size_mb' => 0.0,81 'disk_free_space_mb' => 0.0,82 'collected_at' => \current_time('mysql'),83 'cache_backend' => (\function_exists('\wp_using_ext_object_cache') && \wp_using_ext_object_cache()) ? 'object' : 'transient',98 'revision_count' => 0, 99 'trashed_posts_count' => 0, 100 'expired_transients_count' => 0, 101 'order_count' => 0, 102 'product_count' => 0, 103 'page_count' => 0, 104 'customer_count' => 0, 105 'user_count' => 0, 106 'db_size_mb' => 0.0, 107 'disk_free_space_mb' => 0.0, 108 'collected_at' => \current_time('mysql'), 109 'cache_backend' => (\function_exists('\wp_using_ext_object_cache') && \wp_using_ext_object_cache()) ? 'object' : 'transient', 84 110 ]; 85 111 … … 160 186 161 187 if ($toggles['db_size'] && $within_budget()) { 162 $db_size_mb = dfehc_cache_get($db_size_key);188 $db_size_mb = dfehc_cache_get($db_size_key); 163 189 $db_size_fail = dfehc_cache_get($db_size_fail_key); 190 164 191 if ($db_size_mb === false && $db_size_fail === false) { 165 192 $prev = $wpdb->suppress_errors(); … … 236 263 ]); 237 264 $estimate = 0.0; 238 $estimate += $metrics['user_count'] * (float) ($multipliers['user'] ?? 0.03);239 $estimate += $metrics['order_count'] * (float) ($multipliers['order'] ?? 0.05);240 $estimate += $metrics['revision_count'] * (float) ($multipliers['revision'] ?? 0.05);241 $estimate += $metrics['trashed_posts_count'] * (float) ($multipliers['trash'] ?? 0.05);242 $estimate += $metrics['page_count'] * (float) ($multipliers['page'] ?? 0.10);243 $estimate += $metrics['expired_transients_count'] * (float) ($multipliers['transient'] ?? 0.01);265 $estimate += $metrics['user_count'] * (float) ($multipliers['user'] ?? 0.03); 266 $estimate += $metrics['order_count'] * (float) ($multipliers['order'] ?? 0.05); 267 $estimate += $metrics['revision_count'] * (float) ($multipliers['revision'] ?? 0.05); 268 $estimate += $metrics['trashed_posts_count'] * (float) ($multipliers['trash'] ?? 0.05); 269 $estimate += $metrics['page_count'] * (float) ($multipliers['page'] ?? 0.10); 270 $estimate += $metrics['expired_transients_count'] * (float) ($multipliers['transient'] ?? 0.01); 244 271 if ($estimate <= 0) { 245 272 $estimate = (float) ($multipliers['default'] ?? 500.0); … … 277 304 } 278 305 if ($disk_free_space_mb === null) { 279 $fallback = (float) \apply_filters('dfehc_disk_free_default_mb', \round((50 * 1024 * 1024 * 1024) / 1024 / 1024, 2)); 306 $fallback = (float) \apply_filters( 307 'dfehc_disk_free_default_mb', 308 \round((50 * 1024 * 1024 * 1024) / 1024 / 1024, 2) 309 ); 280 310 $disk_free_space_mb = $fallback; 281 311 $sources['disk_free_space_mb'] = 'default'; … … 283 313 $metrics['disk_free_space_mb'] = (float) $disk_free_space_mb; 284 314 } else { 285 $metrics['disk_free_space_mb'] = (float) \apply_filters('dfehc_disk_free_default_mb', \round((50 * 1024 * 1024 * 1024) / 1024 / 1024, 2)); 315 $metrics['disk_free_space_mb'] = (float) \apply_filters( 316 'dfehc_disk_free_default_mb', 317 \round((50 * 1024 * 1024 * 1024) / 1024 / 1024, 2) 318 ); 286 319 $sources['disk_free_space_mb'] = $toggles['disk'] ? 'skipped' : 'disabled'; 287 320 } … … 336 369 $thresholds = \apply_filters('dfehc_database_health_thresholds', $thresholds, $metrics); 337 370 338 if (((int) ($metrics['revision_count'] ?? 0)) > (int) ($thresholds['revision'] ?? 0)) $conditions_met++;371 if (((int) ($metrics['revision_count'] ?? 0)) > (int) ($thresholds['revision'] ?? 0)) $conditions_met++; 339 372 if (((int) ($metrics['trashed_posts_count'] ?? 0)) > (int) ($thresholds['trash'] ?? 0)) $conditions_met++; 340 373 if (((int) ($metrics['expired_transients_count'] ?? 0)) > (int) ($thresholds['transients'] ?? 0)) $conditions_met++; -
dynamic-front-end-heartbeat-control/trunk/defibrillator/load-estimator.php
r3396626 r3412283 362 362 363 363 private static function set_transient_noautoload(string $key, $value, int $ttl): void { 364 $jitter = (\function_exists('random_int') ? \random_int(0, 5) : 0); 364 $jitter = 0; 365 if (\function_exists('random_int')) { 366 try { 367 $jitter = \random_int(0, 5); 368 } catch (\Throwable $e) { 369 $jitter = 0; 370 } 371 } 365 372 $ttl = \max(1, $ttl + $jitter); 366 373 if (\function_exists('wp_using_ext_object_cache') && \wp_using_ext_object_cache()) { … … 388 395 389 396 private static function set_site_transient_noautoload(string $key, $value, int $ttl): void { 390 $jitter = (\function_exists('random_int') ? \random_int(0, 5) : 0); 397 $jitter = 0; 398 if (\function_exists('random_int')) { 399 try { 400 $jitter = \random_int(0, 5); 401 } catch (\Throwable $e) { 402 $jitter = 0; 403 } 404 } 391 405 $ttl = \max(1, $ttl + $jitter); 392 406 if (\function_exists('wp_using_ext_object_cache') && \wp_using_ext_object_cache()) { -
dynamic-front-end-heartbeat-control/trunk/engine/interval-helper.php
r3396626 r3412283 54 54 } 55 55 56 if (!function_exists('dfehc_set_transient_noautoload')) {57 function dfehc_set_transient_noautoload(string $key, $value, int $expiration): void {58 $group = defined('DFEHC_CACHE_GROUP') ? DFEHC_CACHE_GROUP : 'dfehc';59 if (function_exists('wp_using_ext_object_cache') && wp_using_ext_object_cache()) {60 if (function_exists('wp_cache_add')) {61 if (!wp_cache_add($key, $value, $group, $expiration)) {62 wp_cache_set($key, $value, $group, $expiration);63 }64 } else {65 wp_cache_set($key, $value, $group, $expiration);66 }67 return;68 }69 set_transient($key, $value, $expiration);70 global $wpdb;71 if (!isset($wpdb->options)) return;72 $opt_key = "_transient_$key";73 $opt_key_to = "_transient_timeout_$key";74 $wpdb->suppress_errors(true);75 $autoload = $wpdb->get_var($wpdb->prepare("SELECT autoload FROM {$wpdb->options} WHERE option_name=%s LIMIT 1", $opt_key));76 if ($autoload === 'yes') {77 $wpdb->update($wpdb->options, ['autoload' => 'no'], ['option_name' => $opt_key, 'autoload' => 'yes'], ['%s'], ['%s','%s']);78 }79 $autoload_to = $wpdb->get_var($wpdb->prepare("SELECT autoload FROM {$wpdb->options} WHERE option_name=%s LIMIT 1", $opt_key_to));80 if ($autoload_to === 'yes') {81 $wpdb->update($wpdb->options, ['autoload' => 'no'], ['option_name' => $opt_key_to, 'autoload' => 'yes'], ['%s'], ['%s','%s']);82 }83 $wpdb->suppress_errors(false);84 }85 }86 87 56 if (!function_exists('dfehc_set_transient')) { 88 57 function dfehc_set_transient(string $key, float $value, float $interval): void { 89 58 $ttl = (int) apply_filters('dfehc_transient_ttl', max(60, (int) ceil($interval) * 2), $key, $value, $interval); 90 $ttl += function_exists('random_int') ? random_int(0, 5) : 0; 59 $jitter = 0; 60 if (function_exists('random_int')) { 61 try { 62 $jitter = random_int(0, 5); 63 } catch (\Throwable $e) { 64 $jitter = 0; 65 } 66 } 67 $ttl += $jitter; 91 68 dfehc_set_transient_noautoload($key, $value, $ttl); 92 69 } … … 136 113 $ttl_default = (int) dfehc_clamp($ttl_default, 60, 86400); 137 114 $ttl = (int) apply_filters('dfehc_ema_ttl', $ttl_default, $current, $ema); 138 $ttl += function_exists('random_int') ? random_int(0, 5) : 0; 115 $jitter = 0; 116 if (function_exists('random_int')) { 117 try { 118 $jitter = random_int(0, 5); 119 } catch (\Throwable $e) { 120 $jitter = 0; 121 } 122 } 123 $ttl += $jitter; 139 124 dfehc_set_transient_noautoload($key, $ema, $ttl); 140 125 return $ema; … … 233 218 if ($previous === false) { 234 219 $ttl = (int) apply_filters('dfehc_prev_interval_ttl', 1800); 235 $ttl += function_exists('random_int') ? random_int(0, 5) : 0; 220 $jitter = 0; 221 if (function_exists('random_int')) { 222 try { 223 $jitter = random_int(0, 5); 224 } catch (\Throwable $e) { 225 $jitter = 0; 226 } 227 } 228 $ttl += $jitter; 236 229 dfehc_set_transient_noautoload($key, $proposed, $ttl); 237 230 return $proposed; … … 244 237 $final = dfehc_clamp($proposed, $lower, $upper); 245 238 $ttl = (int) apply_filters('dfehc_prev_interval_ttl', 1800); 246 $ttl += function_exists('random_int') ? random_int(0, 5) : 0; 239 $jitter = 0; 240 if (function_exists('random_int')) { 241 try { 242 $jitter = random_int(0, 5); 243 } catch (\Throwable $e) { 244 $jitter = 0; 245 } 246 } 247 $ttl += $jitter; 247 248 dfehc_set_transient_noautoload($key, $final, $ttl); 248 249 return $final; -
dynamic-front-end-heartbeat-control/trunk/engine/server-load.php
r3396626 r3412283 186 186 $key = dfehc_key(DFEHC_SERVER_LOAD_CACHE_KEY); 187 187 $ttl = dfehc_server_load_ttl(); 188 $jitter = 0; 189 if (function_exists('random_int')) { 190 try { 191 $jitter = random_int(0, 5); 192 } catch (Throwable $e) { 193 $jitter = 0; 194 } 195 } 196 $ttl += $jitter; 188 197 try { 189 $ttl += function_exists('random_int') ? random_int(0, 5) : 0;190 198 if ($type === 'redis') { 191 199 $client->setex($key, $ttl, $value); … … 224 232 ]; 225 233 $ttl = dfehc_server_load_ttl(); 226 $ttl += function_exists('random_int') ? random_int(0, 5) : 0; 234 $jitter = 0; 235 if (function_exists('random_int')) { 236 try { 237 $jitter = random_int(0, 5); 238 } catch (Throwable $e) { 239 $jitter = 0; 240 } 241 } 242 $ttl += $jitter; 227 243 if (wp_using_ext_object_cache()) { 228 244 wp_cache_set($payloadKey, $payload, DFEHC_CACHE_GROUP, $ttl); … … 287 303 } 288 304 289 function dfehc_get_cpu_cores(): int 290 { 291 static $cores = null; 292 if ($cores !== null) { 293 return (int) $cores; 294 } 295 $override = getenv('DFEHC_CPU_CORES'); 296 if ($override && ctype_digit((string) $override) && (int) $override > 0) { 297 $cores = (int) $override; 298 return (int) $cores; 299 } 300 if (is_readable('/sys/fs/cgroup/cpu.max')) { 301 $line = file_get_contents('/sys/fs/cgroup/cpu.max'); 302 if ($line !== false) { 303 [$quota, $period] = explode(' ', trim($line)); 304 if ($quota !== 'max') { 305 $q = (int) $quota; 306 $p = (int) $period; 307 if ($q > 0 && $p > 0) { 308 $cores = max(1, (int) ceil($q / $p)); 305 if (!function_exists('dfehc_get_cpu_cores')) { 306 function dfehc_get_cpu_cores(): int 307 { 308 static $cores = null; 309 if ($cores !== null) { 310 return (int) $cores; 311 } 312 $override = getenv('DFEHC_CPU_CORES'); 313 if ($override && ctype_digit((string) $override) && (int) $override > 0) { 314 $cores = (int) $override; 315 return (int) $cores; 316 } 317 if (is_readable('/sys/fs/cgroup/cpu.max')) { 318 $line = file_get_contents('/sys/fs/cgroup/cpu.max'); 319 if ($line !== false) { 320 [$quota, $period] = explode(' ', trim($line)); 321 if ($quota !== 'max') { 322 $q = (int) $quota; 323 $p = (int) $period; 324 if ($q > 0 && $p > 0) { 325 $cores = max(1, (int) ceil($q / $p)); 326 $cores = (int) apply_filters('dfehc_cpu_cores', (int) $cores); 327 return (int) $cores; 328 } 329 } 330 } 331 } 332 if (is_readable('/proc/self/cgroup')) { 333 $content = file_get_contents('/proc/self/cgroup'); 334 if ($content !== false && preg_match('/^[0-9]+:[^:]*cpu[^:]*:(.+)$/m', $content, $m)) { 335 $path = '/' . ltrim(trim($m[1]), '/'); 336 $base = '/sys/fs/cgroup' . $path; 337 $quotaFile = "$base/cpu.cfs_quota_us"; 338 $periodFile = "$base/cpu.cfs_period_us"; 339 if (is_readable($quotaFile) && is_readable($periodFile)) { 340 $quota = (int) file_get_contents($quotaFile); 341 $period = (int) file_get_contents($periodFile); 342 if ($quota > 0 && $period > 0) { 343 $cores = max(1, (int) ceil($quota / $period)); 344 $cores = (int) apply_filters('dfehc_cpu_cores', (int) $cores); 345 return (int) $cores; 346 } 347 } 348 } 349 } 350 if (is_readable('/sys/fs/cgroup/cpu/cpu.cfs_quota_us') && is_readable('/sys/fs/cgroup/cpu/cpu.cfs_period_us')) { 351 $quota = (int) file_get_contents('/sys/fs/cgroup/cpu/cpu.cfs_quota_us'); 352 $period = (int) file_get_contents('/sys/fs/cgroup/cpu/cpu.cfs_period_us'); 353 if ($quota > 0 && $period > 0) { 354 $cores = max(1, (int) ceil($quota / $period)); 355 $cores = (int) apply_filters('dfehc_cpu_cores', (int) $cores); 356 return (int) $cores; 357 } 358 } 359 $disabled = array_map('trim', explode(',', (string) ini_get('disable_functions'))); 360 if (function_exists('shell_exec') && !in_array('shell_exec', $disabled, true) && !ini_get('open_basedir')) { 361 $n = shell_exec('nproc 2>/dev/null'); 362 if ($n && ctype_digit(trim((string) $n))) { 363 $cores = max(1, (int) trim((string) $n)); 364 $cores = (int) apply_filters('dfehc_cpu_cores', (int) $cores); 365 return (int) $cores; 366 } 367 } 368 if (is_readable('/proc/cpuinfo')) { 369 $info = file_get_contents('/proc/cpuinfo'); 370 if ($info !== false) { 371 $cnt = preg_match_all('/^processor/m', $info); 372 if ($cnt) { 373 $cores = (int) $cnt; 309 374 $cores = (int) apply_filters('dfehc_cpu_cores', (int) $cores); 310 375 return (int) $cores; … … 312 377 } 313 378 } 314 } 315 if (is_readable('/proc/self/cgroup')) { 316 $content = file_get_contents('/proc/self/cgroup'); 317 if ($content !== false && preg_match('/^[0-9]+:[^:]*cpu[^:]*:(.+)$/m', $content, $m)) { 318 $path = '/' . ltrim(trim($m[1]), '/'); 319 $base = '/sys/fs/cgroup' . $path; 320 $quotaFile = "$base/cpu.cfs_quota_us"; 321 $periodFile = "$base/cpu.cfs_period_us"; 322 if (is_readable($quotaFile) && is_readable($periodFile)) { 323 $quota = (int) file_get_contents($quotaFile); 324 $period = (int) file_get_contents($periodFile); 325 if ($quota > 0 && $period > 0) { 326 $cores = max(1, (int) ceil($quota / $period)); 327 $cores = (int) apply_filters('dfehc_cpu_cores', (int) $cores); 328 return (int) $cores; 329 } 330 } 331 } 332 } 333 if (is_readable('/sys/fs/cgroup/cpu/cpu.cfs_quota_us') && is_readable('/sys/fs/cgroup/cpu/cpu.cfs_period_us')) { 334 $quota = (int) file_get_contents('/sys/fs/cgroup/cpu/cpu.cfs_quota_us'); 335 $period = (int) file_get_contents('/sys/fs/cgroup/cpu/cpu.cfs_period_us'); 336 if ($quota > 0 && $period > 0) { 337 $cores = max(1, (int) ceil($quota / $period)); 338 $cores = (int) apply_filters('dfehc_cpu_cores', (int) $cores); 339 return (int) $cores; 340 } 341 } 342 $disabled = array_map('trim', explode(',', (string) ini_get('disable_functions'))); 343 if (function_exists('shell_exec') && !in_array('shell_exec', $disabled, true) && !ini_get('open_basedir')) { 344 $n = shell_exec('nproc 2>/dev/null'); 345 if ($n && ctype_digit(trim((string) $n))) { 346 $cores = max(1, (int) trim((string) $n)); 347 $cores = (int) apply_filters('dfehc_cpu_cores', (int) $cores); 348 return (int) $cores; 349 } 350 } 351 if (is_readable('/proc/cpuinfo')) { 352 $info = file_get_contents('/proc/cpuinfo'); 353 if ($info !== false) { 354 $cnt = preg_match_all('/^processor/m', $info); 355 if ($cnt) { 356 $cores = (int) $cnt; 357 $cores = (int) apply_filters('dfehc_cpu_cores', (int) $cores); 358 return (int) $cores; 359 } 360 } 361 } 362 $cores = 1; 363 $cores = (int) apply_filters('dfehc_cpu_cores', (int) $cores); 364 return (int) $cores; 379 $cores = 1; 380 $cores = (int) apply_filters('dfehc_cpu_cores', (int) $cores); 381 return (int) $cores; 382 } 365 383 } 366 384 … … 389 407 add_action('dfehc_log_server_load_hook', 'dfehc_log_server_load'); 390 408 391 function dfehc_get_server_load_ajax_handler(): void 392 { 393 $allow_public = apply_filters('dfehc_allow_public_server_load', false); 394 if (!$allow_public) { 395 $action = 'get_server_load'; 396 $nonce_action = 'dfehc-' . $action; 397 $valid = function_exists('check_ajax_referer') 398 ? check_ajax_referer($nonce_action, 'nonce', false) 399 : wp_verify_nonce((string)($_POST['nonce'] ?? $_GET['nonce'] ?? ''), $nonce_action); 400 if (!$valid) { 401 wp_send_json_error(['message' => 'Invalid nonce.'], 403); 402 } 403 $cap = apply_filters('dfehc_required_capability', 'read'); 404 if (!current_user_can($cap)) { 405 wp_send_json_error(['message' => 'Not authorised.'], 403); 406 } 407 } else { 408 $ip = dfehc_client_ip(); 409 $rk = dfehc_key('dfehc_rl_' . md5($ip)); 410 $cnt = (int) get_transient($rk); 411 $limit = (int) apply_filters('dfehc_public_rate_limit', 60); 412 $win = (int) apply_filters('dfehc_public_rate_window', 60); 413 if ($cnt >= $limit) { 414 wp_send_json_error(['message' => 'rate_limited'], 429); 415 } 416 set_transient($rk, $cnt + 1, $win); 417 } 418 nocache_headers(); 419 wp_send_json_success(dfehc_get_server_load_persistent()); 409 if (!function_exists('dfehc_get_server_load_ajax_handler')) { 410 function dfehc_get_server_load_ajax_handler(): void 411 { 412 $allow_public = apply_filters('dfehc_allow_public_server_load', false); 413 if (!$allow_public) { 414 $action = 'get_server_load'; 415 $nonce_action = 'dfehc-' . $action; 416 $valid = function_exists('check_ajax_referer') 417 ? check_ajax_referer($nonce_action, 'nonce', false) 418 : wp_verify_nonce((string)($_POST['nonce'] ?? $_GET['nonce'] ?? ''), $nonce_action); 419 if (!$valid) { 420 wp_send_json_error(['message' => 'Invalid nonce.'], 403); 421 } 422 $cap = apply_filters('dfehc_required_capability', 'read'); 423 if (!current_user_can($cap)) { 424 wp_send_json_error(['message' => 'Not authorised.'], 403); 425 } 426 } else { 427 $ip = dfehc_client_ip(); 428 $rk = dfehc_key('dfehc_rl_' . md5($ip)); 429 $cnt = (int) get_transient($rk); 430 $limit = (int) apply_filters('dfehc_public_rate_limit', 60); 431 $win = (int) apply_filters('dfehc_public_rate_window', 60); 432 if ($cnt >= $limit) { 433 wp_send_json_error(['message' => 'rate_limited'], 429); 434 } 435 set_transient($rk, $cnt + 1, $win); 436 } 437 nocache_headers(); 438 wp_send_json_success(dfehc_get_server_load_persistent()); 439 } 420 440 } 421 441 add_action('wp_ajax_get_server_load', 'dfehc_get_server_load_ajax_handler'); … … 454 474 if (wp_using_ext_object_cache()) { 455 475 $ttl = dfehc_server_load_ttl(); 456 $ttl += function_exists('random_int') ? random_int(0, 5) : 0; 476 $jitter = 0; 477 if (function_exists('random_int')) { 478 try { 479 $jitter = random_int(0, 5); 480 } catch (Throwable $e) { 481 $jitter = 0; 482 } 483 } 484 $ttl += $jitter; 457 485 wp_cache_set($key, $fresh, DFEHC_CACHE_GROUP, $ttl); 458 486 } -
dynamic-front-end-heartbeat-control/trunk/engine/server-response.php
r3396626 r3412283 126 126 ]; 127 127 $ttl = (int) apply_filters('dfehc_high_traffic_cache_expiration', 300); 128 $ttl += function_exists('random_int') ? random_int(0, 5) : 0; 128 $jitter = 0; 129 if (function_exists('random_int')) { 130 try { 131 $jitter = random_int(0, 5); 132 } catch (\Throwable $e) { 133 $jitter = 0; 134 } 135 } 136 $ttl += $jitter; 129 137 dfehc_set_transient_noautoload($cacheKey, $high, $ttl); 130 138 return $high; 131 139 } 132 140 133 if (!dfehc_ acquire_lock()) {141 if (!dfehc_rt_acquire_lock()) { 134 142 return array_merge($defaults, is_array($cached) ? $cached : []); 135 143 } … … 155 163 $results['timestamp'] = current_time('mysql'); 156 164 $results['ts_unix'] = $now; 157 $exp += function_exists('random_int') ? random_int(0, 5) : 0; 165 $jitter = 0; 166 if (function_exists('random_int')) { 167 try { 168 $jitter = random_int(0, 5); 169 } catch (\Throwable $e) { 170 $jitter = 0; 171 } 172 } 173 $exp += $jitter; 158 174 dfehc_set_transient_noautoload($baselineKey, $results, $exp); 159 175 $baseline = $results; … … 184 200 $results['ts_unix'] = $now; 185 201 $exp = (int) apply_filters('dfehc_baseline_expiration', DFEHC_BASELINE_EXP); 186 $exp += function_exists('random_int') ? random_int(0, 5) : 0; 202 $jitter = 0; 203 if (function_exists('random_int')) { 204 try { 205 $jitter = random_int(0, 5); 206 } catch (\Throwable $e) { 207 $jitter = 0; 208 } 209 } 210 $exp += $jitter; 187 211 dfehc_set_transient_noautoload($baselineKey, $results, $exp); 188 212 $spike = 0.0; … … 195 219 196 220 if (abs($spike - $prev_spike) >= DFEHC_SPIKE_OPT_EPS) { 197 $ttl = DFEHC_BASELINE_EXP + (function_exists('random_int') ? random_int(0, 5) : 0); 221 $ttl = DFEHC_BASELINE_EXP; 222 $jitter = 0; 223 if (function_exists('random_int')) { 224 try { 225 $jitter = random_int(0, 5); 226 } catch (\Throwable $e) { 227 $jitter = 0; 228 } 229 } 230 $ttl += $jitter; 198 231 dfehc_set_transient_noautoload($spikeKey, $spike, $ttl); 199 232 } 200 233 201 234 $exp = (int) apply_filters('dfehc_cache_expiration', 3 * MINUTE_IN_SECONDS); 202 $exp += function_exists('random_int') ? random_int(0, 5) : 0; 235 $jitter = 0; 236 if (function_exists('random_int')) { 237 try { 238 $jitter = random_int(0, 5); 239 } catch (\Throwable $e) { 240 $jitter = 0; 241 } 242 } 243 $exp += $jitter; 203 244 dfehc_set_transient_noautoload($cacheKey, $results, $exp); 204 245 205 246 return array_merge($defaults, $results); 206 247 } finally { 207 dfehc_r elease_lock();248 dfehc_rt_release_lock(); 208 249 } 209 250 } … … 324 365 if ($head_supported === null) { 325 366 $ttl = (int) apply_filters('dfehc_head_negative_ttl', DFEHC_HEAD_NEG_TTL); 326 $ttl += function_exists('random_int') ? random_int(0, 5) : 0; 367 $jitter = 0; 368 if (function_exists('random_int')) { 369 try { 370 $jitter = random_int(0, 5); 371 } catch (\Throwable $e) { 372 $jitter = 0; 373 } 374 } 375 $ttl += $jitter; 327 376 dfehc_set_transient_noautoload($head_key, 0, $ttl); 328 377 } … … 331 380 if ($head_supported === null) { 332 381 $ttl = (int) apply_filters('dfehc_head_positive_ttl', DFEHC_HEAD_POS_TTL); 333 $ttl += function_exists('random_int') ? random_int(0, 5) : 0; 382 $jitter = 0; 383 if (function_exists('random_int')) { 384 try { 385 $jitter = random_int(0, 5); 386 } catch (\Throwable $e) { 387 $jitter = 0; 388 } 389 } 390 $ttl += $jitter; 334 391 dfehc_set_transient_noautoload($head_key, 1, $ttl); 335 392 } … … 366 423 } else { 367 424 $ttl = (int) apply_filters('dfehc_probe_fail_ttl', 60); 368 $ttl += function_exists('random_int') ? random_int(0, 5) : 0; 425 $jitter = 0; 426 if (function_exists('random_int')) { 427 try { 428 $jitter = random_int(0, 5); 429 } catch (\Throwable $e) { 430 $jitter = 0; 431 } 432 } 433 $ttl += $jitter; 369 434 dfehc_set_transient_noautoload($negKey, 1, $ttl); 370 435 $r['method'] = 'failed'; … … 402 467 } 403 468 404 if (!function_exists('dfehc_ acquire_lock')) {405 function dfehc_ acquire_lock(): bool469 if (!function_exists('dfehc_rt_acquire_lock')) { 470 function dfehc_rt_acquire_lock(): bool 406 471 { 407 472 $key = dfehc_key('dfehc_measure_lock'); … … 434 499 } 435 500 436 if (!function_exists('dfehc_r elease_lock')) {437 function dfehc_r elease_lock(): void501 if (!function_exists('dfehc_rt_release_lock')) { 502 function dfehc_rt_release_lock(): void 438 503 { 439 504 if (isset($GLOBALS['dfehc_rt_lock']) && $GLOBALS['dfehc_rt_lock'] instanceof WP_Lock) { -
dynamic-front-end-heartbeat-control/trunk/engine/system-load-fallback.php
r3396626 r3412283 31 31 if (!function_exists('dfehc_set_transient_noautoload')) { 32 32 function dfehc_set_transient_noautoload(string $key, $value, int $expiration): void { 33 $jitter = function_exists('random_int') ? random_int(0, 5) : 0; 33 $jitter = 0; 34 if (function_exists('random_int')) { 35 try { 36 $jitter = random_int(0, 5); 37 } catch (\Throwable $e) { 38 $jitter = 0; 39 } 40 } 34 41 $expiration = max(1, $expiration + $jitter); 35 42 if (function_exists('wp_using_ext_object_cache') && wp_using_ext_object_cache()) { -
dynamic-front-end-heartbeat-control/trunk/heartbeat-async.php
r3396626 r3412283 2 2 declare(strict_types=1); 3 3 4 define('DFEHC_LOAD_AVERAGES', 'dfehc_load_averages');5 define('DFEHC_SERVER_LOAD', 'dfehc_server_load');6 define('DFEHC_RECOMMENDED_INTERVAL', 'dfehc_recommended_interval');7 define('DFEHC_CAPABILITY', 'read');8 define('DFEHC_LOAD_LOCK_BASE', 'dfehc_compute_load_lock');9 define('DFEHC_CACHE_GROUP', 'dfehc');4 if (!defined('DFEHC_LOAD_AVERAGES')) define('DFEHC_LOAD_AVERAGES', 'dfehc_load_averages'); 5 if (!defined('DFEHC_SERVER_LOAD')) define('DFEHC_SERVER_LOAD', 'dfehc_server_load'); 6 if (!defined('DFEHC_RECOMMENDED_INTERVAL')) define('DFEHC_RECOMMENDED_INTERVAL', 'dfehc_recommended_interval'); 7 if (!defined('DFEHC_CAPABILITY')) define('DFEHC_CAPABILITY', 'read'); 8 if (!defined('DFEHC_LOAD_LOCK_BASE')) define('DFEHC_LOAD_LOCK_BASE', 'dfehc_compute_load_lock'); 9 if (!defined('DFEHC_CACHE_GROUP')) define('DFEHC_CACHE_GROUP', 'dfehc'); 10 10 11 11 function dfehc_max_server_load(): int { static $v; if ($v === null) { $v = (int) apply_filters('dfehc_max_server_load', 85); } return $v; } … … 15 15 function dfehc_server_load_ttl(): int { static $v; if ($v === null) { $v = (int) apply_filters('dfehc_server_load_ttl', 180); } return $v; } 16 16 17 function dfehc_host_token(): string 18 { 19 $host = @php_uname('n') ?: (defined('WP_HOME') ? WP_HOME : (function_exists('home_url') ? home_url() : 'unknown')); 20 $salt = defined('DB_NAME') ? (string) DB_NAME : ''; 21 return substr(md5((string) $host . $salt), 0, 10); 22 } 23 24 function dfehc_scoped_key(string $base): string 25 { 26 $blog = function_exists('get_current_blog_id') ? (string) get_current_blog_id() : '0'; 27 return "{$base}_{$blog}_" . dfehc_host_token(); 28 } 29 30 function dfehc_store_lockfree(string $key, $value, int $ttl): bool 31 { 32 if (function_exists('wp_cache_add') && wp_cache_add($key, $value, DFEHC_CACHE_GROUP, $ttl)) { 33 return true; 34 } 35 return set_transient($key, $value, $ttl); 17 if (!function_exists('dfehc_host_token')) { 18 function dfehc_host_token(): string 19 { 20 $host = @php_uname('n') ?: (defined('WP_HOME') ? WP_HOME : (function_exists('home_url') ? home_url() : 'unknown')); 21 $salt = defined('DB_NAME') ? (string) DB_NAME : ''; 22 return substr(md5((string) $host . $salt), 0, 10); 23 } 24 } 25 26 if (!function_exists('dfehc_scoped_key')) { 27 function dfehc_scoped_key(string $base): string 28 { 29 $blog = function_exists('get_current_blog_id') ? (string) get_current_blog_id() : '0'; 30 return "{$base}_{$blog}_" . dfehc_host_token(); 31 } 32 } 33 34 if (!function_exists('dfehc_store_lockfree')) { 35 function dfehc_store_lockfree(string $key, $value, int $ttl): bool 36 { 37 if (function_exists('wp_cache_add') && wp_cache_add($key, $value, DFEHC_CACHE_GROUP, $ttl)) { 38 return true; 39 } 40 return set_transient($key, $value, $ttl); 41 } 36 42 } 37 43 … … 72 78 } 73 79 74 function dfehc_set_transient_noautoload(string $key, $value, int $expiration): void 75 { 76 if (wp_using_ext_object_cache()) { 77 if (function_exists('wp_cache_add')) { 78 if (!wp_cache_add($key, $value, DFEHC_CACHE_GROUP, $expiration)) { 80 if (!function_exists('dfehc_set_transient_noautoload')) { 81 function dfehc_set_transient_noautoload(string $key, $value, int $expiration): void 82 { 83 if (wp_using_ext_object_cache()) { 84 if (function_exists('wp_cache_add')) { 85 if (!wp_cache_add($key, $value, DFEHC_CACHE_GROUP, $expiration)) { 86 wp_cache_set($key, $value, DFEHC_CACHE_GROUP, $expiration); 87 } 88 } else { 79 89 wp_cache_set($key, $value, DFEHC_CACHE_GROUP, $expiration); 80 90 } 81 } else { 82 wp_cache_set($key, $value, DFEHC_CACHE_GROUP, $expiration); 83 } 84 return; 85 } 86 dfehc_store_lockfree($key, $value, $expiration); 87 global $wpdb; 88 $opt_key = "_transient_$key"; 89 $opt_key_to = "_transient_timeout_$key"; 90 $wpdb->suppress_errors(true); 91 $autoload = $wpdb->get_var($wpdb->prepare("SELECT autoload FROM {$wpdb->options} WHERE option_name=%s LIMIT 1", $opt_key)); 92 if ($autoload === 'yes') { 93 $wpdb->update($wpdb->options, ['autoload' => 'no'], ['option_name' => $opt_key, 'autoload' => 'yes'], ['%s'], ['%s','%s']); 94 } 95 $autoload_to = $wpdb->get_var($wpdb->prepare("SELECT autoload FROM {$wpdb->options} WHERE option_name=%s LIMIT 1", $opt_key_to)); 96 if ($autoload_to === 'yes') { 97 $wpdb->update($wpdb->options, ['autoload' => 'no'], ['option_name' => $opt_key_to, 'autoload' => 'yes'], ['%s'], ['%s','%s']); 98 } 99 $wpdb->suppress_errors(false); 91 return; 92 } 93 dfehc_store_lockfree($key, $value, $expiration); 94 global $wpdb; 95 $opt_key = "_transient_$key"; 96 $opt_key_to = "_transient_timeout_$key"; 97 $wpdb->suppress_errors(true); 98 $autoload = $wpdb->get_var($wpdb->prepare("SELECT autoload FROM {$wpdb->options} WHERE option_name=%s LIMIT 1", $opt_key)); 99 if ($autoload === 'yes') { 100 $wpdb->update($wpdb->options, ['autoload' => 'no'], ['option_name' => $opt_key, 'autoload' => 'yes'], ['%s'], ['%s','%s']); 101 } 102 $autoload_to = $wpdb->get_var($wpdb->prepare("SELECT autoload FROM {$wpdb->options} WHERE option_name=%s LIMIT 1", $opt_key_to)); 103 if ($autoload_to === 'yes') { 104 $wpdb->update($wpdb->options, ['autoload' => 'no'], ['option_name' => $opt_key_to, 'autoload' => 'yes'], ['%s'], ['%s','%s']); 105 } 106 $wpdb->suppress_errors(false); 107 } 100 108 } 101 109 … … 442 450 } 443 451 444 function dfehc_calculate_recommended_interval_user_activity(float $current_load): int 445 { 446 $key = dfehc_scoped_key(DFEHC_RECOMMENDED_INTERVAL); 447 $interval = get_transient($key); 448 if ($interval !== false) { 449 return (int) $interval; 450 } 451 return $current_load >= dfehc_max_server_load() ? dfehc_max_interval() : dfehc_min_interval(); 452 if (!function_exists('dfehc_calculate_recommended_interval_user_activity')) { 453 function dfehc_calculate_recommended_interval_user_activity(float $current_load): float 454 { 455 $key = dfehc_scoped_key(DFEHC_RECOMMENDED_INTERVAL); 456 $interval = get_transient($key); 457 if ($interval !== false) { 458 return (float) $interval; 459 } 460 return $current_load >= dfehc_max_server_load() 461 ? (float) dfehc_max_interval() 462 : (float) dfehc_min_interval(); 463 } 452 464 } 453 465 -
dynamic-front-end-heartbeat-control/trunk/heartbeat-controller.php
r3396626 r3412283 4 4 Plugin URI: https://heartbeat.support 5 5 Description: An enhanced solution to optimize the performance of your WordPress website. Stabilize your website's load averages and enhance the browsing experience for visitors during high-traffic fluctuations. 6 Version: 1.2.99 56 Version: 1.2.996 7 7 Author: Codeloghin 8 8 Author URI: https://codeloghin.com … … 11 11 12 12 declare(strict_types=1); 13 14 if (!defined('ABSPATH')) { 15 exit; 16 } 13 17 14 18 if (!defined('DFEHC_PLUGIN_PATH')) { … … 96 100 } 97 101 102 add_action('wp_default_scripts', function (WP_Scripts $scripts) { 103 if (is_admin() || wp_doing_ajax()) { 104 return; 105 } 106 107 if (isset($scripts->registered['heartbeat'])) { 108 $scripts->registered['heartbeat']->src = plugin_dir_url(__FILE__) . 'js/heartbeat.min.js'; 109 $scripts->registered['heartbeat']->ver = '1.6.1'; 110 $scripts->registered['heartbeat']->deps = array('jquery'); 111 } 112 }); 113 98 114 function dfehc_enqueue_scripts(): void 99 115 { 100 wp_register_script('dfehc-heartbeat', plugin_dir_url(__FILE__) . 'js/heartbeat.min.js', ['heartbeat'], '1.6.0', true); 101 wp_enqueue_script('dfehc-heartbeat'); 116 if (is_admin() || wp_doing_ajax()) { 117 return; 118 } 119 120 wp_enqueue_script('heartbeat'); 102 121 103 122 $load = function_exists('dfehc_get_server_load') ? dfehc_get_server_load() : null; … … 113 132 $ver = defined('DFEHC_VERSION') ? (string) DFEHC_VERSION : (string) filemtime(__FILE__); 114 133 115 wp_localize_script('dfehc-heartbeat', 'dfehc_heartbeat_vars', [ 116 'recommendedInterval' => (float) $recommended, 117 'heartbeat_control_enabled' => get_option('dfehc_heartbeat_control_enabled', '1'), 118 'nonce' => wp_create_nonce(DFEHC_NONCE_ACTION), 119 'ver' => $ver, 120 'cache_bypass_rate' => 0.05, 121 ]); 134 $min_interval_option = defined('DFEHC_OPTION_MIN_INTERVAL') ? (int) get_option(DFEHC_OPTION_MIN_INTERVAL, defined('DFEHC_DEFAULT_MIN_INTERVAL') ? DFEHC_DEFAULT_MIN_INTERVAL : DFEHC_MIN_INTERVAL) : (int) DFEHC_MIN_INTERVAL; 135 $max_interval_option = defined('DFEHC_OPTION_MAX_INTERVAL') ? (int) get_option(DFEHC_OPTION_MAX_INTERVAL, defined('DFEHC_DEFAULT_MAX_INTERVAL') ? DFEHC_DEFAULT_MAX_INTERVAL : DFEHC_MAX_INTERVAL) : (int) DFEHC_MAX_INTERVAL; 136 $max_server_load_option = defined('DFEHC_OPTION_MAX_SERVER_LOAD') ? (float) get_option(DFEHC_OPTION_MAX_SERVER_LOAD, defined('DFEHC_DEFAULT_MAX_SERVER_LOAD') ? DFEHC_DEFAULT_MAX_SERVER_LOAD : DFEHC_MAX_SERVER_LOAD) : (float) DFEHC_MAX_SERVER_LOAD; 137 138 wp_localize_script( 139 'heartbeat', 140 'dfehc_heartbeat_vars', 141 array( 142 'recommendedInterval' => (float) $recommended, 143 'heartbeat_control_enabled' => get_option('dfehc_heartbeat_control_enabled', '1'), 144 'nonce' => wp_create_nonce(DFEHC_NONCE_ACTION), 145 'ver' => $ver, 146 'cache_bypass_rate' => 0.05, 147 ) 148 ); 122 149 } 123 150 add_action('wp_enqueue_scripts', 'dfehc_enqueue_scripts'); … … 156 183 $total_weight = 0; 157 184 $offset = 0; 185 $start_time = microtime(true); 186 $time_limit = 1.5; 158 187 while (true) { 188 if (microtime(true) - $start_time > $time_limit) { 189 break; 190 } 159 191 $userBatch = dfehc_get_users_in_batches($batch_size, $offset); 160 192 if (!$userBatch) { … … 179 211 } 180 212 181 function dfehc_get_recommended_heartbeat_interval_async() 213 function dfehc_get_recommended_heartbeat_interval_async(): float 182 214 { 183 215 if (!class_exists('Heartbeat_Async') && file_exists(__DIR__ . '/heartbeat-async.php')) { -
dynamic-front-end-heartbeat-control/trunk/js/heartbeat.js
r3396626 r3412283 250 250 251 251 try { 252 const body = new URLSearchParams({ action: ' get_server_load' });252 const body = new URLSearchParams({ action: 'dfehc_update_heartbeat_interval' }); 253 253 if (nonce) body.append('nonce', nonce); 254 254 const res = await fetchWithTimeout(url, { -
dynamic-front-end-heartbeat-control/trunk/js/heartbeat.min.js
r3396626 r3412283 1 ((wp)=>{const intervals={low:[15,30,60,120,180,240,300],medium:[30,60,120,180,240,300],high:[60,120,180,240,300]};const vars=typeof window.dfehc_heartbeat_vars==="object"?window.dfehc_heartbeat_vars:{};const clampNumber=(v,lo,hi,d)=>{const n=Number(v);if(!Number.isFinite(n))return d;return Math.min(hi,Math.max(lo,n))};const DEFAULT_MIN=15;const DEFAULT_MAX=300;const ABS_MIN=1;const ABS_MAX=3600;let MIN=clampNumber(vars.min_interval,ABS_MIN,ABS_MAX,DEFAULT_MIN);let MAX=clampNumber(vars.max_interval,MIN,ABS_MAX,DEFAULT_MAX);if(MIN>MAX){const t=MIN;MIN=MAX;MAX=t}const LOAD_CAP=Number.isFinite(vars.max_server_load)?Number(vars.max_server_load):85;const msOrAuto=(v,d)=>{const n=Number(v);if(!Number.isFinite(n))return d;return n<=600?n*1e3:n};const cacheTimeout=msOrAuto(vars.cache_duration, 3e5);const cacheBypassRate=Math.min(1,Math.max(0,Number(vars.cache_bypass_rate)||.05));const sanitizeKey=(s)=>String(s||"").replace(/[^a-z0-9_.-]/gi,"_");const siteKey=sanitizeKey(vars.site_key||location.host);const localCacheKey=`dfehc_heartbeat_server_load:${siteKey}:${vars.ver||"1"}`;const memoryCache=Object.create(null);const SUPPORTS_BC=typeof window.BroadcastChannel==="function";const bc=SUPPORTS_BC?new BroadcastChannel("dfehc-heartbeat"):null;const TAB_ID=Math.random().toString(36).slice(2)+Date.now().toString(36);let isLeader=false;let lastLeaderBeat=0;const LEADER_TTL=5e3;const leaderKey=`${localCacheKey}:leader`;function readLeader(){try{const raw=localStorage.getItem(leaderKey);if(!raw)return null;const obj=JSON.parse(raw);if(!obj||typeof obj!=="object")return null;const ts=Number(obj.ts);const tab=String(obj.tab||"");if(!Number.isFinite(ts)||!tab)return null;return{ts,tab}}catch{return null}}function writeLeader(ts,tab){try{localStorage.setItem(leaderKey,JSON.stringify({ts,tab}))}catch{}}function clearLeader(tab){try{const cur=readLeader();if(!cur||cur.tab===tab)localStorage.removeItem(leaderKey)}catch{}}function tryBecomeLeader(){if(document.hidden)return false;const now=Date.now();const cur=readLeader();if(!cur||now-cur.ts>LEADER_TTL){writeLeader(now,TAB_ID);isLeader=true;lastLeaderBeat=now;if(bc)bc.postMessage({t:"leader",ts:now,tab:TAB_ID});return true}if(cur.tab===TAB_ID){isLeader=true;lastLeaderBeat=cur.ts;return true}return false}function renewLeadership(){if(!isLeader)return;const now=Date.now();if(now-lastLeaderBeat>2e3){writeLeader(now,TAB_ID);lastLeaderBeat=now}}function relinquishLeadership(){if(!isLeader)return;clearLeader(TAB_ID);isLeader=false}const getLocalCache=(key)=>{try{const raw=window.localStorage.getItem(key);if(!raw)return null;const parsed=JSON.parse(raw);if(!parsed||typeof parsed!=="object")return null;const timestamp=Number(parsed.timestamp);const data=parsed.data;if(!Number.isFinite(timestamp))return null;return Date.now()-timestamp<cacheTimeout?data:null}catch{try{window.localStorage.removeItem(key)}catch{}return null}};const setLocalCache=(key,data)=>{try{const jitterMs=Math.floor(Math.random()*5e3);window.localStorage.setItem(key,JSON.stringify({timestamp:Date.now()-jitterMs,data}))}catch{}};const nearestFrom=(value,list)=>list.reduce((best,v)=>Math.abs(v-value)<Math.abs(best-value)?v:best,list[0]);const trafficLevel=(load)=>load<=50?"low":load<=75?"medium":"high";const calcRecommendedIntervalFromLoad=(load)=>{const bucket=Math.max(0,Math.min(100,Math.round(load)));if(Object.prototype.hasOwnProperty.call(memoryCache,bucket))return memoryCache[bucket];const level=trafficLevel(load);const opts=intervals[level];const min=Math.max(MIN,opts[0]);const max=Math.min(MAX,opts[opts.length-1]);const ratio=Math.max(0,Math.min(1,load/LOAD_CAP));const raw=min+(max-min)*ratio;const clamped=Math.min(Math.max(raw,MIN),MAX);const snapped=nearestFrom(clamped,opts);memoryCache[bucket]=snapped;return snapped};const toNum=(v)=>{const n=Number(v);return Number.isFinite(n)?n:null};const coerceServerPayload=(val)=>{if(val&&typeof val==="object"){const interval=toNum(val.interval);const load=toNum(val.load);return{interval,load}}if(Number.isFinite(Number(val))){const n=Number(val);const mode=(vars.server_payload||"auto").toLowerCase();if(mode==="interval")return{interval:n,load:null};if(mode==="load")return{interval:null,load:n};if(n>=0&&n<=100)return{interval:null,load:n};if(n>100)return{interval:n,load:null}}return{interval:null,load:null}};const debounce=(fn,wait)=>{let t;return(...args)=>{clearTimeout(t);t=setTimeout(()=>fn(...args),wait)}};let lastInterval=null;const applyPresetThenExact=(secs)=>{const hb=wp&&wp.heartbeat;if(!hb||typeof hb.interval!=="function")return;if(typeof secs!=="number"||!Number.isFinite(secs))return;if(lastInterval!==null&&Math.round(lastInterval)===Math.round(secs))return;const preset=secs<=20?"fast":secs<=60?"standard":"slow";try{hb.interval(preset);hb.interval(secs)}catch{}lastInterval=secs};const setHeartbeatInterval=(secs)=>{let s=secs;if(s>=60){const jitter=Math.floor(Math.random()*3)-1;s=Math.max(MIN,Math.min(MAX,s+jitter))}applyPresetThenExact(s)};const debouncedSetInterval=debounce(setHeartbeatInterval,150);let failureCount=0;let nextRetryAt=0;const backoffMs=()=>Math.min(12e4,2e3*Math.pow(2,Math.min(failureCount,5)));const fetchWithTimeout=(url,opts,ms)=>{if("AbortController"in window){const ctrl=new AbortController;const t=setTimeout(()=>ctrl.abort(),ms);return fetch(url,{...opts,signal:ctrl.signal}).finally(()=>clearTimeout(t))}return Promise.race([fetch(url,opts),new Promise((_,rej)=>setTimeout(()=>rej(new Error("timeout")),ms))])};let reqSeq=0;const fetchServerData=async(nonce,rid)=>{if(Date.now()<nextRetryAt){const base=Number(vars.fallback_interval)||60;const jitter=Math.floor(Math.random()*7)-3;return{interval:Math.max(MIN,Math.min(MAX,base+jitter)),load:null,rid}}if(Math.random()>=cacheBypassRate){const cached=getLocalCache(localCacheKey);if(cached!==null)return{...coerceServerPayload(cached),rid}}const I_AM_LEADER=tryBecomeLeader();if(!I_AM_LEADER){const wait=Math.min(1500,Math.max(150,Math.floor(Math.random()*600)));await new Promise(r=>setTimeout(r,wait));const cached=getLocalCache(localCacheKey);if(cached!==null)return{...coerceServerPayload(cached),rid}}if(navigator.onLine===false){const base=Number(vars.fallback_interval)||60;const jitter=Math.floor(Math.random()*7)-3;return{interval:Math.max(MIN,Math.min(MAX,base+jitter)),load:null,rid}}const url=vars.ajax_url||typeof window.ajaxurl!=="undefined"?window.ajaxurl:`${location.origin.replace(/\/$/,"")}/wp-admin/admin-ajax.php`;try{const body=new URLSearchParams({action:"get_server_load"});if(nonce)body.append("nonce",nonce);const res=await fetchWithTimeout(url,{method:"POST",body,credentials:"same-origin",headers:{"Content-Type":"application/x-www-form-urlencoded; charset=UTF-8","Cache-Control":"no-store"}},6e3);if(!res.ok)throw new Error(String(res.status));const json=await res.json();if(!json||json.success!==true)throw new Error("bad payload");setLocalCache(localCacheKey,json.data);if(bc)bc.postMessage({t:"payload",data:json.data});failureCount=0;nextRetryAt=0;renewLeadership();return{...coerceServerPayload(json.data),rid}}catch{failureCount+=1;nextRetryAt=Date.now()+backoffMs();const base=Number(vars.fallback_interval)||60;const jitter=Math.floor(Math.random()*7)-3;relinquishLeadership();return{interval:Math.max(MIN,Math.min(MAX,base+jitter)),load:null,rid}}};const heartbeat={update(interval){debouncedSetInterval(interval)},updateUI(interval){const sel=document.querySelector("#dfehc-heartbeat-interval");if(sel)sel.value=String(interval);debouncedSetInterval(interval)},async init(nonce){const conn=navigator.connection||navigator.mozConnection||navigator.webkitConnection;const et=conn&&conn.effectiveType?String(conn.effectiveType):"";if("deviceMemory"in navigator&&navigator.deviceMemory<2)return;if(conn&&conn.saveData||et.startsWith("2g")||et.startsWith("slow-2g"))return;const myRid=++reqSeq;const payload=await fetchServerData(nonce,myRid);if(payload.rid!==reqSeq)return;const interval=payload.interval;const load=payload.load;const snapList=intervals[trafficLevel(typeof load==="number"?load:LOAD_CAP)]||intervals.low;const finalInterval=typeof interval==="number"?nearestFrom(Math.min(Math.max(interval,MIN),MAX),snapList):calcRecommendedIntervalFromLoad(typeof load==="number"?load:60);this.updateUI(finalInterval)}};if(bc){bc.onmessage=(e)=>{const m=e.data||{};if(m.t==="leader"){const tab=String(m.tab||"");if(tab&&tab!==TAB_ID)isLeader=false}if(m.t==="payload"){try{setLocalCache(localCacheKey,m.data)}catch{}Object.keys(memoryCache).forEach(k=>delete memoryCache[k]);const coerced=coerceServerPayload(m.data);const interval=coerced.interval;const load=coerced.load;const snapList=intervals[trafficLevel(typeof load==="number"?load:LOAD_CAP)]||intervals.low;const finalInterval=typeof interval==="number"?nearestFrom(Math.min(Math.max(interval,MIN),MAX),snapList):calcRecommendedIntervalFromLoad(typeof load==="number"?load:60);debouncedSetInterval(finalInterval)}}}window.addEventListener("storage",(e)=>{if(e.key!==localCacheKey||!e.newValue)return;try{const v=JSON.parse(e.newValue);if(!v||typeof v!=="object"||!("data"in v))return;Object.keys(memoryCache).forEach(k=>delete memoryCache[k]);const coerced=coerceServerPayload(v.data);const interval=coerced.interval;const load=coerced.load;const snapList=intervals[trafficLevel(typeof load==="number"?load:LOAD_CAP)]||intervals.low;const finalInterval=typeof interval==="number"?nearestFrom(Math.min(Math.max(interval,MIN),MAX),snapList):calcRecommendedIntervalFromLoad(typeof load==="number"?load:60);debouncedSetInterval(finalInterval)}catch{}});let memoVersion=String(vars.ver||"1");function maybeResetMemo(){const v=String(vars.ver||"1");if(v!==memoVersion){memoVersion=v;Object.keys(memoryCache).forEach(k=>delete memoryCache[k])}}function cleanup(){relinquishLeadership();if(bc&&typeof bc.close==="function"){try{bc.close()}catch{}}}document.addEventListener("visibilitychange",()=>{if(!document.hidden)nextRetryAt=0});window.addEventListener("online",()=>{nextRetryAt=0});window.addEventListener("pageshow",(e)=>{if(e.persisted)nextRetryAt=0});window.addEventListener("pagehide",cleanup,{once:true});window.addEventListener("beforeunload",cleanup,{once:true});document.addEventListener("DOMContentLoaded",()=>{maybeResetMemo();if((vars.heartbeat_control_enabled||"")!=="1")return;const nonce=vars.nonce;if("requestIdleCallback"in window){window.requestIdleCallback(()=>heartbeat.init(nonce))}else{setTimeout(()=>heartbeat.init(nonce),100)}const sel=document.querySelector("#dfehc-heartbeat-interval");if(sel){sel.addEventListener("change",function(){const val=parseInt(this.value,10);if(!Number.isNaN(val))heartbeat.update(Math.min(Math.max(val,MIN),MAX))})}})})(window.wp||{});1 ((wp)=>{const intervals={low:[15,30,60,120,180,240,300],medium:[30,60,120,180,240,300],high:[60,120,180,240,300]};const vars=typeof window.dfehc_heartbeat_vars==="object"?window.dfehc_heartbeat_vars:{};const clampNumber=(v,lo,hi,d)=>{const n=Number(v);if(!Number.isFinite(n))return d;return Math.min(hi,Math.max(lo,n))};const DEFAULT_MIN=15;const DEFAULT_MAX=300;const ABS_MIN=1;const ABS_MAX=3600;let MIN=clampNumber(vars.min_interval,ABS_MIN,ABS_MAX,DEFAULT_MIN);let MAX=clampNumber(vars.max_interval,MIN,ABS_MAX,DEFAULT_MAX);if(MIN>MAX){const t=MIN;MIN=MAX;MAX=t}const LOAD_CAP=Number.isFinite(vars.max_server_load)?Number(vars.max_server_load):85;const msOrAuto=(v,d)=>{const n=Number(v);if(!Number.isFinite(n))return d;return n<=600?n*1e3:n};const cacheTimeout=msOrAuto(vars.cache_duration,5*60*1e3);const cacheBypassRate=Math.min(1,Math.max(0,Number(vars.cache_bypass_rate)||.05));const sanitizeKey=s=>String(s||"").replace(/[^a-z0-9_.-]/gi,"_");const siteKey=sanitizeKey(vars.site_key||location.host);const localCacheKey=`dfehc_heartbeat_server_load:${siteKey}:${vars.ver||"1"}`;const memoryCache=Object.create(null);const SUPPORTS_BC=typeof window.BroadcastChannel==="function";const bc=SUPPORTS_BC?new BroadcastChannel("dfehc-heartbeat"):null;const TAB_ID=Math.random().toString(36).slice(2)+Date.now().toString(36);let isLeader=false;let lastLeaderBeat=0;const LEADER_TTL=5e3;const leaderKey=`${localCacheKey}:leader`;function readLeader(){try{const raw=localStorage.getItem(leaderKey);if(!raw)return null;const obj=JSON.parse(raw);if(!obj||typeof obj!=="object")return null;const ts=Number(obj.ts);const tab=String(obj.tab||"");if(!Number.isFinite(ts)||!tab)return null;return{ts,tab}}catch{return null}}function writeLeader(ts,tab){try{localStorage.setItem(leaderKey,JSON.stringify({ts,tab}))}catch{}}function clearLeader(tab){try{const cur=readLeader();if(!cur||cur.tab===tab)localStorage.removeItem(leaderKey)}catch{}}function tryBecomeLeader(){if(document.hidden)return false;const now=Date.now();const cur=readLeader();if(!cur||now-cur.ts>LEADER_TTL){writeLeader(now,TAB_ID);isLeader=true;lastLeaderBeat=now;if(bc)bc.postMessage({t:"leader",ts:now,tab:TAB_ID});return true}if(cur.tab===TAB_ID){isLeader=true;lastLeaderBeat=cur.ts;return true}return false}function renewLeadership(){if(!isLeader)return;const now=Date.now();if(now-lastLeaderBeat>2e3){writeLeader(now,TAB_ID);lastLeaderBeat=now}}function relinquishLeadership(){if(!isLeader)return;clearLeader(TAB_ID);isLeader=false}const getLocalCache=key=>{try{const raw=window.localStorage.getItem(key);if(!raw)return null;const parsed=JSON.parse(raw);if(!parsed||typeof parsed!=="object")return null;const timestamp=Number(parsed.timestamp);const data=parsed.data;if(!Number.isFinite(timestamp))return null;return Date.now()-timestamp<cacheTimeout?data:null}catch{try{window.localStorage.removeItem(key)}catch{}return null}};const setLocalCache=(key,data)=>{try{const jitterMs=Math.floor(Math.random()*5e3);window.localStorage.setItem(key,JSON.stringify({timestamp:Date.now()-jitterMs,data}))}catch{}};const nearestFrom=(value,list)=>list.reduce((best,v)=>Math.abs(v-value)<Math.abs(best-value)?v:best,list[0]);const trafficLevel=load=>load<=50?"low":load<=75?"medium":"high";const calcRecommendedIntervalFromLoad=load=>{const bucket=Math.max(0,Math.min(100,Math.round(load)));if(Object.prototype.hasOwnProperty.call(memoryCache,bucket))return memoryCache[bucket];const level=trafficLevel(load);const opts=intervals[level];const min=Math.max(MIN,opts[0]);const max=Math.min(MAX,opts[opts.length-1]);const ratio=Math.max(0,Math.min(1,load/LOAD_CAP));const raw=min+(max-min)*ratio;const clamped=Math.min(Math.max(raw,MIN),MAX);const snapped=nearestFrom(clamped,opts);memoryCache[bucket]=snapped;return snapped};const toNum=v=>{const n=Number(v);return Number.isFinite(n)?n:null};const coerceServerPayload=val=>{if(val&&typeof val==="object"){const interval=toNum(val.interval);const load=toNum(val.load);return{interval,load}}if(Number.isFinite(Number(val))){const n=Number(val);const mode=(vars.server_payload||"auto").toLowerCase();if(mode==="interval")return{interval:n,load:null};if(mode==="load")return{interval:null,load:n};if(n>=0&&n<=100)return{interval:null,load:n};if(n>100)return{interval:n,load:null}}return{interval:null,load:null}};const debounce=(fn,wait)=>{let t;return(...args)=>{clearTimeout(t);t=setTimeout(()=>fn(...args),wait)}};let lastInterval=null;const applyPresetThenExact=secs=>{const hb=wp&&wp.heartbeat;if(!hb||typeof hb.interval!=="function")return;if(typeof secs!=="number"||!Number.isFinite(secs))return;if(lastInterval!==null&&Math.round(lastInterval)===Math.round(secs))return;const preset=secs<=20?"fast":secs<=60?"standard":"slow";try{hb.interval(preset);hb.interval(secs)}catch{}lastInterval=secs};const setHeartbeatInterval=secs=>{let s=secs;if(s>=60){const jitter=Math.floor(Math.random()*3)-1;s=Math.max(MIN,Math.min(MAX,s+jitter))}applyPresetThenExact(s)};const debouncedSetInterval=debounce(setHeartbeatInterval,150);let failureCount=0;let nextRetryAt=0;const backoffMs=()=>Math.min(12e4,2e3*Math.pow(2,Math.min(failureCount,5)));const fetchWithTimeout=(url,opts,ms)=>{if("AbortController"in window){const ctrl=new AbortController;const t=setTimeout(()=>ctrl.abort(),ms);return fetch(url,{...opts,signal:ctrl.signal}).finally(()=>clearTimeout(t))}return Promise.race([fetch(url,opts),new Promise((_,rej)=>setTimeout(()=>rej(new Error("timeout")),ms))])};let reqSeq=0;const fetchServerData=async(nonce,rid)=>{if(Date.now()<nextRetryAt){const base=Number(vars.fallback_interval)||60;const jitter=Math.floor(Math.random()*7)-3;return{interval:Math.max(MIN,Math.min(MAX,base+jitter)),load:null,rid}}if(Math.random()>=cacheBypassRate){const cached=getLocalCache(localCacheKey);if(cached!==null)return{...coerceServerPayload(cached),rid}}const I_AM_LEADER=tryBecomeLeader();if(!I_AM_LEADER){const wait=Math.min(1500,Math.max(150,Math.floor(Math.random()*600)));await new Promise(r=>setTimeout(r,wait));const cached=getLocalCache(localCacheKey);if(cached!==null)return{...coerceServerPayload(cached),rid}}if(navigator.onLine===false){const base=Number(vars.fallback_interval)||60;const jitter=Math.floor(Math.random()*7)-3;return{interval:Math.max(MIN,Math.min(MAX,base+jitter)),load:null,rid}}const url=vars.ajax_url||typeof window.ajaxurl!=="undefined"?window.ajaxurl:`${location.origin.replace(/\/$/,"")}/wp-admin/admin-ajax.php`;try{const body=new URLSearchParams({action:"dfehc_update_heartbeat_interval"});if(nonce)body.append("nonce",nonce);const res=await fetchWithTimeout(url,{method:"POST",body,credentials:"same-origin",headers:{"Content-Type":"application/x-www-form-urlencoded; charset=UTF-8","Cache-Control":"no-store"}},6e3);if(!res.ok)throw new Error(String(res.status));const json=await res.json();if(!json||json.success!==true)throw new Error("bad payload");setLocalCache(localCacheKey,json.data);if(bc)bc.postMessage({t:"payload",data:json.data});failureCount=0;nextRetryAt=0;renewLeadership();return{...coerceServerPayload(json.data),rid}}catch{failureCount+=1;nextRetryAt=Date.now()+backoffMs();const base=Number(vars.fallback_interval)||60;const jitter=Math.floor(Math.random()*7)-3;relinquishLeadership();return{interval:Math.max(MIN,Math.min(MAX,base+jitter)),load:null,rid}}};const heartbeat={update(interval){debouncedSetInterval(interval)},updateUI(interval){const sel=document.querySelector("#dfehc-heartbeat-interval");if(sel)sel.value=String(interval);debouncedSetInterval(interval)},async init(nonce){const conn=navigator.connection||navigator.mozConnection||navigator.webkitConnection;const et=conn&&conn.effectiveType?String(conn.effectiveType):"";if("deviceMemory"in navigator&&navigator.deviceMemory<2)return;if(conn&&conn.saveData||et.startsWith("2g")||et.startsWith("slow-2g"))return;const myRid=++reqSeq;const payload=await fetchServerData(nonce,myRid);if(payload.rid!==reqSeq)return;const interval=payload.interval;const load=payload.load;const snapList=intervals[trafficLevel(typeof load==="number"?load:LOAD_CAP)]||intervals.low;const finalInterval=typeof interval==="number"?nearestFrom(Math.min(Math.max(interval,MIN),MAX),snapList):calcRecommendedIntervalFromLoad(typeof load==="number"?load:60);this.updateUI(finalInterval)}};if(bc){bc.onmessage=e=>{const m=e.data||{};if(m.t==="leader"){const tab=String(m.tab||"");if(tab&&tab!==TAB_ID)isLeader=false}if(m.t==="payload"){try{setLocalCache(localCacheKey,m.data)}catch{}Object.keys(memoryCache).forEach(k=>delete memoryCache[k]);const coerced=coerceServerPayload(m.data);const interval=coerced.interval;const load=coerced.load;const snapList=intervals[trafficLevel(typeof load==="number"?load:LOAD_CAP)]||intervals.low;const finalInterval=typeof interval==="number"?nearestFrom(Math.min(Math.max(interval,MIN),MAX),snapList):calcRecommendedIntervalFromLoad(typeof load==="number"?load:60);debouncedSetInterval(finalInterval)}}}window.addEventListener("storage",e=>{if(e.key!==localCacheKey||!e.newValue)return;try{const v=JSON.parse(e.newValue);if(!v||typeof v!=="object"||!("data"in v))return;Object.keys(memoryCache).forEach(k=>delete memoryCache[k]);const coerced=coerceServerPayload(v.data);const interval=coerced.interval;const load=coerced.load;const snapList=intervals[trafficLevel(typeof load==="number"?load:LOAD_CAP)]||intervals.low;const finalInterval=typeof interval==="number"?nearestFrom(Math.min(Math.max(interval,MIN),MAX),snapList):calcRecommendedIntervalFromLoad(typeof load==="number"?load:60);debouncedSetInterval(finalInterval)}catch{}});let memoVersion=String(vars.ver||"1");function maybeResetMemo(){const v=String(vars.ver||"1");if(v!==memoVersion){memoVersion=v;Object.keys(memoryCache).forEach(k=>delete memoryCache[k])}}function cleanup(){relinquishLeadership();if(bc&&typeof bc.close==="function"){try{bc.close()}catch{}}}document.addEventListener("visibilitychange",()=>{if(!document.hidden)nextRetryAt=0});window.addEventListener("online",()=>{nextRetryAt=0});window.addEventListener("pageshow",e=>{if(e.persisted)nextRetryAt=0});window.addEventListener("pagehide",cleanup,{once:true});window.addEventListener("beforeunload",cleanup,{once:true});document.addEventListener("DOMContentLoaded",()=>{maybeResetMemo();if((vars.heartbeat_control_enabled||"")!=="1")return;const nonce=vars.nonce;if("requestIdleCallback"in window){window.requestIdleCallback(()=>heartbeat.init(nonce))}else{setTimeout(()=>heartbeat.init(nonce),100)}const sel=document.querySelector("#dfehc-heartbeat-interval");if(sel){sel.addEventListener("change",function(){const val=parseInt(this.value,10);if(!Number.isNaN(val))heartbeat.update(Math.min(Math.max(val,MIN),MAX))})}})})(window.wp||{}); -
dynamic-front-end-heartbeat-control/trunk/readme.txt
r3405181 r3412283 3 3 Tested up to: 6.9 4 4 Requires PHP: 7.2 5 Stable tag: 1.2.99 55 Stable tag: 1.2.996 6 6 License: GPLv2 or later 7 7 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 17 17 For the best outcome it's recommended that you have a caching option configured as well as optimized pages(minified CSS, JavaScript, including optimized images). 18 18 19 <strong>If you already have the plugin files on your device you can follow the steps:</strong>20 21 Step 1: Login to your WordPress Dashboard.22 23 Step 2: Click on "Plugins" on the left-hand side menu, then select "Add New".24 25 Step 3: Click on the "Upload Plugin" button at the top of the page.26 27 Step 4: Click on the "Choose File" button and browse to the location on your computer where the plugin file is saved. Select the plugin dynamic-heartbeat.zip file and click on "Open".28 29 Step 5: Once the plugin file is uploaded, click on the "Install Now" button.30 31 Step 6: After installation, click on the "Activate" button to activate the plugin.32 33 Step 7: Once activated, the plugin will begin working right away. Allow a few visits on your pages to let the plugin fully determine the best heartbeat interval. Make sure you clear your cache after activation.34 35 19 <strong>Additional important information:</strong> 36 20 37 - Some caching plugins offer manual heartbeat control. For the best outcome, make sure that you only have this plugin controlling your heartbeat intervals automatically. This plugin is designed to integrate with any other plugins you might have installed. Compatible with other services, enhancing overall heartbeat performance and ensuring that you can leverage all the benefits that other plugins and CDNs bring to your site.21 - Some caching plugins offer manual heartbeat control. For best results, allow this plugin to manage your heartbeat intervals automatically. You do not need to configure a manual frequency in any other plugin—feel free to use their other features, but avoid enabling their manual heartbeat settings. 38 22 - Verified GDPR compliance: the plugin does not collect or store any personal data. 23 - Rigorously tested to ensure reliable performance during high-traffic surges. 39 24 40 25 == Installation == … … 52 37 With a dynamic heartbeat frequency, your website can consistently operate at optimal performance. This is especially valuable for sites in highly competitive niches where every metric affects search rankings, ad performance, and even hosting costs. A real-time adaptive frequency ensures each visitor receives the most efficient heartbeat interval, reducing response times, improving page load speed, and minimizing unnecessary server load. 53 38 54 = I have existing performance and SEO optimization plugins. Will this plugin cause any conflicts?55 Not at all. This plugin is built to enhance your website’s performance and is fully compatible with all major performance and SEO tools. The only potential overlap is that some of these plugins may include an option to set a manual heartbeat frequency. This is the only area where settings can conflict.39 = How do I configure the plugin on my website? = 40 The plugin automatically delivers the most optimal heartbeat frequency by analyzing your hosting environment, website size, and real usage patterns. In most cases, no user intervention is required after activation. 56 41 57 Even if both are enabled, your site will not crash. However, to ensure the heartbeat frequency remains dynamically managed, avoid enabling or configuring manual heartbeat frequency settings in other caching or performance plugins while using this plugin. 42 You can access Settings → DFEHC in the WordPress admin to view available configuration options, such as setting a manual heartbeat frequency or disabling the heartbeat entirely. For advanced users working on specific or unusual projects, the plugin also provides several filters that allow fine-tuning and precise control over the heartbeat pace. 43 44 = I have existing performance and SEO optimization plugins. Will this plugin cause any conflicts? = 45 Not at all. This plugin is built to enhance your website’s performance and is fully compatible with all major performance and SEO tools. The only potential overlap is that some of these plugins may include an option to set a manual heartbeat frequency. This is the only area where settings can overlap. 46 47 Even if both are enabled, your website will not crash. However, to ensure the heartbeat frequency remains dynamically managed, avoid enabling or configuring manual heartbeat frequency settings in other caching or performance plugins while using this plugin. 58 48 59 49 == Changelog == 50 51 = 1.2.996 = 52 53 * General enhancements. 60 54 61 55 = 1.2.995 = -
dynamic-front-end-heartbeat-control/trunk/visitor/cookie-helper.php
r3396626 r3412283 46 46 } 47 47 48 if (!function_exists('dfehc_client_ip')) { 49 function dfehc_client_ip(): string 50 { 51 $ip = (string) ($_SERVER['REMOTE_ADDR'] ?? '0.0.0.0'); 52 return (string) apply_filters('dfehc_client_ip', $ip); 53 } 54 } 55 48 56 if (!function_exists('dfehc_set_transient_noautoload')) { 49 57 function dfehc_set_transient_noautoload(string $key, $value, int $expiration): void 50 58 { 51 if (wp_using_ext_object_cache()) { 59 $group = defined('DFEHC_CACHE_GROUP') ? DFEHC_CACHE_GROUP : 'dfehc'; 60 61 if (function_exists('wp_using_ext_object_cache') && wp_using_ext_object_cache()) { 52 62 if (function_exists('wp_cache_add')) { 53 if (!wp_cache_add($key, $value, defined('DFEHC_CACHE_GROUP') ? DFEHC_CACHE_GROUP : 'dfehc', $expiration)) {54 wp_cache_set($key, $value, defined('DFEHC_CACHE_GROUP') ? DFEHC_CACHE_GROUP : 'dfehc', $expiration);63 if (!wp_cache_add($key, $value, $group, $expiration)) { 64 wp_cache_set($key, $value, $group, $expiration); 55 65 } 56 66 } else { 57 wp_cache_set($key, $value, defined('DFEHC_CACHE_GROUP') ? DFEHC_CACHE_GROUP : 'dfehc', $expiration);67 wp_cache_set($key, $value, $group, $expiration); 58 68 } 59 69 return; 60 70 } 71 61 72 set_transient($key, $value, $expiration); 73 62 74 global $wpdb; 63 75 if (!isset($wpdb->options)) { 64 76 return; 65 77 } 66 $opt_key = "_transient_$key";78 $opt_key = "_transient_$key"; 67 79 $opt_key_to = "_transient_timeout_$key"; 68 80 $wpdb->suppress_errors(true); 69 81 $autoload = $wpdb->get_var($wpdb->prepare("SELECT autoload FROM {$wpdb->options} WHERE option_name=%s LIMIT 1", $opt_key)); 70 82 if ($autoload === 'yes') { 71 $wpdb->update($wpdb->options, ['autoload' => 'no'], ['option_name' => $opt_key, 'autoload' => 'yes'], ['%s'], ['%s','%s']); 83 $wpdb->update( 84 $wpdb->options, 85 ['autoload' => 'no'], 86 ['option_name' => $opt_key, 'autoload' => 'yes'], 87 ['%s'], 88 ['%s', '%s'] 89 ); 72 90 } 73 91 $autoload_to = $wpdb->get_var($wpdb->prepare("SELECT autoload FROM {$wpdb->options} WHERE option_name=%s LIMIT 1", $opt_key_to)); 74 92 if ($autoload_to === 'yes') { 75 $wpdb->update($wpdb->options, ['autoload' => 'no'], ['option_name' => $opt_key_to, 'autoload' => 'yes'], ['%s'], ['%s','%s']); 93 $wpdb->update( 94 $wpdb->options, 95 ['autoload' => 'no'], 96 ['option_name' => $opt_key_to, 'autoload' => 'yes'], 97 ['%s'], 98 ['%s', '%s'] 99 ); 76 100 } 77 101 $wpdb->suppress_errors(false); … … 96 120 } 97 121 98 $len = strlen($ip_bin);122 $len = strlen($ip_bin); 99 123 $max_bits = $len * 8; 100 124 if ($mask < 0 || $mask > $max_bits) { … … 111 135 return true; 112 136 } 113 $ip_byte = ord($ip_bin[$bytes]);114 $sub_byte = ord($sub_bin[$bytes]);137 $ip_byte = ord($ip_bin[$bytes]); 138 $sub_byte = ord($sub_bin[$bytes]); 115 139 $mask_byte = (0xFF << (8 - $bits)) & 0xFF; 116 140 return ($ip_byte & $mask_byte) === ($sub_byte & $mask_byte); … … 132 156 function dfehc_select_client_ip_from_xff(string $xff, array $trustedCidrs): ?string 133 157 { 134 $candidates = array_filter(array_map('trim', explode(',', $xff)), 'strlen');158 $candidates = array_filter(array_map('trim', explode(',', $xff)), 'strlen'); 135 159 $ipNonTrusted = null; 136 160 foreach ($candidates as $ip) { … … 183 207 } 184 208 185 $ip = dfehc_client_ip();186 $ipKeyScoped = dfehc_scoped_key('dfehc_bad_ip_') . md5($ip ?: 'none');187 $group = apply_filters('dfehc_cache_group', defined('DFEHC_CACHE_GROUP') ? DFEHC_CACHE_GROUP : 'dfehc');209 $ip = dfehc_client_ip(); 210 $ipKeyScoped = dfehc_scoped_key('dfehc_bad_ip_') . md5($ip ?: 'none'); 211 $group = apply_filters('dfehc_cache_group', defined('DFEHC_CACHE_GROUP') ? DFEHC_CACHE_GROUP : 'dfehc'); 188 212 189 213 if ($ip && function_exists('wp_using_ext_object_cache') && wp_using_ext_object_cache() && function_exists('wp_cache_get')) { … … 212 236 if (function_exists('wp_is_json_request') && wp_is_json_request()) return false; 213 237 if (function_exists('rest_get_url_prefix')) { 214 $p = rest_get_url_prefix();238 $p = rest_get_url_prefix(); 215 239 $uri = $_SERVER['REQUEST_URI'] ?? ''; 216 240 if ($p && strpos($uri, '/' . trim($p, '/') . '/') === 0) return false; … … 231 255 } 232 256 233 $ip = dfehc_client_ip();234 $group = apply_filters('dfehc_cache_group', defined('DFEHC_CACHE_GROUP') ? DFEHC_CACHE_GROUP : 'dfehc');235 $maxRPM = (int) apply_filters('dfehc_max_rpm', 120);257 $ip = dfehc_client_ip(); 258 $group = apply_filters('dfehc_cache_group', defined('DFEHC_CACHE_GROUP') ? DFEHC_CACHE_GROUP : 'dfehc'); 259 $maxRPM = (int) apply_filters('dfehc_max_rpm', 120); 236 260 $badIpTtl = (int) apply_filters('dfehc_bad_ip_ttl', HOUR_IN_SECONDS); 237 261 $scopedVisitorKey = dfehc_scoped_key('dfehc_total_visitors'); … … 397 421 } 398 422 399 $cnt = (int) get_transient($scopedVisitorKey);423 $cnt = (int) get_transient($scopedVisitorKey); 400 424 $vTtl = $lifetime + (function_exists('random_int') ? random_int(0, 5) : 0); 401 425 dfehc_set_transient_noautoload($scopedVisitorKey, $cnt + 1, $vTtl); -
dynamic-front-end-heartbeat-control/trunk/visitor/manager.php
r3396626 r3412283 228 228 return; 229 229 } 230 $prev = ignore_user_abort(true); 230 231 $prev = (bool) ignore_user_abort(true); 232 231 233 if (function_exists('set_time_limit')) { 232 234 @set_time_limit(30); 233 235 } 236 234 237 try { 235 238 global $wpdb; … … 237 240 $batch_size = (int) apply_filters('dfehc_cleanup_batch_size', $batch_size); 238 241 $batch_size = max(1, min(500, $batch_size)); 239 $ids = $wpdb->get_col($wpdb->prepare( 240 "SELECT ID FROM $wpdb->users WHERE ID > %d ORDER BY ID ASC LIMIT %d", 241 $last_id, $batch_size 242 )); 242 243 $ids = $wpdb->get_col( 244 $wpdb->prepare( 245 "SELECT ID FROM $wpdb->users WHERE ID > %d ORDER BY ID ASC LIMIT %d", 246 $last_id, 247 $batch_size 248 ) 249 ); 250 243 251 if (!$ids) { 244 252 update_option(dfehc_scoped_key('dfehc_last_cleanup_cron'), time(), false); 245 253 return; 246 254 } 255 247 256 $cutoff = time() - (int) apply_filters('dfehc_activity_expiration', WEEK_IN_SECONDS); 257 248 258 foreach ($ids as $id) { 249 259 $ts = (int) get_user_meta($id, $meta_key, true); … … 252 262 } 253 263 } 264 254 265 if (count($ids) === $batch_size) { 255 wp_schedule_single_event(time() + 15 + (function_exists('random_int') ? random_int(0, 5) : rand(0, 5)), 'dfehc_cleanup_user_activity', [end($ids), $batch_size]); 256 } 266 wp_schedule_single_event( 267 time() + 15 + (function_exists('random_int') ? random_int(0, 5) : rand(0, 5)), 268 'dfehc_cleanup_user_activity', 269 [end($ids), $batch_size] 270 ); 271 } 272 257 273 update_option(dfehc_scoped_key('dfehc_last_cleanup_cron'), time(), false); 274 258 275 } finally { 259 ignore_user_abort($prev); 276 277 ignore_user_abort((bool) $prev); 278 260 279 dfehc_release_lock($lock); 261 280 } … … 444 463 } 445 464 465 function dfehc_ensure_reset_visitors_schedule(): void { 466 if (!function_exists('wp_next_scheduled') || !function_exists('wp_schedule_event')) { 467 return; 468 } 469 if (wp_next_scheduled('dfehc_reset_total_visitors_event')) { 470 return; 471 } 472 $aligned = time() - time() % 300 + 300; 473 $ts = $aligned + HOUR_IN_SECONDS; 474 $ok = wp_schedule_event($ts, 'hourly', 'dfehc_reset_total_visitors_event'); 475 if (!$ok) { 476 wp_schedule_single_event($ts, 'dfehc_reset_total_visitors_event'); 477 } 478 } 479 add_action('init', 'dfehc_ensure_reset_visitors_schedule', 10); 480 446 481 function dfehc_on_deactivate(): void { 447 482 wp_clear_scheduled_hook('dfehc_process_user_activity');
Note: See TracChangeset
for help on using the changeset viewer.