Ver Fonte

perf: isolate analysis report queue workloads

yemeishu há 1 semana atrás
pai
commit
7ffe93d94c

+ 1 - 1
app/Jobs/GenerateAnalysisPdfJob.php

@@ -38,7 +38,7 @@ class GenerateAnalysisPdfJob implements ShouldQueue
         $this->recordId = $recordId;
 
         // 指定使用 pdf 队列,由独立的 pdf-worker 容器处理
-        $this->onQueue('pdf');
+        $this->onQueue((string) config('queue.workloads.pdf', 'pdf'));
         // 避免事务未提交时 worker 提前消费导致读到未提交数据
         $this->afterCommit();
     }

+ 1 - 2
app/Jobs/ProcessAnalysisReportTaskJob.php

@@ -27,7 +27,7 @@ class ProcessAnalysisReportTaskJob implements ShouldQueue
         public ?string $recordId = null
     ) {
         // 与 PDF 相关重流程统一走 pdf 队列
-        $this->onQueue('pdf');
+        $this->onQueue((string) config('queue.workloads.pdf', 'pdf'));
         // 避免事务未提交时 worker 提前消费导致读到旧数据
         $this->afterCommit();
     }
@@ -88,4 +88,3 @@ class ProcessAnalysisReportTaskJob implements ShouldQueue
         ]);
     }
 }
-

+ 3 - 1
app/Jobs/ProcessExamAnswerAnalysisJob.php

@@ -27,7 +27,9 @@ class ProcessExamAnswerAnalysisJob implements ShouldQueue
         public string $taskId,
         public array $examData
     ) {
-        $this->onQueue('pdf');
+        // This job computes and persists analysis data. Keep it off the PDF
+        // queue so report rendering can start as soon as analysis finishes.
+        $this->onQueue((string) config('queue.workloads.exam_answer_analysis', 'default'));
         $this->afterCommit();
     }
 

+ 3 - 2
app/Jobs/ProcessQuestionDifficultyCalibrationJob.php

@@ -28,8 +28,9 @@ class ProcessQuestionDifficultyCalibrationJob implements ShouldQueue
     ) {
         $this->questions = $this->compactQuestions($questions);
 
-        // 放在 pdf 队列,且由调用方在 PDF Job 之后入队,保证报告优先生成。
-        $this->onQueue('pdf');
+        // Online difficulty calibration is not on the user-visible critical
+        // path. Keep it away from PDF workers so it cannot race report output.
+        $this->onQueue((string) config('queue.workloads.question_difficulty_calibration', 'default'));
         $this->afterCommit();
     }
 

+ 11 - 5
app/Services/ExamAnswerAnalysisService.php

@@ -70,11 +70,11 @@ class ExamAnswerAnalysisService
         $hasAnswerChanged = (bool) ($recordChangeState['steps_changed'] ?? false)
             || (bool) ($recordChangeState['questions_changed'] ?? false);
         if ($hasAnswerChanged) {
-            Log::warning('ExamAnswerAnalysisService: 在线题目难度校准已移至PDF生成后异步执行', [
+            Log::warning('ExamAnswerAnalysisService: 在线题目难度校准已移至非关键路径异步执行', [
                 'paper_id' => $paperId,
                 'student_id' => $studentId,
                 'question_count' => count($questions),
-                'queue' => 'pdf',
+                'queue' => config('queue.workloads.question_difficulty_calibration', 'default'),
             ]);
         } else {
             Log::info('ExamAnswerAnalysisService: 本次答案无变化,跳过在线难度更新', [
@@ -1845,17 +1845,23 @@ class ExamAnswerAnalysisService
             ]);
 
             if (! empty($difficultyCalibrationQuestions)) {
+                $calibrationDelaySeconds = max(
+                    0,
+                    (int) config('queue.workloads.question_difficulty_calibration_delay_seconds', 30)
+                );
+
                 dispatch(new \App\Jobs\ProcessQuestionDifficultyCalibrationJob(
                     $paperId,
                     $studentId,
                     $difficultyCalibrationQuestions
-                ));
+                ))->delay(now()->addSeconds($calibrationDelaySeconds));
 
-                Log::warning('在线题目难度校准任务已加入队列(PDF生成后执行)', [
+                Log::warning('在线题目难度校准任务已加入延迟队列', [
                     'student_id' => $studentId,
                     'paper_id' => $paperId,
                     'question_count' => count($difficultyCalibrationQuestions),
-                    'queue' => 'pdf',
+                    'queue' => config('queue.workloads.question_difficulty_calibration', 'default'),
+                    'delay_seconds' => $calibrationDelaySeconds,
                 ]);
             }
         } catch (\Exception $e) {

+ 18 - 0
config/queue.php

@@ -15,6 +15,24 @@ return [
 
     'default' => env('QUEUE_CONNECTION', 'database'),
 
+    /*
+    |--------------------------------------------------------------------------
+    | Workload Queue Names
+    |--------------------------------------------------------------------------
+    |
+    | Keep browser-visible PDF generation isolated from CPU/DB side work. The
+    | defaults match docker-compose.yml: math_cms_queue consumes "default",
+    | while math_cms_pdf consumes "pdf".
+    |
+    */
+
+    'workloads' => [
+        'pdf' => env('QUEUE_PDF', 'pdf'),
+        'exam_answer_analysis' => env('QUEUE_EXAM_ANSWER_ANALYSIS', 'default'),
+        'question_difficulty_calibration' => env('QUEUE_QUESTION_DIFFICULTY_CALIBRATION', 'default'),
+        'question_difficulty_calibration_delay_seconds' => (int) env('QUEUE_QUESTION_DIFFICULTY_CALIBRATION_DELAY_SECONDS', 30),
+    ],
+
     /*
     |--------------------------------------------------------------------------
     | Queue Connections