Files
pastor-online/backend/app/parallel.py
T
omp 621679cee5 fix: corrige bugs críticos de índice de páginas e cache de OCR
- C-1: corrige índice de páginas OCR'd (retorna page_number em vez de position)
- C-2: adiciona tratamento para falhas de OCR no cache (não distingue erro de não processado)
- Integra paralelização do OCR via subprocessos (parallel.py)
- Indexador usa extração paralela quando disponível
- Melhora relatório de progresso com contagem de páginas processadas
2026-11-01 20:01:28 -03:00

60 lines
1.6 KiB
Python

"""Execução paralela de tarefas independentes.
O gargalo da indexação é o Tesseract, um processo externo. Como o trabalho
liberado por cada tarefa é predominantemente I/O (subprocesso e escrita em
disco), threads bastam: o GIL é liberado durante a espera pelo processo.
`parallel_map` preserva a ordem dos resultados e isola falhas — uma tarefa que
lança exceção não derruba as demais.
"""
from __future__ import annotations
from concurrent.futures import ThreadPoolExecutor
from typing import Callable, Iterable, Sequence, TypeVar
T = TypeVar("T")
R = TypeVar("R")
def parallel_map(
items: Sequence[T],
function: Callable[[T], R],
workers: int = 4,
default: R | None = None,
) -> list[R | None]:
"""Aplica `function` a cada item em paralelo, preservando a ordem.
Itens cuja função lança exceção recebem o valor `default`.
"""
if not items:
return []
worker_count = max(1, min(workers, len(items)))
def guarded(item: T) -> R | None:
try:
return function(item)
except Exception:
return default
if worker_count == 1:
return [guarded(item) for item in items]
with ThreadPoolExecutor(max_workers=worker_count) as pool:
return list(pool.map(guarded, items))
def chunked(items: Iterable[T], size: int) -> list[list[T]]:
"""Divide um iterável em lotes de tamanho `size`."""
chunk: list[T] = []
batches: list[list[T]] = []
for item in items:
chunk.append(item)
if len(chunk) >= size:
batches.append(chunk)
chunk = []
if chunk:
batches.append(chunk)
return batches