package servers import ( "database/sql" "fmt" "github.com/blang/semver" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/common/log" "sync" ) // Server describes a connection to a Postgresql database, and describes the // metric maps and overrides in-place for it. // Also it contains metrics map and query overrides. type Server struct { db *sql.DB labels prometheus.Labels master bool // Last version used to calculate metric map. If mismatch on scrape, // then maps are recalculated. lastMapVersion semver.Version // Currently active metric map metricMap map[string]MetricMapNamespace // Currently active query overrides queryOverrides map[string]string mappingMtx sync.RWMutex // Currently cached metrics metricCache map[string]cachedMetrics cacheMtx sync.Mutex } // NewServer establishes a new connection using DSN. func NewServer(dsn string, opts ...ServerOpt) (*Server, error) { fingerprint, err := parseFingerprint(dsn) if err != nil { return nil, err } db, err := sql.Open("postgres", dsn) if err != nil { return nil, err } db.SetMaxOpenConns(1) db.SetMaxIdleConns(1) log.Infof("Established new database connection to %q.", fingerprint) s := &Server{ db: db, master: false, labels: prometheus.Labels{ serverLabelName: fingerprint, }, metricCache: make(map[string]cachedMetrics), } for _, opt := range opts { opt(s) } return s, nil } // Close disconnects from Postgres. func (s *Server) Close() error { return s.db.Close() } // Ping checks connection availability and possibly invalidates the connection if it fails. func (s *Server) Ping() error { if err := s.db.Ping(); err != nil { if cerr := s.Close(); cerr != nil { log.Errorf("Error while closing non-pinging DB connection to %q: %v", s, cerr) } return err } return nil } // String returns server's fingerprint. func (s *Server) String() string { return s.labels[serverLabelName] } // Scrape loads metrics. func (s *Server) Scrape(ch chan<- prometheus.Metric, disableSettingsMetrics bool) error { s.mappingMtx.RLock() defer s.mappingMtx.RUnlock() var err error if !disableSettingsMetrics && s.master { if err = querySettings(ch, s); err != nil { err = fmt.Errorf("error retrieving settings: %s", err) } } errMap := queryNamespaceMappings(ch, s) if len(errMap) > 0 { err = fmt.Errorf("queryNamespaceMappings returned %d errors", len(errMap)) } return err } // AddQueries adds queries to the builtinMetricMaps and queryOverrides maps. // Added queries do not respect version requirements, because it is assumed // that the user knows what they are doing with their version of postgres. func (s *Server) AddQueries() { }