Saltar a contenido

← Volver al índice | Pivote Quarkus+Dapr

Análisis de Viabilidad: Migración Pekko → Quarkus + Dapr

Tipo: Investigación — Evaluación de Viabilidad
Audiencia: Equipo de desarrollo, arquitectos
Fecha: 20 de marzo de 2026
Relacionado con: Pivote Arquitectónico | Análisis Tecnológico


1. Inventario de Actores Pekko Actuales → Equivalentes Dapr

1.1 Mapeo Actor por Actor

Actor Pekko (original) Responsabilidad Equivalente Dapr/Quarkus Complejidad migración
ImageProcessingActor Pre-procesado de imágenes (resize, limpieza, OpenCV) Quarkus service + Virtual Thread — tarea CPU-bound sin estado 🟢 Baja
AIInferenceActor Invocación de LLM para identificación LangChain4j @RegisterAiService — la interfaz CDI sustituye completamente al actor 🟢 Baja
SampleIngestionActor Ingesta de muestras desde múltiples fuentes Dapr Bindings + Pub/Sub — webhooks M365, GSheets polling 🟢 Baja
RealTimeDetectionActor Streaming de video/frames desde cámara Quarkus WebSocket + Virtual Threads — Java 25 maneja millones de conexiones 🟡 Media
Actor: Sesión Cámara Estado de conexión por dispositivo (gemelo digital) Dapr State Management — estado por clave (session_id) en Redis 🟢 Baja
Actor: Pipeline IA Orquestación multi-paso (preproceso → inferencia → resultado) Dapr Workflow — orquestación declarativa con retry, compensación 🟢 Baja
Actor: Ingesta Datos ETL de SharePoint/GSheets Dapr Bindings (input bindings para Graph API webhooks) 🟢 Baja
Pekko Streams Backpressure y streaming reactivo Quarkus Mutiny + SmallRye Reactive Messaging 🟡 Media
Cluster Sharding Distribución elástica de actores Kubernetes auto-scaling + Dapr placement service 🟢 Baja
Supervision Tree Tolerancia a fallos jerárquica K8s liveness/readiness probes + Dapr retry policies 🟡 Media

[!TIP] Resultado: 7 de 10 componentes son de migración baja. Los 3 de complejidad media tienen alternativas probadas en el ecosistema Quarkus+K8s.

1.2 Lo que Pekko Hace Bien y No se Necesita

Capacidad Pekko ¿Se necesita en IEO? Alternativa
Cluster Sharding No (no hay millones de usuarios concurrentes) K8s HPA
Artery TCP Remoting No (no hay cluster multi-nodo en MVP) Dapr Service Invocation
Supervision Trees Sí, pero simplificada K8s probes + Dapr retry
Pekko Persistence Sí (event sourcing) Dapr State + PostgreSQL
Pekko Streams Parcialmente (backpressure) SmallRye Reactive Messaging + Kafka

2. Máquinas de Estados Finitos (FSMs) del Sistema

2.1 FSM: Ciclo de Vida de una Muestra

stateDiagram-v2
    [*] --> Capturada: Foto desde cámara/upload

    Capturada --> Preprocesando: OpenCV limpieza
    Preprocesando --> EnColaIA: Imagen normalizada
    EnColaIA --> IdentificandoIA: Modelo disponible
    IdentificandoIA --> IdentificadaIA: Confianza >= umbral
    IdentificandoIA --> RequiereRevision: Confianza < umbral

    IdentificadaIA --> PendienteConfirmacion: Espera validación humana
    RequiereRevision --> PendienteConfirmacion: Prioridad alta

    PendienteConfirmacion --> Confirmada: Investigador valida
    PendienteConfirmacion --> Corregida: Investigador corrige especie/edad

    Confirmada --> EnBaseDatos: Persistida con metadatos
    Corregida --> EnBaseDatos: Persistida + feedback IA

    EnBaseDatos --> EmbeddingsGenerados: Vectores en ChromaDB
    EmbeddingsGenerados --> [*]: Disponible para búsqueda

Implementación Dapr:

Estado Building Block Actor Pekko equivalente
Capturada → Preprocesando Virtual Thread (CPU-bound) ImageProcessingActor
EnColaIA → IdentificandoIA Dapr Pub/Sub (evento imagen.lista) AIInferenceActor (cola interna)
IdentificandoIA LangChain4j (@RegisterAiService) AIInferenceActor
PendienteConfirmacion Dapr State (estado mutable por muestra) Actor con FSM interna
Confirmada → EnBaseDatos Dapr Pub/Sub (evento muestra.confirmada) SampleIngestionActor
→ EmbeddingsGenerados Dapr Workflow (paso de pipeline) Pekko Streams

2.2 FSM: Pipeline ETL de Carga Incremental

stateDiagram-v2
    [*] --> Monitorizado: Fuente registrada

    Monitorizado --> CambioDetectado: Polling/Webhook/CDC
    CambioDetectado --> DescargandoDelta: Fetch datos nuevos

    DescargandoDelta --> ValidandoEsquema: Datos descargados
    ValidandoEsquema --> EsquemaOK: Sin cambios de estructura
    ValidandoEsquema --> EsquemaCambiado: Columnas nuevas/eliminadas

    EsquemaCambiado --> EsperandoAprobacion: Alerta a Responsable
    EsperandoAprobacion --> EsquemaOK: Responsable aprueba
    EsperandoAprobacion --> Rechazado: Responsable rechaza
    Rechazado --> Monitorizado: Vuelve a monitorizar

    EsquemaOK --> Normalizando: Aplicar transformaciones
    Normalizando --> GenerandoEmbeddings: Datos normalizados
    GenerandoEmbeddings --> Persistiendo: Vectores listos

    Persistiendo --> CargaCompletada: PostgreSQL + ChromaDB + MinIO
    CargaCompletada --> LogRegistrado: Auditoría inmutable
    LogRegistrado --> Monitorizado: Siguiente ciclo

    CambioDetectado --> ErrorConexion: Fuente no disponible
    ErrorConexion --> Monitorizado: Retry con backoff

Implementación Dapr:

Transición Building Block Detalle
Monitorizado → CambioDetectado Dapr Input Binding (GSheets, Graph API, MQTT) Configuración declarativa YAML
Descargando → Validando Dapr Workflow Activity Paso con timeout y retry
EsquemaCambiado → EsperandoAprobacion Dapr State + Pub/Sub (notificación) Estado persistido, evento esquema.cambio
Normalizando → Embeddings → Persistiendo Dapr Workflow (3 activities secuenciales) Con compensación si falla
Log Dapr State (append-only) Log inmutable por dataset

2.3 FSM: Entrenamiento LoRA (Long-Running Process)

stateDiagram-v2
    [*] --> Planificado: Configuración recibida

    Planificado --> PreparandoDataset: Descarga y preproceso imágenes
    PreparandoDataset --> DatasetListo: Captions + tags generados

    DatasetListo --> Entrenando: GPU disponible
    DatasetListo --> EsperandoGPU: GPU ocupada
    EsperandoGPU --> Entrenando: GPU liberada

    Entrenando --> Entrenando: Progreso (epoch N/M)
    Entrenando --> EntrenamientoCompletado: Todas las epochs OK
    Entrenando --> ErrorEntrenamiento: OOM / error GPU

    ErrorEntrenamiento --> Planificado: Retry con params ajustados

    EntrenamientoCompletado --> Validando: Test de precisión
    Validando --> Aprobado: Precisión >= 80%
    Validando --> Rechazado2: Precisión < 80%

    Rechazado2 --> Planificado: Re-entrenar con más datos
    Aprobado --> Desplegando: Copiar a Ollama
    Desplegando --> Activo: Modelo disponible
    Activo --> [*]

Implementación Dapr:

Estado Building Block Ventaja vs Pekko
Todo el workflow Dapr Workflow Estado persiste entre reinicios (Pekko pierde estado en crash)
Progreso Dapr State (training:{id}{epoch, loss, eta}) Consultable via API REST
EsperandoGPU Dapr Timer + externalización Sin actor bloqueado en memoria
Notificaciones Dapr Pub/Sub Desacoplado del proceso

2.4 FSM: Sesión de Cámara en Tiempo Real

stateDiagram-v2
    [*] --> Desconectado

    Desconectado --> Conectando: WebSocket abierto
    Conectando --> Activa: Handshake OK + auth
    Conectando --> ErrorAuth: Token inválido
    ErrorAuth --> Desconectado

    Activa --> Streaming: Frames recibidos
    Streaming --> Streaming: Frame N procesado
    Streaming --> Pausada: Sin frames (>30s)
    Pausada --> Streaming: Frames reanudados
    Pausada --> Desconectada: Timeout (5min)

    Streaming --> BufferOffline: Pérdida de conexión
    BufferOffline --> Sincronizando: Conexión recuperada
    Sincronizando --> Activa: Buffer vaciado

    Activa --> Desconectado: Cierre explícito
    Desconectada --> Desconectado

Implementación Dapr:

Aspecto Pekko (original) Quarkus + Dapr
WebSocket Pekko HTTP Quarkus WebSocket Next (Jakarta WebSocket)
Estado de sesión Actor en memoria Dapr State en Redis (persiste entre reinicios)
Concurrencia 1 actor por sesión 1 Virtual Thread por sesión (Java 25 — millones posibles)
Buffer offline Actor interno Dapr State + cola Kafka
Timeout Pekko Scheduler Quarkus @Scheduled o Dapr Timer

3. Pekko vs Dapr — Comparativa Definitiva

3.1 Filosofía

Aspecto Apache Pekko Dapr
Modelo Actor Model (Erlang/OTP) Sidecar + Building Blocks
Código Escribes lógica de actores, supervisión, mensajes Escribes lógica de negocio, Dapr maneja la infraestructura
Estado Encapsulado en RAM del actor Externalizado (Redis, PostgreSQL)
Resiliencia Supervision trees (código custom) Retry policies + K8s probes (declarativo YAML)
Distribución Cluster Sharding + Artery (complejo) K8s scaling + Dapr placement (automático)
Latencia Ultra-baja (~μs entre actores) Baja (~1-5ms via sidecar HTTP/gRPC)
Curva Alta (conceptos: actores, mailbox, supervision, stash, become) Baja (HTTP/gRPC estándar + YAML)

3.2 Cuándo Pekko es Mejor

Escenario ¿Aplica al IEO?
Trading de alta frecuencia (μs de latencia) ❌ No
Telecomunicaciones con millones de sesiones simultáneas ❌ No
Sistemas distribuidos multi-datacenter con consistencia eventual compleja ❌ No
Procesamiento de streams masivos con backpressure a nivel de byte ❌ No (Kafka cubre esto)

3.3 Cuándo Dapr es Mejor

Escenario ¿Aplica al IEO?
Sistema de integración multi-fuente (GSheets, SharePoint, Oracle, IoT) Sí — core del proyecto
Orquestación de workflows (ETL, entrenamiento IA)
Microservicios en Kubernetes con escalado automático Sí — DragonCloud
Estado de procesos largos consultable via API Sí — LoRA training, batch embeddings
Pub/Sub entre servicios desacoplados Sí — eventos muestra.creada, esquema.cambio
Developers centrados en lógica de negocio, no en infraestructura Sí — equipo pequeño

[!IMPORTANT] Veredicto: Pekko es un martillo de precisión para cirugía de latencia (telecoms, trading). El IEO necesita un sistema de integración con workflows y observabilidad — Dapr está diseñado exactamente para esto. Con Dapr, el código Quarkus se centra en la lógica de negocio: identificar especies, procesar datos, generar embeddings. La coordinación de actores desaparece del código.


4. Evaluación de Viabilidad: Escenarios Críticos

4.1 Matriz de Viabilidad

Escenario Viabilidad Quarkus+Dapr Riesgo Mitigación
Identificación de especie via cámara ✅ Alta Latencia WebSocket Virtual Threads eliminan bloqueo
ETL incremental multi-fuente ✅ Alta Formatos heterogéneos Dapr Bindings + validación custom
Entrenamiento LoRA (horas/días) ✅ Alta Estado perdido en crash Dapr Workflow persiste estado automáticamente
Búsqueda semántica CAG+RAG ✅ Alta Latencia RAG LangChain4j con caché KV-Cache + Redis
Video streaming 60fps ✅ Media-Alta Overhead sidecar (~1-5ms) WebSocket directo (sin pasar por Dapr) para frames
Sincronización offline barco ✅ Alta Sin red durante días Dapr State local + sync via cola al volver
Dashboard en tiempo real ✅ Alta SSE/WebSocket concurrente Virtual Threads — sin límite práctico
Migración a K8s ✅ Alta Complejidad operativa Dapr simplifica networking inter-servicio
Multi-modelo IA por departamento ✅ Alta Gestión de modelos LangChain4j multi-model config centralizada
Auditoría ENS ✅ Alta Trazabilidad Dapr + OpenTelemetry — tracing completo

4.2 Escenario de Mayor Riesgo: Video Streaming

El único escenario donde Pekko tiene ventaja teórica es el streaming de video a 60fps, donde el overhead del sidecar Dapr (~1-5ms) podría ser un factor.

Solución arquitectónica:

flowchart LR
    CAM["Cámara 60fps"] -->|"WebSocket directo"| QK["Quarkus WebSocket - Virtual Thread"]
    QK -->|"frame limpio cada 1s"| DAPR["Dapr Service Invocation"]
    DAPR --> OLLAMA["Ollama (inferencia)"]
    OLLAMA -->|"resultado"| DAPR
    DAPR -->|"SSE"| UI["Frontend"]

    style QK fill:#2ecc71,color:#fff
    style DAPR fill:#9b59b6,color:#fff

El truco: el WebSocket de video va directo a Quarkus (sin Dapr sidecar) porque es una conexión punto-a-punto de alta frecuencia. Dapr solo se involucra para la inferencia (1 request/segundo, no 60). Es el patrón "sidecar bypass for hot path".


5. Código que Desaparece con la Migración

5.1 Lo que hay que escribir con Pekko (y ya no)

// ❌ ANTES: ~200 líneas de actor + supervisión + protocolo
public class AIInferenceActor extends AbstractBehavior<AIInferenceActor.Command> {
    sealed interface Command permits InferImage, GetStatus, Shutdown {}
    record InferImage(String imagePath, ActorRef<Result> replyTo) implements Command {}
    // ... 50 líneas de protocolo

    @Override
    public Receive<Command> createReceive() {
        return newReceiveBuilder()
            .onMessage(InferImage.class, this::onInferImage)
            .onMessage(GetStatus.class, this::onGetStatus)
            .onSignal(PreRestart.class, signal -> onPreRestart())
            // ... 30 líneas de handling
            .build();
    }
    // ... 100 líneas de lógica + retry + supervision
}
// ✅ DESPUÉS: ~15 líneas con Quarkus + LangChain4j
@RegisterAiService(modelName = "qwen25-vl")
@SystemMessage("Eres un experto en identificación de especies marinas del IEO...")
public interface IdentificadorEspecies {
    Identificacion identificar(@V("prompt") String prompt, @ImageUrl String imagenUrl);
}

5.2 Reducción de Código Estimada

Componente Líneas con Pekko Líneas con Quarkus+Dapr Reducción
Actores IA (4 types) ~800 ~60 (interfaces CDI) -92%
Protocolos de mensajes ~400 0 (HTTP/gRPC estándar) -100%
Supervisión y recovery ~300 ~20 (YAML retry policies) -93%
Cluster y sharding ~200 0 (K8s automático) -100%
Workflows ETL ~500 ~150 (Dapr Workflow) -70%
Total ~2.200 ~230 -90%

6. Conclusión

[!IMPORTANT] La migración de Pekko a Quarkus + Dapr es VIABLE y RECOMENDADA para el proyecto IEO.

Factor Evaluación
Viabilidad técnica ✅ Alta — todos los escenarios cubiertos
Riesgo mayor 🟡 Video streaming (mitigado con sidecar bypass)
Reducción de código ~90% menos código de infraestructura
Foco en negocio El equipo escribe lógica de identificación, no actores
Cloud-native Docker → K8s (DragonCloud) nativo
IA LangChain4j ≥ Spring AI en patrones agénticos
Java 25 Virtual Threads eliminan la necesidad primaria de actores

Decisión: Proceder con la migración completa de la documentación al nuevo stack.


Documentos Relacionados

Nivel Documento Descripción
Arquitectura Pivote Quarkus+Dapr Stack propuesto completo
Investigación Análisis Tecnológico Documento original con Pekko (a migrar)
Arquitectura Arquitectura del Sistema Stack actual (a migrar)