Ship production optimization M1-M12.5A: queues, metrics, HTTP observability, and vector search reliability.
Keep similar-ai from tripping the global circuit on a lone URL 502, clamp Qdrant search to 100, and add Server-Timing plus slow-request logging. Studio shared props, Academy S3 exists caching, heat chunking, and Redis/scheduler hygiene stay in this rollout.
This commit is contained in:
@@ -83,6 +83,9 @@ return [
|
||||
'low_engagement_after_days' => (int) env('COLLECTIONS_V5_LOW_ENGAGEMENT_AFTER_DAYS', 14),
|
||||
],
|
||||
'queue' => [
|
||||
// No Horizon supervisor consumes this queue today. Keep dispatch
|
||||
// off until a consumer exists (M7). Existing jobs are not deleted.
|
||||
'dispatch_enabled' => (bool) env('COLLECTIONS_V5_DISPATCH_ENABLED', false),
|
||||
'name' => env('COLLECTIONS_V5_QUEUE', 'collections'),
|
||||
'health_batch_size' => (int) env('COLLECTIONS_V5_HEALTH_BATCH_SIZE', 40),
|
||||
'recommendation_batch_size' => (int) env('COLLECTIONS_V5_RECOMMENDATION_BATCH_SIZE', 40),
|
||||
|
||||
@@ -15,6 +15,8 @@ return [
|
||||
'queues' => [
|
||||
'moderation' => env('FORUM_SECURITY_MODERATION_QUEUE', 'forum-moderation'),
|
||||
'firewall' => env('FORUM_SECURITY_FIREWALL_QUEUE', 'forum-security'),
|
||||
// Plugin jobs have no Horizon consumer. Pause enqueue until a worker exists.
|
||||
'dispatch_enabled' => (bool) env('FORUM_QUEUE_DISPATCH_ENABLED', false),
|
||||
],
|
||||
|
||||
'firewall' => [
|
||||
|
||||
+25
-3
@@ -99,6 +99,7 @@ return [
|
||||
'waits' => [
|
||||
'redis:broadcasts' => 15,
|
||||
'redis:default' => 60,
|
||||
'redis:mail' => 60,
|
||||
'redis:notifications' => 90,
|
||||
],
|
||||
|
||||
@@ -209,9 +210,9 @@ return [
|
||||
'maxJobs' => 0,
|
||||
'memory' => 128,
|
||||
'tries' => 1,
|
||||
// Long-running recommendation rebuild jobs declare timeouts up to 900s.
|
||||
// Keep the worker timeout above that ceiling so Horizon does not kill
|
||||
// healthy jobs before Laravel can enforce the job-level timeout.
|
||||
// Must stay below config/queue.php redis.retry_after (default 1080).
|
||||
// Nightly recommendation rebuilds are chunked; keep headroom for
|
||||
// remaining long jobs on the default queue.
|
||||
'timeout' => 960,
|
||||
'nice' => 0,
|
||||
],
|
||||
@@ -228,6 +229,21 @@ return [
|
||||
'timeout' => 90,
|
||||
'nice' => 0,
|
||||
],
|
||||
// Mail is intentionally isolated: SMTP latency must not block
|
||||
// search/recommendation workers, and mail jobs declare tries=3–5.
|
||||
'supervisor-mail' => [
|
||||
'connection' => 'redis',
|
||||
'queue' => ['mail'],
|
||||
'balance' => 'simple',
|
||||
'autoScalingStrategy' => 'time',
|
||||
'maxProcesses' => 1,
|
||||
'maxTime' => 0,
|
||||
'maxJobs' => 0,
|
||||
'memory' => 128,
|
||||
'tries' => 5,
|
||||
'timeout' => 90,
|
||||
'nice' => 0,
|
||||
],
|
||||
],
|
||||
|
||||
'environments' => [
|
||||
@@ -242,6 +258,9 @@ return [
|
||||
'balanceMaxShift' => 1,
|
||||
'balanceCooldown' => 3,
|
||||
],
|
||||
'supervisor-mail' => [
|
||||
'maxProcesses' => 2,
|
||||
],
|
||||
],
|
||||
|
||||
'local' => [
|
||||
@@ -251,6 +270,9 @@ return [
|
||||
'supervisor-messaging' => [
|
||||
'maxProcesses' => 2,
|
||||
],
|
||||
'supervisor-mail' => [
|
||||
'maxProcesses' => 1,
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
'slow_request' => [
|
||||
'enabled' => (bool) env('HTTP_SLOW_REQUEST_LOG_ENABLED', false),
|
||||
'threshold_ms' => (int) env('HTTP_SLOW_REQUEST_MS', 750),
|
||||
/*
|
||||
* Testing only. When set, replaces measured duration so tests do not sleep.
|
||||
*/
|
||||
'test_duration_ms' => env('HTTP_SLOW_REQUEST_TEST_DURATION_MS'),
|
||||
],
|
||||
];
|
||||
@@ -123,6 +123,14 @@ return [
|
||||
'handler' => NullHandler::class,
|
||||
],
|
||||
|
||||
'slow-http' => [
|
||||
'driver' => 'daily',
|
||||
'path' => storage_path('logs/slow-http.log'),
|
||||
'level' => 'info',
|
||||
'days' => (int) env('HTTP_SLOW_REQUEST_LOG_DAYS', 14),
|
||||
'replace_placeholders' => false,
|
||||
],
|
||||
|
||||
'emergency' => [
|
||||
'path' => storage_path('logs/laravel.log'),
|
||||
],
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Hourly artwork metric snapshots (heat, rising, windowed leaderboards).
|
||||
*
|
||||
* Default retention is 30 days so monthly leaderboards and Studio 30d KPIs
|
||||
* have a complete window after warm-up. Override with env; never hardcode.
|
||||
* Studio 30d KPIs use MAX-MIN of cumulative counters, never SUM.
|
||||
*/
|
||||
return [
|
||||
|
||||
'hourly_snapshot_retention_days' => (int) env('ARTWORK_METRIC_HOURLY_RETENTION_DAYS', 30),
|
||||
|
||||
'hourly_snapshot_prune_chunk' => (int) env('ARTWORK_METRIC_HOURLY_PRUNE_CHUNK', 5000),
|
||||
|
||||
'hourly_snapshot_prune_sleep_ms' => (int) env('ARTWORK_METRIC_HOURLY_PRUNE_SLEEP_MS', 50),
|
||||
|
||||
];
|
||||
+4
-1
@@ -68,7 +68,10 @@ return [
|
||||
'driver' => 'redis',
|
||||
'connection' => env('REDIS_QUEUE_CONNECTION', 'default'),
|
||||
'queue' => env('REDIS_QUEUE', 'default'),
|
||||
'retry_after' => (int) env('REDIS_QUEUE_RETRY_AFTER', 90),
|
||||
// Must exceed the longest Horizon worker timeout (960s) plus a buffer.
|
||||
// If retry_after is shorter than a running job, Redis re-queues it as a
|
||||
// new attempt and Horizon (--tries=1) marks MaxAttemptsExceeded.
|
||||
'retry_after' => (int) env('REDIS_QUEUE_RETRY_AFTER', 1080),
|
||||
'block_for' => null,
|
||||
'after_commit' => false,
|
||||
],
|
||||
|
||||
@@ -77,6 +77,11 @@ return [
|
||||
'candidate_pool' => 200, // top N candidates to run diversity filter on
|
||||
],
|
||||
|
||||
// Unique lock for RankBuildScopeListsJob. Must exceed queue wait + job
|
||||
// timeout (300s). Production wait has been ~2.5h; 6h prevents hourly
|
||||
// stacking while still recovering a dead lock the same day.
|
||||
'scope_job_unique_for' => (int) env('RANK_SCOPE_JOB_UNIQUE_FOR', 21600),
|
||||
|
||||
// ── Anti-spam / burst-view damping ──────────────────────────────────────
|
||||
'spam' => [
|
||||
'views_24h_threshold' => 2000,
|
||||
|
||||
@@ -54,6 +54,9 @@ return [
|
||||
'forum-threads' => [
|
||||
'size' => (int) env('SITEMAPS_SHARD_FORUM_THREADS_SIZE', 10000),
|
||||
],
|
||||
'tags' => [
|
||||
'size' => (int) env('SITEMAPS_SHARD_TAGS_SIZE', 25000),
|
||||
],
|
||||
'collections' => [
|
||||
'size' => (int) env('SITEMAPS_SHARD_COLLECTIONS_SIZE', 10000),
|
||||
],
|
||||
|
||||
@@ -7,6 +7,7 @@ return [
|
||||
|
||||
'queue' => [
|
||||
'name' => env('SKINBASE_AI_MODERATION_QUEUE', 'forum-moderation'),
|
||||
'dispatch_enabled' => (bool) env('FORUM_QUEUE_DISPATCH_ENABLED', false),
|
||||
],
|
||||
|
||||
'preflight' => [
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
'online_visitors' => [
|
||||
// Cap SSCAN/all() so moderation pages never SMEMBERS the 2.4M-member index.
|
||||
'index_read_limit' => (int) env('ONLINE_VISITOR_INDEX_READ_LIMIT', 2000),
|
||||
'prune_enabled' => (bool) env('ONLINE_VISITOR_INDEX_PRUNE_ENABLED', false),
|
||||
'prune_scan_count' => (int) env('ONLINE_VISITOR_INDEX_PRUNE_SCAN_COUNT', 500),
|
||||
'prune_max_batches' => (int) env('ONLINE_VISITOR_INDEX_PRUNE_MAX_BATCHES', 200),
|
||||
'prune_max_members' => (int) env('ONLINE_VISITOR_INDEX_PRUNE_MAX_MEMBERS', 100000),
|
||||
'prune_sleep_ms' => (int) env('ONLINE_VISITOR_INDEX_PRUNE_SLEEP_MS', 25),
|
||||
],
|
||||
];
|
||||
Reference in New Issue
Block a user