Skip to content

prd(ingestion): extrair camada de ingestão multi-provedor com TimescaleDB #299

@danielhe4rt

Description

@danielhe4rt

Contexto

Hoje cada integration-* (discord, twitch, devto, github) mistura OAuth + Transport + ETL num só módulo, e o ETL escreve direto nas tabelas do activity, no mesmo Postgres transacional do login/painéis/gamification/identity.

Medições em produção:

Tabela Linhas Tamanho
messages 3,27M 2,3 GB
message_embeds ~1M 907 MB
voice_messages 394k 67 MB
demais activity ~150 MB
total ~3,4 GB

Taxa de ingestão real: ~800 msg/dia (~0,01 escrita/s). Ou seja, a dor não é throughput — é massa fria acumulada coabitando com o banco transacional + agregações temporais caras. Some-se a isso a entrada planejada de Instagram e WhatsApp (além de Discord/Twitch).

Decisão

Extrair uma nova aplicação Laravel — ingestion — como ponto único de ingestão de todos os provedores, dona de um datastore TimescaleDB. O app principal vira consumidor do resultado e larga os ~3,4 GB.

Decisão completa, alternativas rejeitadas e consequências: docs/adr/0001-extract-ingestion-context.md (ADR system-wide).

Fronteira

PROVEDORES (ao vivo) ─ Discord WS · Twitch EventSub · Instagram/WhatsApp webhooks
         ▼
┌──────────────────────────────────────────┐
│  INGESTION  (app Laravel separada)         │
│  Raw Landing → Transform → Serving         │
│  chave: (provider, external_account_id)    │
│  TimescaleDB: hypertables + continuous agg │
└───────┬───────────────────────────┬───────┘
  PULL  │ connection read-only        │ PUSH: fila Redis
        │ (agregados / histórico)     │ (ModerationContentDTO)
        ▼                             ▼
   Painéis/Gamification/Portal     Moderation → Identity (soberano)

Decisões travadas

  • 1. Driver = volume acumulado (não throughput).
  • 2. App separada justificada pelo futuro (Instagram/WhatsApp/+).
  • 3. Híbrido: agregados via continuous aggregates; conteúdo cru disparado por evento (não query).
  • 4. Bot runtime + todos os webhooks migram — ingestão é ponto único.
  • 5. Atividade chaveada por (provider, external_account_id, tenant); nova camada cega ao He4rt User; identity permanece soberano (ADR identity/0001).
  • 6. PULL = connection read-only Timescale (contrato = views materializadas); PUSH = fila Redis compartilhada.
  • 7. integration-* se parte: OAuth + enforcement Transport ficam; só Transport(ETL) migra.
  • 8. Raw Landing append-only (replay/reprocessamento sem repedir ao provedor).
  • 9. App principal guarda zero mensagens; texto anonimizado após 24 meses (LGPD).
  • 10. Migração: backfill → dual-write → switch → drop; antigo é a verdade até bater paridade.

Glossário

Termo Definição
Raw Landing Payload cru do provedor (JSONB, append-only), como chega. Permite replay.
Transform Normaliza + resolve conta-de-provedor + enriquece (hoje em ETL/Actions).
Serving O resultado consumido pelo app principal: hypertables normalizadas + continuous aggregates.
Provider Account (provider, external_account_id, tenant) — a chave de ingestão. Cega ao He4rt User.

Termo "data lake" / "lake" aposentado — o store é normalizado, schema-on-write.

Fora de escopo desta issue (detalhe de execução — issues próprias)

  • Modelagem das hypertables, time_bucket das continuous aggregates, COUNT(DISTINCT) aproximado (hyperloglog) para "unique users".
  • Migração das tabelas de referência (discord_guilds/channels/roles) junto com a ingestão.
  • Idempotência do PUSH (dedupe por provider_message_id).
  • Mecânica do dual-write: feature flag, reconciliação de paridade, gatilho de cutover.

Consequências

  • App principal acopla ao schema da Timescale (views materializadas = contrato estável).
  • Reescrever ~7 Query classes do dashboard + ranking: external_identity_idexternal_account_id.
  • Connector Saloon (Discord/Twitch) duplicado entre as camadas.
  • Linhas históricas backfilladas são não-reprocessáveis (sem raw original).

Metadata

Metadata

Assignees

No one assigned

    Labels

    difficulty:epic2+ weeksmod:identityAuth & user identitymod:ingestionCamada de ingestao multi-provedor (ETL + TimescaleDB)mod:moderationModeration pipelinetype:prdProduct Requirements Document

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions