func NewBackend( ctx scope.Context, dsn string, c cluster.Cluster, serverDesc *cluster.PeerDesc) (*Backend, error) { var version string if serverDesc == nil { version = "dev" } else { version = serverDesc.Version } parsedDSN, err := url.Parse(dsn) if err == nil { if parsedDSN.User != nil { parsedDSN.User = url.UserPassword(parsedDSN.User.Username(), "xxxxxx") } log.Printf("psql backend %s on %s", version, parsedDSN.String()) } else { return nil, fmt.Errorf("url.Parse: %s", err) } db, err := sql.Open("postgres", dsn) if err != nil { return nil, fmt.Errorf("sql.Open: %s", err) } b := &Backend{ DB: db, dsn: dsn, desc: serverDesc, version: version, cluster: c, peers: map[string]string{}, listeners: map[string]ListenerMap{}, ctx: ctx, } b.logger = log.New(os.Stdout, fmt.Sprintf("[backend %p] ", b), log.LstdFlags) if serverDesc != nil { b.peers[serverDesc.ID] = serverDesc.Era for _, desc := range c.Peers() { b.peers[desc.ID] = desc.Era } } if err := b.start(); err != nil { return nil, err } return b, nil }
func scan(ctx scope.Context, c cluster.Cluster, pb *psql.Backend) error { type PresenceWithUserAgent struct { psql.Presence UserAgent string `db:"user_agent"` } rows, err := pb.DbMap.Select( PresenceWithUserAgent{}, "SELECT p.room, p.session_id, p.server_id, p.server_era, p.updated, p.fact, s.user_agent"+ " FROM presence p, session_log s WHERE p.session_id = s.session_id") if err != nil { return err } peers := map[string]string{} for _, desc := range c.Peers() { peers[desc.ID] = desc.Era } activeRows := 0 activeRowsPerRoom := map[string]int{} activeSessionsPerAgent := map[string]int{} lurkingSessionsPerAgent := map[string]int{} webSessionsPerAgent := map[string]int{} lurkingRows := 0 lurkingRowsPerRoom := map[string]int{} for _, row := range rows { presence, ok := row.(*PresenceWithUserAgent) if !ok { fmt.Printf("error: expected row of type *PresenceWithUserAgent, got %T\n", row) continue } if peers[presence.ServerID] == presence.ServerEra { activeRows++ activeRowsPerRoom[presence.Room]++ parts := strings.Split(presence.SessionID, "-") activeSessionsPerAgent[parts[0]]++ // Check web-client status. // TODO: use positive fingerprint from web client instead of user-agent if presence.UserAgent != "" && !strings.HasPrefix(presence.UserAgent, "Python") { webSessionsPerAgent[parts[0]]++ } // Check lurker status. Currently this is indicated by a blank name on the session. session, err := presence.SessionView(proto.General) if err != nil { fmt.Printf("error: failed to extract session from presence row: %s\n", err) continue } if session.Name == "" { lurkingRows++ lurkingRowsPerRoom[presence.Room]++ lurkingSessionsPerAgent[parts[0]]++ } } } rowCount.Set(float64(len(rows))) activeRowCount.Set(float64(activeRows)) lurkingRowCount.Set(float64(lurkingRows)) uniqueAgentCount.Set(float64(len(activeSessionsPerAgent))) uniqueLurkingAgentCount.Set(float64(len(lurkingSessionsPerAgent))) uniqueWebAgentCount.Set(float64(len(webSessionsPerAgent))) for room, count := range activeRowsPerRoom { activeRowCountPerRoom.With(prometheus.Labels{"room": room}).Set(float64(count)) } for room, count := range lurkingRowsPerRoom { lurkingRowCountPerRoom.With(prometheus.Labels{"room": room}).Set(float64(count)) } for _, count := range activeSessionsPerAgent { sessionsPerAgent.Observe(float64(count)) } return nil }