Executando verificação de segurança...
1

[Help]: JOB Laravel, problema com SQL Server

Estou tendo problema com uma job onde esta no Laravel Nigthwatch esta acusando um erro:
Unhandled 42000 Laravel 11.45.1 PHP 8.3.23 Illuminate\Database\QueryException SQLSTATE[42000]: [Microsoft][ODBC Driver 17 for SQL Server][SQL Server]New transaction is not allowed because there are other threads running in the session.
Porém quando eu roda a job em ambiente local não acontece nenhum erro, esse é minha JOB:

try {
            $this->getUserIdBlackboardByCourse($this->freeCourse->id);
            $grades = $this->getGradeColumnsByCourse("CURSOLIVRE-".$this->freeCourse->id);
            $gradeId = null;

            /**
             * Busca o id da coluna de notas da prova final de integração
             */
            foreach ($grades as $grade) {
                if ($grade['name'] == $this->freeCourse->prova_final_integracao)
                    $gradeId = $grade['id'];
            }

            // prova que define a nota do aluno
            if ($gradeId) {
                $studentsGrades = $this->getCourseGrades("CURSOLIVRE-".$this->freeCourse->id, $gradeId);

                //atualiza os alunos com base na nota que veio do blackboard
                foreach ($studentsGrades as $studentGrade) {
                    $studentGrade['score'] = $studentGrade['score'] ?? -1;

                    if($studentGrade['score'] == -1 && !$this->closeCourse) continue;

                    $student = StudentFreeCourses::with('lyAluno')->whereHas('lyAlunoFL', function ($query) use ($studentGrade) {
                        $query->where('FL_FIELD_15', $studentGrade['userId']);
                    })
                        ->where('curso_livre_id', $this->freeCourse->id)
                        ->where('status', '<>', StudentFreeCourses::STATUS_APROVADO)
                        ->first();

                    // Se o student NÃO existir, significa que não integrado com o blackboard. Em tese, sempre deve passar aqui
                    if ($student && ($studentGrade['score'] != -1)) {
                        $student->nota_final = $studentGrade['score'];
                        $student->status = $studentGrade['score'] >= $this->freeCourse->nota_minima_aprovacao ? StudentFreeCourses::STATUS_APROVADO : StudentFreeCourses::STATUS_REPROVADO;
                        $student->save();

                        // Cria o certificado, envia para a clouddocs e envia email para o aluno.
                        if ($student->status == StudentFreeCourses::STATUS_APROVADO) {
                            ProcessFreeCourseCertificateClouddocs::dispatch($student);
                            
                            if($this->freeCourse->atividade_complementar){
                                // Dispara a job para inserir os alunos na atividade complementar
                                ProcessInsertFreeCourseComplementaryActivity::dispatch($this->freeCourse, $student);
                            }
                        }
                    }
                }

                if ($this->closeCourse) {
                    // Os alunos do curso que ficaram sem nota na prova, são reprovados aqui
                    StudentFreeCourses::where('curso_livre_id', $this->freeCourse->id)
                        ->where('status', StudentFreeCourses::STATUS_MATRICULADO)
                        ->update([
                            'status' => StudentFreeCourses::STATUS_REPROVADO
                        ]);
    
                    $this->freeCourse->status = FreeCourse::STATUS_COMPLETED;
                    $this->freeCourse->save();
                }
            }

            Log::info('Sucesso');
        } catch (\Exception $e) {
            Log::info('erro' . $e->getMessage());
            if($this->closeCourse){
                $this->freeCourse->status = FreeCourse::STATUS_ERROR;
                $this->freeCourse->save();
            }
            throw $e;
        }
Carregando publicação patrocinada...
2

Me parece que você rodou uma vez isso em produção só que o processo não foi encerrado corretamente, e deve estar travando qualquer coisa nova, como se tivesse sido feito um lock table e ela não foi destravada posteriormente.

Não acho que esse erro esteja relacionado com o código do job, mas sim com o ambiente de produção, acredito que se você explicar dessa maneira e pedir auxilio pra alguma IA ela pode te guiar em como debugar o estado da máquina e descobrir oque causa essa trava nas transactions.

Pelo que me lembro as transactions são um mecanismo pra facilitar o rollback das queries, se tiver uma em aberto sem finalizar muito provável que não vai dar pra fazer nada no banco.

2

Meus 2 cents,

Alguns pitacos:

  1. Nao parece ser problema do processo em si - mas da execucao concorrente

  2. Como o processo eh executado de hora eh hora - pode ser que ele tente uma execucao sendo que a anterior ainda nao terminou ou ficou travada. Neste caso, voce pode colocar algum semaforo/flag para indicar que o processo ainda esta em execucao e evitar novas execucoes.

2.1. Seria interessante que neste caso, quando o flag indicar que ainda tem processo em execucao, seja enviado um email (ou semelhante) para o sysadmin verificar o que pode ter acontecido com o processo.

  1. Como voce ja indicou o SQL problematico (...$student = StudentFreeCourses::with('lyAluno'...) e logo depois tem um "$student->save()" da para entender que eh ali que a excecao ocorre, mas devido a esta questao de dupla execucao

  2. Chutaria como bandido um "dispatch" ou o "update" no final.

De qualquer forma, o flag para evitar dupla execucao deve resolver.

OBS: "resolver" aqui significa: a excecao nao ocorrer. Mas seria interessante descobrir porque esta demorando tanto para executar, afinal 1 hora para um processo que parece simples nao faz muito sentido.

Saude e Sucesso !

1

Esse job está sendo disparado como? quais os traits ela está usando?

Não tem nenhuma informação sobre transações. precisamos de mais contexto pra te ajudar.

Também ajuda se você colocar o log completo, não tem como saber onde está ocorrendo o erro

1

Esse job é executa em um command que executa de hora em hora:

$courses = FreeCourse::where('status', FreeCourse::STATUS_IN_PROGRESS)->where('auto_aprovar_reprovar', true)->get();
foreach ($courses as $course) {
    ProcessCloseCourse::dispatch($course, false);
}

SQLSTATE[42000]: [Microsoft][ODBC Driver 17 for SQL Server][SQL Server]New transaction is not allowed because there are other threads running in the session. (Connection: sqlsrv, SQL: select top 1 * from [APP].[sig].[aluno_cursos_livres] where exists (select * from [Lyceum].[dbo].[LY_FL_ALUNO] where [APP].[sig].[aluno_cursos_livres].[aluno] = [Lyceum].[dbo].[LY_FL_ALUNO].[ALUNO] and [FL_FIELD_15] = '') and [curso_livre_id] = '' and [status] <> 2)
Essa é toda a informação que peguei.
Eu só entendi que o erro esta no select que pega o aluno, mas ele em si não tem nenhum problema.