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 ¶
- type Store
- func (s *Store) Announcements() store.AnnouncementRepo
- func (s *Store) Channels() store.ChannelRepo
- func (s *Store) Close() error
- func (s *Store) Compact(ctx context.Context) error
- func (s *Store) DB() *sql.DB
- func (s *Store) Incidents() store.IncidentRepo
- func (s *Store) LastPush(ctx context.Context, monitorID int64) (time.Time, bool, error)
- func (s *Store) LatestHeartbeats(ctx context.Context) (map[int64]domain.Heartbeat, error)
- func (s *Store) Monitors() store.MonitorRepo
- func (s *Store) OldestHeartbeat(ctx context.Context) (time.Time, bool, error)
- func (s *Store) PruneHeartbeats(ctx context.Context, before time.Time) (int64, error)
- func (s *Store) PruneRollups(ctx context.Context, res domain.Resolution, before time.Time) (int64, error)
- func (s *Store) QueryHeartbeats(ctx context.Context, q store.HeartbeatQuery) ([]domain.Heartbeat, error)
- func (s *Store) QueryRollups(ctx context.Context, q store.RollupQuery) ([]domain.Rollup, error)
- func (s *Store) RecordPush(ctx context.Context, monitorID int64, at time.Time) error
- func (s *Store) Rollback() error
- func (s *Store) RollbackAll() error
- func (s *Store) RollupWatermark(ctx context.Context, res domain.Resolution) (time.Time, error)
- func (s *Store) Sessions() store.SessionRepo
- func (s *Store) SetRollupWatermark(ctx context.Context, res domain.Resolution, bucket time.Time) error
- func (s *Store) States() store.StateRepo
- func (s *Store) StatusPages() store.StatusPageRepo
- func (s *Store) StreamHeartbeats(ctx context.Context, monitorID int64, r store.TimeRange, ...) error
- func (s *Store) Tokens() store.TokenRepo
- func (s *Store) Users() store.UserRepo
- func (s *Store) WriteHeartbeats(ctx context.Context, hbs []domain.Heartbeat) error
- func (s *Store) WriteRollups(ctx context.Context, rs []domain.Rollup) error
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 ¶
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 ¶
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 ¶
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) Compact ¶
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) Incidents ¶
func (s *Store) Incidents() store.IncidentRepo
Incidents devolve o repositório de janelas de indisponibilidade.
func (*Store) LatestHeartbeats ¶
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 ¶
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 ¶
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 ¶
QueryRollups devolve agregados da janela em ordem cronológica.
func (*Store) RecordPush ¶
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 ¶
Rollback desfaz a última migration. Existe para que o teste consiga verificar que o schema desce, não só sobe.
func (*Store) RollbackAll ¶
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 ¶
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) 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) WriteHeartbeats ¶
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 ¶
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.