Skip to content

UnCarnaval/beethoven

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 

Repository files navigation

Beethoven — Documentación general

Qué es, en pocas palabras

Beethoven es un servidor central (orquestador) que reparte trabajo entre nodos conectados por WebSocket. La idea es parecida a un pool de minería: quien más aporta (aquí: dispositivos activos y tiempo en línea) acumula más créditos y tiende a recibir más asignaciones; cada asignación consume crédito y corresponde a una tarea (por ejemplo, una URL a ejecutar en el nodo).

Los nodos no deciden a quién se envía cada tarea: solo se conectan, informan estado, envían URLs al pool global y ejecutan lo que el servidor les manda.


Glosario rápido

Término Significado
Nodo Cliente WebSocket que ejecuta tareas y puede enviar URLs al pool.
Pool Cola global de tareas (URL + dueño + prioridad + reintentos).
Crédito Unidad de saldo del nodo; el servidor la descuenta al enviar un execute según device_spread.
device_spread one_device (defecto): 1 crédito por execute. all_devices: N créditos con N = ranuras idle del ejecutor (último heartbeat). Misma URL en todas las idle.
device_slots Obligatorio en cada heartbeat: una entrada por ranura (idle / executing / unavailable). Capacidad diaria = idle + executing; active_devices debe ser len(device_slots).
kind / track_count En tareas con una sola url: kind puede ser track o playlist. Si es playlist, track_count (nº de pistas) es obligatorio. Beethoven lo reenvía en execute para que el nodo estime carga; no multiplica los créditos del servidor por ese número.
limit_count / status Opcionales en cada ítem de tarea. limit_count: tope de streams para esa tarea. status: active | paused; en paused el scheduler no asigna hasta actualizar (p. ej. sync_tasks). Van en el execute hacia el nodo (limit_count puede ser null).
execute + task_progress El execute incluye client_id y owner_machine_id. El ejecutor puede enviar task_progress (streams_total); Beethoven reenvía al nodo owner para actualizar conteos sin cerrar la tarea.
Owner Nodo que envió esa URL al pool (auditoría); puede ejecutarla otro o el mismo nodo (auto-tráfico permitido).
Scheduler Bucle que acumula créditos, elige nodo y tarea, y envía execute.
Dead letter Tarea descartada tras superar el máximo de reintentos fallidos o timeouts.

Cómo encaja todo (vista simple)

  1. El nodo se registra y manda heartbeats periódicos con device_slots (obligatorio) y active_devices = número de ranuras reportadas.
  2. Cualquier nodo puede meter URLs en el pool (submit_tasks).
  3. El servidor suma créditos a los nodos en línea (con techo por día y por dispositivo; no arrastran al día siguiente).
  4. Cuando hay créditos y hay tareas, el servidor elige un par (nodo, tarea) de forma que el nodo tenga saldo suficiente para el coste de esa tarea (por prioridad entre las asequibles; puede ser trabajo que el propio nodo envió). Cada ítem puede llevar device_spread (ver abajo). Un playlist de 110 URLs se encola como 110 tareas; el coste en créditos depende del modo por fila.
  5. El nodo ejecuta y responde result (ok / fail). Los fallos y los timeouts reencolan la tarea hasta un máximo; luego va a dead letter.
  6. Si un nodo deja de estar online, las tareas que él había enviado pasan a owner libre para que otros las ejecuten (si no está online, no “merece” que le repartan trabajo).

Para mensajes JSON exactos y diagramas, ver también NODE_INTEGRATION.md.


Puertos y HTTP de administración

Puerto Uso
PORT (default 8081) WebSocket de nodos.
HTTP_ADMIN_PORT (default 9090) Solo operación:
Ruta Qué hace
GET /health El proceso responde (útil para “¿vive?”).
GET /ready Listo para tráfico; si REQUIRE_REDIS_READY=true y Redis no está bien, devuelve error.
GET /metrics Métricas Prometheus (cola, nodos online, contadores, etc.).

Variables de entorno (agrupadas)

Red y proceso

Variable Default Para qué sirve
PORT 8081 Puerto WebSocket.
HTTP_ADMIN_PORT 9090 Puerto HTTP admin.
INSTANCE_ID host + PID Identidad de esta instancia (Redis / lock).

Créditos y scheduler

Variable Default Para qué sirve
CREDITS_PER_DEVICE_PER_DAY 1440 Techo de créditos por dispositivo y día UTC (creditDayKey).
OFFLINE_AFTER_SEC 15 Sin heartbeat → nodo offline.
TASK_TIMEOUT_SEC 60 Sin result → se reencola la tarea.
SCHEDULER_TICK_MS 100 Frecuencia del bucle del scheduler.

Dinámica de créditos y dispositivos

  • Tasa fija mientras estás en línea: (cap_devices × CREDITS_PER_DEVICE_PER_DAY) / 86400 créditos por segundo, donde cap_devices es siempre idle + executing del último heartbeat (sin snapshot previo → 0 hasta el primer heartbeat). El saldo nunca supera el tope “por reloj” del día UTC: cap × (segundos desde medianoche UTC / 86400).
  • El techo acumulable del día sigue a cap_devices en cada heartbeat (ranuras unavailable no suman al techo).
  • Si ya tenías más saldo que el nuevo techo (porque bajaron dispositivos), el saldo se ajusta al techo; lo que ya gastaste en execute no vuelve (sigue descontado).
  • Si suben dispositivos, el techo sube y puedes seguir acumulando hasta ese máximo con el saldo que te quedaba.
  • Reconectar el mismo día con el mismo machine_id: otro register conserva créditos del día, creditDayKey y reputación (no arranca de cero). El techo se alinea otra vez con el próximo heartbeat.
  • Con device_slots vacío (active_devices: 0) no se acumula; el saldo no se anula por el techo: puedes gastar lo que ya tenías hasta volver a tener ranuras con capacidad.

Techo cumplido y reparto de tareas

  • Cuando un nodo llega al techo de acumulación del día (cap_devices × créditos/día), se marca una vez ese día UTC y dejan de asignarse al pool las tareas cuyo owner es ese nodo (sus URLs quedan en cola hasta el día siguiente).
  • Ese mismo nodo sigue pudiendo recibir execute de tareas de otros (o con owner null), si sigue online, con créditos ≥ 1 y al menos una ranura idle en el último heartbeat.
  • Objetivo: seguir trabajando aunque ya no se “promocione” tu contenido propio ese día. Si en cola solo quedan tareas de nodos ya en techo, puede no haber nada asignable hasta que entre trabajo de otros o cambie el día.

Límites y abuso

Variable Default Para qué sirve
MAX_POOL_TASKS 100000 Tope de tareas en cola.
MAX_WS_MESSAGE_BYTES 1048576 Tamaño máximo de un mensaje WS.
SUBMIT_RATE_WINDOW_MS 1000 Ventana para rate limit de submit_tasks.
SUBMIT_RATE_MAX 200 Máx. submit_tasks aceptados por nodo en esa ventana.
TASK_MAX_ATTEMPTS 5 Reintentos antes de dead letter.
MAX_PLAYLIST_ITEMS 2000 Máx. URLs por ítem playlist / album en un submit_tasks.
MAX_DEVICE_SLOTS_HEARTBEAT 512 Máx. entradas en heartbeat.device_slots.
MAX_TASK_TRACK_COUNT 50000 Máx. track_count en tareas kind: "playlist" (URL única).
MAX_TASK_LIMIT_COUNT 50000000 Máx. limit_count por tarea.

URLs

Variable Default Para qué sirve
ALLOW_HTTP_URLS desactivado Pon true para permitir http://.
BLOCK_PRIVATE_HOSTS activado Pon false para no bloquear localhost / IPs privadas (solo si confías en quien envía URLs).

Autenticación de nodos

Variable Efecto
(ninguna) Sin auth (none).
BEETHOVEN_SHARED_SECRET Todos los nodos usan el mismo auth_token en register.
BEETHOVEN_NODE_TOKENS JSON {"machine_id":"token",...}: token distinto por nodo.

Si defines secret compartido y mapa JSON, gana el modo compartido (prioridad en el código actual).

Redis (opcional pero recomendado en producción)

Variable Para qué sirve
REDIS_URL Persistencia de snapshot + lock del scheduler entre réplicas.
PERSISTENCE_DEBOUNCE_MS Retraso antes de guardar en Redis (menos escrituras).
SCHEDULER_LOCK_TTL_SEC TTL del candado del scheduler (segundos).
REQUIRE_REDIS_READY true/ready falla si Redis no está usable.

Sin Redis, todo vive en memoria: un reinicio borra cola y estado (salvo lo que los nodos reenvíen).


Cómo arrancar

npm install
npm start

Con Redis:

set REDIS_URL=redis://127.0.0.1:6379
npm start

(En Linux/macOS usa export en lugar de set.)

Pruebas:

npm test

Preguntas frecuentes (muy cortas)

¿Puedo tener varias copias del servidor?
Solo con cuidado: varias instancias compitiendo sin Redis duplican trabajo. Con Redis, el lock del scheduler hace que una sola instancia reparta en cada momento (ajusta INSTANCE_ID por proceso).

¿Los créditos se acumulan de un día para otro?
No. Cada día UTC se reinicia el saldo de créditos del día; hay techo diario por dispositivo.

¿Los créditos pueden ser negativos?
No; el servidor evita bajar de cero.

¿Dónde está el contrato de mensajes?
En NODE_INTEGRATION.md (campos JSON, errores y diagramas Mermaid).


Implementación: Node.js · Contrato actual: protocol_version === 1 · Constante en código: src/protocol/constants.js.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors