| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273 |
- import { config } from "../config/index.js";
- import { descarregarModelo } from "../services/ollamaClient.js";
- import { partesLocaisSaoPaulo } from "./timezone.js";
- // jobs que usam o Ollama em lote (avaliação, ingest RAG) — só um por vez, pra não somar
- // memória/GPU e travar a máquina
- let jobPesadoAtual = null;
- // janela "HH-HH" no fuso de São Paulo (ex.: "19-07" atravessa a meia-noite); vazia = sempre
- export function dentroDaJanela(date = new Date()) {
- const janela = config.jobsPesados.janela;
- if (!janela) return true;
- const m = /^(\d{1,2})-(\d{1,2})$/.exec(janela);
- if (!m) return true;
- const inicio = Number(m[1]);
- const fim = Number(m[2]);
- const hora = Number(partesLocaisSaoPaulo(date, { hour: "numeric", hourCycle: "h23" }).hour);
- if (inicio === fim) return true;
- return inicio < fim ? hora >= inicio && hora < fim : hora >= inicio || hora < fim;
- }
- export function pausaEntreItens() {
- const ms = config.jobsPesados.pausaMs;
- return ms > 0 ? new Promise((resolve) => setTimeout(resolve, ms)) : Promise.resolve();
- }
- // roda `fn` se nenhum outro job pesado estiver rodando e se estiver dentro da janela;
- // caso contrário devolve null (o ciclo é só pulado, o próximo tenta de novo)
- export async function comLockPesado(nome, fn) {
- if (!dentroDaJanela()) {
- console.log(`[${nome}] fora da janela JOBS_PESADOS_JANELA=${config.jobsPesados.janela}, pulando`);
- return null;
- }
- if (jobPesadoAtual) {
- console.log(`[${nome}] ocupado por ${jobPesadoAtual}, pulando este ciclo`);
- return null;
- }
- jobPesadoAtual = nome;
- try {
- return await fn();
- } finally {
- jobPesadoAtual = null;
- }
- }
- // disparo manual (admin via HTTP): ignora a janela, mas respeita a exclusão mútua com os jobs —
- // se houver um job pesado rodando, falha com 409 em vez de somar carga no Ollama
- export async function comLockPesadoManual(nome, fn) {
- if (jobPesadoAtual) {
- // o frontend mostra a mensagem crua — por isso em português
- const err = new Error(`Há outro processamento pesado em andamento (${jobPesadoAtual}). Tente de novo em alguns minutos.`);
- err.statusCode = 409;
- throw err;
- }
- jobPesadoAtual = nome;
- try {
- return await fn();
- } finally {
- jobPesadoAtual = null;
- }
- }
- // best-effort: falha ao descarregar não deve derrubar o job
- export async function descarregarModelos(nome, modelos) {
- if (!config.jobsPesados.descarregarAoFim) return;
- for (const model of new Set(modelos.filter(Boolean))) {
- try {
- await descarregarModelo(model);
- } catch (err) {
- console.warn(`[${nome}] falha ao descarregar ${model}:`, err.message);
- }
- }
- }
|