sqlstore

package
v0.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 31, 2026 License: AGPL-3.0 Imports: 15 Imported by: 0

Documentation

Overview

Package sqlstore implementa store.Store sobre um banco relacional.

O schema foi desenhado para que o mesmo SQL sirva a SQLite e PostgreSQL: tempo em milissegundos inteiros, enums em texto, agregação feita em Go. Sem funções de data específicas de dialeto, não há dois caminhos de código para divergirem.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Store

type Store struct {
	// contains filtered or unexported fields
}

Store é a implementação relacional de store.Store.

func Open

func Open(driver, dsn string) (*Store, error)

Open escolhe a implementação pelo driver configurado.

Existe para o binário não precisar de um switch na inicialização, e para "banco plugável" ter um ponto de entrada só.

func OpenPostgres

func OpenPostgres(dsn string) (*Store, error)

OpenPostgres abre a conexão com PostgreSQL e aplica as migrations.

É o caminho de produção: SQLite dá conta de uma instalação inteira num arquivo, mas não de duas instâncias do UpWatch escrevendo ao mesmo tempo, que é o que se quer quando a disponibilidade do monitorador também importa.

O mesmo SQL serve aos dois bancos. As duas diferenças que o schema não resolve — marcador de parâmetro e descoberta do id gerado — vivem em db.go, na borda, e não espalhadas pelos repositórios.

func OpenSQLite

func OpenSQLite(path string) (*Store, error)

OpenSQLite abre (ou cria) o banco SQLite em path e aplica as migrations.

path pode ser um arquivo ou ":memory:". Pragmas são passados na DSN porque valem por conexão — configurá-los uma vez só depois de abrir deixaria as demais conexões do pool sem eles, e a cascata de chaves estrangeiras silenciosamente desligada.

func (*Store) Announcements

func (s *Store) Announcements() store.AnnouncementRepo

Announcements devolve o repositório dos relatos públicos.

func (*Store) Channels

func (s *Store) Channels() store.ChannelRepo

Channels devolve o repositório de destinos de aviso.

func (*Store) Close

func (s *Store) Close() error

Close encerra a conexão.

func (*Store) Compact

func (s *Store) Compact(ctx context.Context) error

Compact devolve ao sistema de arquivos o espaço liberado pela poda.

Apagar linhas não encolhe o banco por si só: as páginas ficam livres para reuso e o arquivo mantém o pico histórico. Sem esta etapa o UpWatch pararia de crescer sem nunca devolver espaço, que é a queixa por trás do botão manual de VACUUM do Uptime Kuma.

Usa vacuum incremental em vez do completo: o completo reescreve o banco inteiro e o mantém travado durante todo o processo, enquanto o incremental devolve páginas aos poucos sem interromper o monitoramento. O checkpoint anterior é necessário porque, em WAL, as páginas liberadas ficam no diário até serem consolidadas.

func (*Store) DB

func (s *Store) DB() *sql.DB

DB expõe a conexão para inspeção em teste (planos de consulta, contagens).

func (*Store) Incidents

func (s *Store) Incidents() store.IncidentRepo

Incidents devolve o repositório de janelas de indisponibilidade.

func (*Store) LastPush

func (s *Store) LastPush(ctx context.Context, monitorID int64) (time.Time, bool, error)

LastPush devolve o instante do último sinal recebido.

func (*Store) LatestHeartbeats

func (s *Store) LatestHeartbeats(ctx context.Context) (map[int64]domain.Heartbeat, error)

LatestHeartbeats devolve a batida mais recente de cada monitor.

Uma consulta só, com subconsulta correlacionada que usa o índice composto (monitor_id, ts). A alternativa — uma consulta por monitor — faria a exposição de métricas custar N leituras a cada raspagem, e a métrica viraria a maior fonte de carga do banco que ela observa.

func (*Store) Monitors

func (s *Store) Monitors() store.MonitorRepo

Monitors devolve o repositório de definições de monitor.

func (*Store) OldestHeartbeat

func (s *Store) OldestHeartbeat(ctx context.Context) (time.Time, bool, error)

OldestHeartbeat devolve o instante da batida mais antiga preservada.

Usa o índice sobre ts, o mesmo que serve à poda, então o custo é o de ler a primeira entrada da árvore e não o de varrer a tabela.

func (*Store) PruneHeartbeats

func (s *Store) PruneHeartbeats(ctx context.Context, before time.Time) (int64, error)

PruneHeartbeats apaga batidas estritamente anteriores a before.

O corte é exclusivo para que a batida exatamente na fronteira sobreviva, evitando perder a primeira amostra do período que ainda será agregado.

func (*Store) PruneRollups

func (s *Store) PruneRollups(ctx context.Context, res domain.Resolution, before time.Time) (int64, error)

PruneRollups apaga agregados de uma resolução anteriores a before.

A resolução é obrigatória porque cada camada tem retenção própria: podar a horária não pode levar junto a diária, que sustenta o gráfico de meses.

func (*Store) QueryHeartbeats

func (s *Store) QueryHeartbeats(ctx context.Context, q store.HeartbeatQuery) ([]domain.Heartbeat, error)

QueryHeartbeats devolve batidas da janela em ordem cronológica.

A ordenação é garantida porque a agregação percorre a janela em sequência; deixá-la a cargo do plano de execução tornaria os percentis dependentes do banco.

func (*Store) QueryRollups

func (s *Store) QueryRollups(ctx context.Context, q store.RollupQuery) ([]domain.Rollup, error)

QueryRollups devolve agregados da janela em ordem cronológica.

func (*Store) RecordPush

func (s *Store) RecordPush(ctx context.Context, monitorID int64, at time.Time) error

RecordPush registra o sinal recebido de um monitor push.

Sobrescreve em vez de acumular: um cron que bate a cada minuto criaria milhares de linhas por dia sem que nenhuma delas fosse útil depois da seguinte.

func (*Store) Rollback

func (s *Store) Rollback() error

Rollback desfaz a última migration. Existe para que o teste consiga verificar que o schema desce, não só sobe.

func (*Store) RollbackAll

func (s *Store) RollbackAll() error

RollbackAll desfaz todas as migrations, na ordem inversa.

É o que exercita o Down de cada uma. Verificar só a última deixaria passar uma migration antiga sem reversão, e o defeito só apareceria no dia em que alguém precisasse voltar uma versão — que é justamente o pior dia para descobrir.

func (*Store) RollupWatermark

func (s *Store) RollupWatermark(ctx context.Context, res domain.Resolution) (time.Time, error)

RollupWatermark devolve o último bucket já agregado na resolução. Devolve o tempo zero quando nada foi processado ainda.

func (*Store) Sessions

func (s *Store) Sessions() store.SessionRepo

Sessions devolve o repositório de logins ativos.

func (*Store) SetRollupWatermark

func (s *Store) SetRollupWatermark(ctx context.Context, res domain.Resolution, bucket time.Time) error

SetRollupWatermark avança a marca d'água da resolução.

func (*Store) States

func (s *Store) States() store.StateRepo

States devolve o repositório do estado confirmado dos monitores.

func (*Store) StatusPages

func (s *Store) StatusPages() store.StatusPageRepo

StatusPages devolve o repositório das páginas públicas.

func (*Store) StreamHeartbeats

func (s *Store) StreamHeartbeats(
	ctx context.Context,
	monitorID int64,
	r store.TimeRange,
	fn func(domain.Heartbeat) error,
) error

StreamHeartbeats entrega todas as batidas da janela, sem teto.

Caminho da agregação, separado do da API de propósito: um bucket diário com check de um segundo tem 86.400 batidas, e passar pelo limite de paginação truncaria a amostra em silêncio, produzindo percentis que não descrevem período nenhum.

As linhas são consumidas uma a uma em vez de materializadas numa fatia, para o pico de memória não acompanhar o tamanho do bucket.

func (*Store) Tokens

func (s *Store) Tokens() store.TokenRepo

Tokens devolve o repositório de credenciais programáticas.

func (*Store) Users

func (s *Store) Users() store.UserRepo

Users devolve o repositório de contas de acesso.

func (*Store) WriteHeartbeats

func (s *Store) WriteHeartbeats(ctx context.Context, hbs []domain.Heartbeat) error

WriteHeartbeats grava o lote numa única transação.

Uma transação por lote em vez de uma por batida é a decisão de desempenho central do projeto: com mil monitores a cada 30 segundos são ~33 escritas por segundo, que viram cerca de uma transação por segundo. Gravar individualmente pagaria um fsync por batida e travaria o SQLite.

func (*Store) WriteRollups

func (s *Store) WriteRollups(ctx context.Context, rs []domain.Rollup) error

WriteRollups grava agregados de forma idempotente.

Reescrever um bucket substitui a linha em vez de criar outra: uma reexecução após falha do worker precisa ser segura, e sem isso os contadores inflariam a cada tentativa.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL