diff --git a/userapi/storage/interface.go b/userapi/storage/interface.go index 36ffd2a56..d366ff849 100644 --- a/userapi/storage/interface.go +++ b/userapi/storage/interface.go @@ -22,6 +22,7 @@ import ( "github.com/matrix-org/dendrite/clientapi/auth/authtypes" "github.com/matrix-org/dendrite/userapi/api" "github.com/matrix-org/dendrite/userapi/storage/tables" + "github.com/matrix-org/dendrite/userapi/types" ) type Profile interface { @@ -34,6 +35,7 @@ type Profile interface { type Database interface { Profile + Statistics GetAccountByPassword(ctx context.Context, localpart, plaintextPassword string) (*api.Account, error) // CreateAccount makes a new account with the given login name and password, and creates an empty profile // for this account. If no password is supplied, the account will be a passwordless account. If the @@ -107,14 +109,10 @@ type Database interface { GetPushers(ctx context.Context, localpart string) ([]api.Pusher, error) RemovePusher(ctx context.Context, appid, pushkey, localpart string) error RemovePushers(ctx context.Context, appid, pushkey string) error +} - AllUsers(ctx context.Context) (result int64, err error) - NonBridgedUsers(ctx context.Context) (result int64, err error) - RegisteredUserByType(ctx context.Context) (map[string]int64, error) - DailyUsers(ctx context.Context) (result int64, err error) - MonthlyUsers(ctx context.Context) (result int64, err error) - R30Users(ctx context.Context) (map[string]int64, error) - R30UsersV2(ctx context.Context) (map[string]int64, error) +type Statistics interface { + UserStatistics(ctx context.Context) (*types.UserStatistics, *types.DatabaseEngine, error) } // Err3PIDInUse is the error returned when trying to save an association involving diff --git a/userapi/storage/postgres/stats_table.go b/userapi/storage/postgres/stats_table.go index d4e96edcc..5fec65fb2 100644 --- a/userapi/storage/postgres/stats_table.go +++ b/userapi/storage/postgres/stats_table.go @@ -21,9 +21,9 @@ import ( "github.com/lib/pq" "github.com/matrix-org/dendrite/internal" - "github.com/matrix-org/dendrite/internal/sqlutil" "github.com/matrix-org/dendrite/userapi/storage/tables" + "github.com/matrix-org/dendrite/userapi/types" "github.com/matrix-org/gomatrixserverlib" "github.com/sirupsen/logrus" ) @@ -48,6 +48,12 @@ const countUsersLastSeenAfterSQL = "" + " GROUP BY localpart" + " ) u" +/* +R30Users counts the number of 30 day retained users, defined as: +- Users who have created their accounts more than 30 days ago +- Where last seen at most 30 days ago +- Where account creation and last_seen are > 30 days apart +*/ const countR30UsersSQL = ` SELECT platform, COUNT(*) FROM ( SELECT users.localpart, platform, users.created_ts, MAX(uip.last_seen_ts) @@ -75,6 +81,11 @@ SELECT platform, COUNT(*) FROM ( ) u GROUP BY PLATFORM ` +/* +R30UsersV2 counts the number of 30 day retained users, defined as users that: +- Appear more than once in the past 60 days +- Have more than 30 days between the most and least recent appearances that occurred in the past 60 days. +*/ const countR30UsersV2SQL = ` SELECT client_type, @@ -140,6 +151,8 @@ ON CONFLICT (localpart, device_id, timestamp) DO NOTHING ; ` +const queryDBEngineVersion = "SHOW server_version;" + type statsStatements struct { serverName gomatrixserverlib.ServerName lastUpdate time.Time @@ -149,6 +162,7 @@ type statsStatements struct { updateUserDailyVisitsStmt *sql.Stmt countUserByAccountTypeStmt *sql.Stmt countRegisteredUserByTypeStmt *sql.Stmt + dbEngineVersionStmt *sql.Stmt } func NewPostgresStatsTable(db *sql.DB, serverName gomatrixserverlib.ServerName) (tables.StatsTable, error) { @@ -169,6 +183,7 @@ func NewPostgresStatsTable(db *sql.DB, serverName gomatrixserverlib.ServerName) {&s.updateUserDailyVisitsStmt, updateUserDailyVisitsSQL}, {&s.countUserByAccountTypeStmt, countUserByAccountTypeSQL}, {&s.countRegisteredUserByTypeStmt, countRegisteredUserByTypeStmt}, + {&s.dbEngineVersionStmt, queryDBEngineVersion}, }.Prepare(db) } @@ -184,7 +199,7 @@ func (s *statsStatements) startTimers() { time.AfterFunc(time.Minute*5, updateStatsFunc) } -func (s *statsStatements) AllUsers(ctx context.Context, txn *sql.Tx) (result int64, err error) { +func (s *statsStatements) allUsers(ctx context.Context, txn *sql.Tx) (result int64, err error) { stmt := sqlutil.TxStmt(txn, s.countUserByAccountTypeStmt) err = stmt.QueryRowContext(ctx, pq.Int64Array{1, 2, 3, 4}, @@ -192,7 +207,7 @@ func (s *statsStatements) AllUsers(ctx context.Context, txn *sql.Tx) (result int return } -func (s *statsStatements) NonBridgedUsers(ctx context.Context, txn *sql.Tx) (result int64, err error) { +func (s *statsStatements) nonBridgedUsers(ctx context.Context, txn *sql.Tx) (result int64, err error) { stmt := sqlutil.TxStmt(txn, s.countUserByAccountTypeStmt) err = stmt.QueryRowContext(ctx, pq.Int64Array{1, 2, 3}, @@ -200,7 +215,7 @@ func (s *statsStatements) NonBridgedUsers(ctx context.Context, txn *sql.Tx) (res return } -func (s *statsStatements) RegisteredUserByType(ctx context.Context, txn *sql.Tx) (map[string]int64, error) { +func (s *statsStatements) registeredUserByType(ctx context.Context, txn *sql.Tx) (map[string]int64, error) { stmt := sqlutil.TxStmt(txn, s.countRegisteredUserByTypeStmt) registeredAfter := time.Now().AddDate(0, 0, -1) @@ -225,7 +240,7 @@ func (s *statsStatements) RegisteredUserByType(ctx context.Context, txn *sql.Tx) return result, rows.Err() } -func (s *statsStatements) DailyUsers(ctx context.Context, txn *sql.Tx) (result int64, err error) { +func (s *statsStatements) dailyUsers(ctx context.Context, txn *sql.Tx) (result int64, err error) { stmt := sqlutil.TxStmt(txn, s.countUsersLastSeenAfterStmt) lastSeenAfter := time.Now().AddDate(0, 0, -1) err = stmt.QueryRowContext(ctx, @@ -234,7 +249,7 @@ func (s *statsStatements) DailyUsers(ctx context.Context, txn *sql.Tx) (result i return } -func (s *statsStatements) MonthlyUsers(ctx context.Context, txn *sql.Tx) (result int64, err error) { +func (s *statsStatements) monthlyUsers(ctx context.Context, txn *sql.Tx) (result int64, err error) { stmt := sqlutil.TxStmt(txn, s.countUsersLastSeenAfterStmt) lastSeenAfter := time.Now().AddDate(0, 0, -30) err = stmt.QueryRowContext(ctx, @@ -243,12 +258,13 @@ func (s *statsStatements) MonthlyUsers(ctx context.Context, txn *sql.Tx) (result return } -/* R30Users counts the number of 30 day retained users, defined as: +/* +R30Users counts the number of 30 day retained users, defined as: - Users who have created their accounts more than 30 days ago - Where last seen at most 30 days ago - Where account creation and last_seen are > 30 days apart */ -func (s *statsStatements) R30Users(ctx context.Context, txn *sql.Tx) (map[string]int64, error) { +func (s *statsStatements) r30Users(ctx context.Context, txn *sql.Tx) (map[string]int64, error) { stmt := sqlutil.TxStmt(txn, s.countR30UsersStmt) lastSeenAfter := time.Now().AddDate(0, 0, -30) diff := time.Hour * 24 * 30 @@ -279,11 +295,12 @@ func (s *statsStatements) R30Users(ctx context.Context, txn *sql.Tx) (map[string return result, rows.Err() } -/* R30UsersV2 counts the number of 30 day retained users, defined as users that: +/* +R30UsersV2 counts the number of 30 day retained users, defined as users that: - Appear more than once in the past 60 days - Have more than 30 days between the most and least recent appearances that occurred in the past 60 days. */ -func (s *statsStatements) R30UsersV2(ctx context.Context, txn *sql.Tx) (map[string]int64, error) { +func (s *statsStatements) r30UsersV2(ctx context.Context, txn *sql.Tx) (map[string]int64, error) { stmt := sqlutil.TxStmt(txn, s.countR30UsersV2Stmt) sixtyDaysAgo := time.Now().AddDate(0, 0, -60) thirtyDaysAgo := time.Now().AddDate(0, 0, -30) @@ -322,6 +339,59 @@ func (s *statsStatements) R30UsersV2(ctx context.Context, txn *sql.Tx) (map[stri return result, rows.Err() } +// UserStatistics collects some information about users on this instance. +// Returns the stats itself as well as the database engine version and type. +// On error, returns the stats collected up to the error. +func (s *statsStatements) UserStatistics(ctx context.Context, txn *sql.Tx) (*types.UserStatistics, *types.DatabaseEngine, error) { + var ( + stats = &types.UserStatistics{ + R30UsersV2: map[string]int64{ + "ios": 0, + "android": 0, + "web": 0, + "electron": 0, + "all": 0, + }, + R30Users: map[string]int64{}, + RegisteredUsersByType: map[string]int64{}, + } + dbEngine = &types.DatabaseEngine{Engine: "Postgres", Version: "unknown"} + err error + ) + stats.AllUsers, err = s.allUsers(ctx, txn) + if err != nil { + return stats, dbEngine, err + } + stats.DailyUsers, err = s.dailyUsers(ctx, txn) + if err != nil { + return stats, dbEngine, err + } + stats.MonthlyUsers, err = s.monthlyUsers(ctx, txn) + if err != nil { + return stats, dbEngine, err + } + stats.R30Users, err = s.r30Users(ctx, txn) + if err != nil { + return stats, dbEngine, err + } + stats.R30UsersV2, err = s.r30UsersV2(ctx, txn) + if err != nil { + return stats, dbEngine, err + } + stats.NonBridgedUsers, err = s.nonBridgedUsers(ctx, txn) + if err != nil { + return stats, dbEngine, err + } + stats.RegisteredUsersByType, err = s.registeredUserByType(ctx, txn) + if err != nil { + return stats, dbEngine, err + } + + stmt := sqlutil.TxStmt(txn, s.dbEngineVersionStmt) + err = stmt.QueryRowContext(ctx).Scan(&dbEngine.Version) + return stats, dbEngine, err +} + func (s *statsStatements) updateUserDailyVisits(ctx context.Context, txn *sql.Tx) error { stmt := sqlutil.TxStmt(txn, s.updateUserDailyVisitsStmt) _ = stmt diff --git a/userapi/storage/shared/storage.go b/userapi/storage/shared/storage.go index fced3445b..a8e94f3c3 100644 --- a/userapi/storage/shared/storage.go +++ b/userapi/storage/shared/storage.go @@ -26,6 +26,7 @@ import ( "strings" "time" + "github.com/matrix-org/dendrite/userapi/types" "github.com/matrix-org/gomatrixserverlib" "golang.org/x/crypto/bcrypt" @@ -773,24 +774,7 @@ func (d *Database) RemovePushers( }) } -func (d *Database) AllUsers(ctx context.Context) (result int64, err error) { - return d.Stats.AllUsers(ctx, nil) -} -func (d *Database) NonBridgedUsers(ctx context.Context) (result int64, err error) { - return d.Stats.NonBridgedUsers(ctx, nil) -} -func (d *Database) RegisteredUserByType(ctx context.Context) (map[string]int64, error) { - return d.Stats.RegisteredUserByType(ctx, nil) -} -func (d *Database) DailyUsers(ctx context.Context) (result int64, err error) { - return d.Stats.DailyUsers(ctx, nil) -} -func (d *Database) MonthlyUsers(ctx context.Context) (result int64, err error) { - return d.Stats.MonthlyUsers(ctx, nil) -} -func (d *Database) R30Users(ctx context.Context) (map[string]int64, error) { - return d.Stats.R30Users(ctx, nil) -} -func (d *Database) R30UsersV2(ctx context.Context) (map[string]int64, error) { - return d.Stats.R30UsersV2(ctx, nil) +// UserStatistics populates types.UserStatistics, used in reports. +func (d *Database) UserStatistics(ctx context.Context) (*types.UserStatistics, *types.DatabaseEngine, error) { + return d.Stats.UserStatistics(ctx, nil) } diff --git a/userapi/storage/sqlite3/stats_table.go b/userapi/storage/sqlite3/stats_table.go index 4e2790813..688f099b7 100644 --- a/userapi/storage/sqlite3/stats_table.go +++ b/userapi/storage/sqlite3/stats_table.go @@ -23,6 +23,7 @@ import ( "github.com/matrix-org/dendrite/internal" "github.com/matrix-org/dendrite/internal/sqlutil" "github.com/matrix-org/dendrite/userapi/storage/tables" + "github.com/matrix-org/dendrite/userapi/types" "github.com/matrix-org/gomatrixserverlib" "github.com/sirupsen/logrus" ) @@ -47,6 +48,12 @@ const countUsersLastSeenAfterSQL = "" + " GROUP BY localpart" + " ) u" +/* +R30Users counts the number of 30 day retained users, defined as: +- Users who have created their accounts more than 30 days ago +- Where last seen at most 30 days ago +- Where account creation and last_seen are > 30 days apart +*/ const countR30UsersSQL = ` SELECT platform, COUNT(*) FROM ( SELECT users.localpart, platform, users.created_ts, MAX(uip.last_seen_ts) @@ -74,6 +81,11 @@ SELECT platform, COUNT(*) FROM ( ) u GROUP BY PLATFORM ` +/* +R30UsersV2 counts the number of 30 day retained users, defined as users that: +- Appear more than once in the past 60 days +- Have more than 30 days between the most and least recent appearances that occurred in the past 60 days. +*/ const countR30UsersV2SQL = ` SELECT client_type, @@ -139,6 +151,8 @@ ON CONFLICT (localpart, device_id, timestamp) DO NOTHING ; ` +const queryDBEngineVersion = "select sqlite_version();" + type statsStatements struct { serverName gomatrixserverlib.ServerName db *sql.DB @@ -149,6 +163,7 @@ type statsStatements struct { updateUserDailyVisitsStmt *sql.Stmt countUserByAccountTypeStmt *sql.Stmt countRegisteredUserByTypeStmt *sql.Stmt + dbEngineVersionStmt *sql.Stmt } func NewSQLiteStatsTable(db *sql.DB, serverName gomatrixserverlib.ServerName) (tables.StatsTable, error) { @@ -170,6 +185,7 @@ func NewSQLiteStatsTable(db *sql.DB, serverName gomatrixserverlib.ServerName) (t {&s.updateUserDailyVisitsStmt, updateUserDailyVisitsSQL}, {&s.countUserByAccountTypeStmt, countUserByAccountTypeSQL}, {&s.countRegisteredUserByTypeStmt, countRegisteredUserByTypeStmt}, + {&s.dbEngineVersionStmt, queryDBEngineVersion}, }.Prepare(db) } @@ -185,7 +201,7 @@ func (s *statsStatements) startTimers() { time.AfterFunc(time.Minute*5, updateStatsFunc) } -func (s *statsStatements) AllUsers(ctx context.Context, txn *sql.Tx) (result int64, err error) { +func (s *statsStatements) allUsers(ctx context.Context, txn *sql.Tx) (result int64, err error) { query := strings.Replace(countUserByAccountTypeSQL, "($1)", sqlutil.QueryVariadic(4), 1) queryStmt, err := s.db.Prepare(query) if err != nil { @@ -198,7 +214,7 @@ func (s *statsStatements) AllUsers(ctx context.Context, txn *sql.Tx) (result int return } -func (s *statsStatements) NonBridgedUsers(ctx context.Context, txn *sql.Tx) (result int64, err error) { +func (s *statsStatements) nonBridgedUsers(ctx context.Context, txn *sql.Tx) (result int64, err error) { query := strings.Replace(countUserByAccountTypeSQL, "($1)", sqlutil.QueryVariadic(3), 1) queryStmt, err := s.db.Prepare(query) if err != nil { @@ -211,7 +227,7 @@ func (s *statsStatements) NonBridgedUsers(ctx context.Context, txn *sql.Tx) (res return } -func (s *statsStatements) RegisteredUserByType(ctx context.Context, txn *sql.Tx) (map[string]int64, error) { +func (s *statsStatements) registeredUserByType(ctx context.Context, txn *sql.Tx) (map[string]int64, error) { stmt := sqlutil.TxStmt(txn, s.countRegisteredUserByTypeStmt) registeredAfter := time.Now().AddDate(0, 0, -1) @@ -236,7 +252,7 @@ func (s *statsStatements) RegisteredUserByType(ctx context.Context, txn *sql.Tx) return result, rows.Err() } -func (s *statsStatements) DailyUsers(ctx context.Context, txn *sql.Tx) (result int64, err error) { +func (s *statsStatements) dailyUsers(ctx context.Context, txn *sql.Tx) (result int64, err error) { stmt := sqlutil.TxStmt(txn, s.countUsersLastSeenAfterStmt) lastSeenAfter := time.Now().AddDate(0, 0, -1) err = stmt.QueryRowContext(ctx, @@ -245,7 +261,7 @@ func (s *statsStatements) DailyUsers(ctx context.Context, txn *sql.Tx) (result i return } -func (s *statsStatements) MonthlyUsers(ctx context.Context, txn *sql.Tx) (result int64, err error) { +func (s *statsStatements) monthlyUsers(ctx context.Context, txn *sql.Tx) (result int64, err error) { stmt := sqlutil.TxStmt(txn, s.countUsersLastSeenAfterStmt) lastSeenAfter := time.Now().AddDate(0, 0, -30) err = stmt.QueryRowContext(ctx, @@ -259,7 +275,7 @@ func (s *statsStatements) MonthlyUsers(ctx context.Context, txn *sql.Tx) (result - Where last seen at most 30 days ago - Where account creation and last_seen are > 30 days apart */ -func (s *statsStatements) R30Users(ctx context.Context, txn *sql.Tx) (map[string]int64, error) { +func (s *statsStatements) r30Users(ctx context.Context, txn *sql.Tx) (map[string]int64, error) { stmt := sqlutil.TxStmt(txn, s.countR30UsersStmt) lastSeenAfter := time.Now().AddDate(0, 0, -30) diff := time.Hour * 24 * 30 @@ -295,7 +311,7 @@ func (s *statsStatements) R30Users(ctx context.Context, txn *sql.Tx) (map[string - Appear more than once in the past 60 days - Have more than 30 days between the most and least recent appearances that occurred in the past 60 days. */ -func (s *statsStatements) R30UsersV2(ctx context.Context, txn *sql.Tx) (map[string]int64, error) { +func (s *statsStatements) r30UsersV2(ctx context.Context, txn *sql.Tx) (map[string]int64, error) { stmt := sqlutil.TxStmt(txn, s.countR30UsersV2Stmt) sixtyDaysAgo := time.Now().AddDate(0, 0, -60) thirtyDaysAgo := time.Now().AddDate(0, 0, -30) @@ -334,6 +350,59 @@ func (s *statsStatements) R30UsersV2(ctx context.Context, txn *sql.Tx) (map[stri return result, rows.Err() } +// UserStatistics collects some information about users on this instance. +// Returns the stats itself as well as the database engine version and type. +// On error, returns the stats collected up to the error. +func (s *statsStatements) UserStatistics(ctx context.Context, txn *sql.Tx) (*types.UserStatistics, *types.DatabaseEngine, error) { + var ( + stats = &types.UserStatistics{ + R30UsersV2: map[string]int64{ + "ios": 0, + "android": 0, + "web": 0, + "electron": 0, + "all": 0, + }, + R30Users: map[string]int64{}, + RegisteredUsersByType: map[string]int64{}, + } + dbEngine = &types.DatabaseEngine{Engine: "SQLite", Version: "unknown"} + err error + ) + stats.AllUsers, err = s.allUsers(ctx, txn) + if err != nil { + return stats, dbEngine, err + } + stats.DailyUsers, err = s.dailyUsers(ctx, txn) + if err != nil { + return stats, dbEngine, err + } + stats.MonthlyUsers, err = s.monthlyUsers(ctx, txn) + if err != nil { + return stats, dbEngine, err + } + stats.R30Users, err = s.r30Users(ctx, txn) + if err != nil { + return stats, dbEngine, err + } + stats.R30UsersV2, err = s.r30UsersV2(ctx, txn) + if err != nil { + return stats, dbEngine, err + } + stats.NonBridgedUsers, err = s.nonBridgedUsers(ctx, txn) + if err != nil { + return stats, dbEngine, err + } + stats.RegisteredUsersByType, err = s.registeredUserByType(ctx, txn) + if err != nil { + return stats, dbEngine, err + } + + stmt := sqlutil.TxStmt(txn, s.dbEngineVersionStmt) + err = stmt.QueryRowContext(ctx).Scan(&dbEngine.Version) + return stats, dbEngine, err +} + func (s *statsStatements) updateUserDailyVisits(ctx context.Context, txn *sql.Tx) error { stmt := sqlutil.TxStmt(txn, s.updateUserDailyVisitsStmt) _ = stmt diff --git a/userapi/storage/tables/interface.go b/userapi/storage/tables/interface.go index 79538722c..92db4ebad 100644 --- a/userapi/storage/tables/interface.go +++ b/userapi/storage/tables/interface.go @@ -21,6 +21,7 @@ import ( "github.com/matrix-org/dendrite/clientapi/auth/authtypes" "github.com/matrix-org/dendrite/userapi/api" + "github.com/matrix-org/dendrite/userapi/types" "github.com/matrix-org/gomatrixserverlib" ) @@ -113,13 +114,7 @@ type NotificationTable interface { } type StatsTable interface { - AllUsers(ctx context.Context, txn *sql.Tx) (result int64, err error) - NonBridgedUsers(ctx context.Context, txn *sql.Tx) (result int64, err error) - RegisteredUserByType(ctx context.Context, txn *sql.Tx) (map[string]int64, error) - DailyUsers(ctx context.Context, txn *sql.Tx) (result int64, err error) - MonthlyUsers(ctx context.Context, txn *sql.Tx) (result int64, err error) - R30Users(ctx context.Context, txn *sql.Tx) (map[string]int64, error) - R30UsersV2(ctx context.Context, txn *sql.Tx) (map[string]int64, error) + UserStatistics(ctx context.Context, txn *sql.Tx) (*types.UserStatistics, *types.DatabaseEngine, error) } type NotificationFilter uint32 diff --git a/userapi/types/statistics.go b/userapi/types/statistics.go new file mode 100644 index 000000000..09564f78f --- /dev/null +++ b/userapi/types/statistics.go @@ -0,0 +1,30 @@ +// Copyright 2022 The Matrix.org Foundation C.I.C. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +type UserStatistics struct { + RegisteredUsersByType map[string]int64 + R30Users map[string]int64 + R30UsersV2 map[string]int64 + AllUsers int64 + NonBridgedUsers int64 + DailyUsers int64 + MonthlyUsers int64 +} + +type DatabaseEngine struct { + Engine string + Version string +} diff --git a/userapi/util/phonehomestats.go b/userapi/util/phonehomestats.go index cd72bcc77..e6b440aa9 100644 --- a/userapi/util/phonehomestats.go +++ b/userapi/util/phonehomestats.go @@ -25,7 +25,6 @@ import ( "time" "github.com/matrix-org/dendrite/internal" - "github.com/matrix-org/dendrite/internal/sqlutil" "github.com/matrix-org/dendrite/setup/config" "github.com/matrix-org/dendrite/userapi/storage" "github.com/matrix-org/gomatrixserverlib" @@ -38,7 +37,7 @@ type phoneHomeStats struct { serverName gomatrixserverlib.ServerName startTime time.Time cfg *config.Dendrite - db storage.Database + db storage.Statistics isMonolith bool client *http.Client } @@ -48,13 +47,13 @@ type timestampToRUUsage struct { usage syscall.Rusage } -func StartPhoneHomeCollector(startTime time.Time, cfg *config.Dendrite, userDB storage.Database) { +func StartPhoneHomeCollector(startTime time.Time, cfg *config.Dendrite, statsDB storage.Statistics) { p := phoneHomeStats{ startTime: startTime, serverName: cfg.Global.ServerName, cfg: cfg, - db: userDB, + db: statsDB, isMonolith: cfg.IsMonolith, client: &http.Client{ Timeout: time.Second * 30, @@ -91,8 +90,7 @@ func (p *phoneHomeStats) collect() { // cpu and memory usage information err := getMemoryStats(p) if err != nil { - logrus.WithError(err).Error("unable to get memory/cpu stats") - return + logrus.WithError(err).Error("unable to get memory/cpu stats, using defaults") } // configuration information @@ -109,35 +107,6 @@ func (p *phoneHomeStats) collect() { p.stats["log_level"] = "info" } - // database configuration - db, err := sqlutil.Open(&p.cfg.UserAPI.AccountDatabase) - if err != nil { - logrus.WithError(err).Error("unable to connect to database") - return - } - defer internal.CloseAndLogIfError(context.Background(), db, "phoneHomeStats.collect(): failed to close database connection") - - dbVersion := "unknown" - dbEngine := "unknown" - switch { - case p.cfg.UserAPI.AccountDatabase.ConnectionString.IsSQLite(): - dbEngine = "SQLite" - row := db.QueryRow("select sqlite_version();") - if err = row.Scan(&dbVersion); err != nil { - logrus.WithError(err).Error("unable to query version") - return - } - case p.cfg.UserAPI.AccountDatabase.ConnectionString.IsPostgres(): - dbEngine = "Postgres" - row := db.QueryRow("SHOW server_version;") - if err = row.Scan(&dbVersion); err != nil { - logrus.WithError(err).Error("unable to query version") - return - } - } - p.stats["database_engine"] = dbEngine - p.stats["database_server_version"] = dbVersion - // message and room stats // TODO: Find a solution to actually set these values p.stats["total_room_count"] = 0 @@ -146,57 +115,24 @@ func (p *phoneHomeStats) collect() { p.stats["daily_e2ee_messages"] = 0 p.stats["daily_sent_e2ee_messages"] = 0 - count, err := p.db.AllUsers(ctx) + // user stats and DB engine + userStats, db, err := p.db.UserStatistics(ctx) if err != nil { - logrus.WithError(err).Error("unable to query AllUsers") - return + logrus.WithError(err).Error("unable to query userstats, using default values") } - p.stats["total_users"] = count - - count, err = p.db.NonBridgedUsers(ctx) - if err != nil { - logrus.WithError(err).Error("unable to query NonBridgedUsers") - return - } - p.stats["total_nonbridged_users"] = count - - count, err = p.db.DailyUsers(ctx) - if err != nil { - logrus.WithError(err).Error("unable to query DailyUsers") - return - } - p.stats["daily_active_users"] = count - - count, err = p.db.MonthlyUsers(ctx) - if err != nil { - logrus.WithError(err).Error("unable to query MonthlyUsers") - return - } - p.stats["monthly_active_users"] = count - - res, err := p.db.RegisteredUserByType(ctx) - if err != nil { - logrus.WithError(err).Error("unable to query RegisteredUserByType") - return - } - for t, c := range res { + p.stats["database_engine"] = db.Engine + p.stats["database_server_version"] = db.Version + p.stats["total_users"] = userStats.AllUsers + p.stats["total_nonbridged_users"] = userStats.NonBridgedUsers + p.stats["daily_active_users"] = userStats.DailyUsers + p.stats["monthly_active_users"] = userStats.MonthlyUsers + for t, c := range userStats.RegisteredUsersByType { p.stats["daily_user_type_"+t] = c } - - res, err = p.db.R30Users(ctx) - if err != nil { - logrus.WithError(err).Error("unable to query R30Users") - return - } - for t, c := range res { + for t, c := range userStats.R30Users { p.stats["r30_users_"+t] = c } - res, err = p.db.R30UsersV2(ctx) - if err != nil { - logrus.WithError(err).Error("unable to query R30UsersV2") - return - } - for t, c := range res { + for t, c := range userStats.R30UsersV2 { p.stats["r30v2_users_"+t] = c }