mirror of
https://github.com/matrix-org/dendrite.git
synced 2026-01-01 03:03:10 -06:00
More GH comments changes
- Move comments to SQL statements - Shrink interface, add struct for stats - No fatal errors, use defaults
This commit is contained in:
parent
5bb0b56029
commit
27c76e3f89
|
|
@ -22,6 +22,7 @@ import (
|
||||||
"github.com/matrix-org/dendrite/clientapi/auth/authtypes"
|
"github.com/matrix-org/dendrite/clientapi/auth/authtypes"
|
||||||
"github.com/matrix-org/dendrite/userapi/api"
|
"github.com/matrix-org/dendrite/userapi/api"
|
||||||
"github.com/matrix-org/dendrite/userapi/storage/tables"
|
"github.com/matrix-org/dendrite/userapi/storage/tables"
|
||||||
|
"github.com/matrix-org/dendrite/userapi/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Profile interface {
|
type Profile interface {
|
||||||
|
|
@ -34,6 +35,7 @@ type Profile interface {
|
||||||
|
|
||||||
type Database interface {
|
type Database interface {
|
||||||
Profile
|
Profile
|
||||||
|
Statistics
|
||||||
GetAccountByPassword(ctx context.Context, localpart, plaintextPassword string) (*api.Account, error)
|
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
|
// 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
|
// 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)
|
GetPushers(ctx context.Context, localpart string) ([]api.Pusher, error)
|
||||||
RemovePusher(ctx context.Context, appid, pushkey, localpart string) error
|
RemovePusher(ctx context.Context, appid, pushkey, localpart string) error
|
||||||
RemovePushers(ctx context.Context, appid, pushkey string) error
|
RemovePushers(ctx context.Context, appid, pushkey string) error
|
||||||
|
}
|
||||||
|
|
||||||
AllUsers(ctx context.Context) (result int64, err error)
|
type Statistics interface {
|
||||||
NonBridgedUsers(ctx context.Context) (result int64, err error)
|
UserStatistics(ctx context.Context) (*types.UserStatistics, *types.DatabaseEngine, 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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Err3PIDInUse is the error returned when trying to save an association involving
|
// Err3PIDInUse is the error returned when trying to save an association involving
|
||||||
|
|
|
||||||
|
|
@ -21,9 +21,9 @@ import (
|
||||||
|
|
||||||
"github.com/lib/pq"
|
"github.com/lib/pq"
|
||||||
"github.com/matrix-org/dendrite/internal"
|
"github.com/matrix-org/dendrite/internal"
|
||||||
|
|
||||||
"github.com/matrix-org/dendrite/internal/sqlutil"
|
"github.com/matrix-org/dendrite/internal/sqlutil"
|
||||||
"github.com/matrix-org/dendrite/userapi/storage/tables"
|
"github.com/matrix-org/dendrite/userapi/storage/tables"
|
||||||
|
"github.com/matrix-org/dendrite/userapi/types"
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
@ -48,6 +48,12 @@ const countUsersLastSeenAfterSQL = "" +
|
||||||
" GROUP BY localpart" +
|
" GROUP BY localpart" +
|
||||||
" ) u"
|
" ) 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 = `
|
const countR30UsersSQL = `
|
||||||
SELECT platform, COUNT(*) FROM (
|
SELECT platform, COUNT(*) FROM (
|
||||||
SELECT users.localpart, platform, users.created_ts, MAX(uip.last_seen_ts)
|
SELECT users.localpart, platform, users.created_ts, MAX(uip.last_seen_ts)
|
||||||
|
|
@ -75,6 +81,11 @@ SELECT platform, COUNT(*) FROM (
|
||||||
) u GROUP BY PLATFORM
|
) 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 = `
|
const countR30UsersV2SQL = `
|
||||||
SELECT
|
SELECT
|
||||||
client_type,
|
client_type,
|
||||||
|
|
@ -140,6 +151,8 @@ ON CONFLICT (localpart, device_id, timestamp) DO NOTHING
|
||||||
;
|
;
|
||||||
`
|
`
|
||||||
|
|
||||||
|
const queryDBEngineVersion = "SHOW server_version;"
|
||||||
|
|
||||||
type statsStatements struct {
|
type statsStatements struct {
|
||||||
serverName gomatrixserverlib.ServerName
|
serverName gomatrixserverlib.ServerName
|
||||||
lastUpdate time.Time
|
lastUpdate time.Time
|
||||||
|
|
@ -149,6 +162,7 @@ type statsStatements struct {
|
||||||
updateUserDailyVisitsStmt *sql.Stmt
|
updateUserDailyVisitsStmt *sql.Stmt
|
||||||
countUserByAccountTypeStmt *sql.Stmt
|
countUserByAccountTypeStmt *sql.Stmt
|
||||||
countRegisteredUserByTypeStmt *sql.Stmt
|
countRegisteredUserByTypeStmt *sql.Stmt
|
||||||
|
dbEngineVersionStmt *sql.Stmt
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPostgresStatsTable(db *sql.DB, serverName gomatrixserverlib.ServerName) (tables.StatsTable, error) {
|
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.updateUserDailyVisitsStmt, updateUserDailyVisitsSQL},
|
||||||
{&s.countUserByAccountTypeStmt, countUserByAccountTypeSQL},
|
{&s.countUserByAccountTypeStmt, countUserByAccountTypeSQL},
|
||||||
{&s.countRegisteredUserByTypeStmt, countRegisteredUserByTypeStmt},
|
{&s.countRegisteredUserByTypeStmt, countRegisteredUserByTypeStmt},
|
||||||
|
{&s.dbEngineVersionStmt, queryDBEngineVersion},
|
||||||
}.Prepare(db)
|
}.Prepare(db)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -184,7 +199,7 @@ func (s *statsStatements) startTimers() {
|
||||||
time.AfterFunc(time.Minute*5, updateStatsFunc)
|
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)
|
stmt := sqlutil.TxStmt(txn, s.countUserByAccountTypeStmt)
|
||||||
err = stmt.QueryRowContext(ctx,
|
err = stmt.QueryRowContext(ctx,
|
||||||
pq.Int64Array{1, 2, 3, 4},
|
pq.Int64Array{1, 2, 3, 4},
|
||||||
|
|
@ -192,7 +207,7 @@ func (s *statsStatements) AllUsers(ctx context.Context, txn *sql.Tx) (result int
|
||||||
return
|
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)
|
stmt := sqlutil.TxStmt(txn, s.countUserByAccountTypeStmt)
|
||||||
err = stmt.QueryRowContext(ctx,
|
err = stmt.QueryRowContext(ctx,
|
||||||
pq.Int64Array{1, 2, 3},
|
pq.Int64Array{1, 2, 3},
|
||||||
|
|
@ -200,7 +215,7 @@ func (s *statsStatements) NonBridgedUsers(ctx context.Context, txn *sql.Tx) (res
|
||||||
return
|
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)
|
stmt := sqlutil.TxStmt(txn, s.countRegisteredUserByTypeStmt)
|
||||||
registeredAfter := time.Now().AddDate(0, 0, -1)
|
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()
|
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)
|
stmt := sqlutil.TxStmt(txn, s.countUsersLastSeenAfterStmt)
|
||||||
lastSeenAfter := time.Now().AddDate(0, 0, -1)
|
lastSeenAfter := time.Now().AddDate(0, 0, -1)
|
||||||
err = stmt.QueryRowContext(ctx,
|
err = stmt.QueryRowContext(ctx,
|
||||||
|
|
@ -234,7 +249,7 @@ func (s *statsStatements) DailyUsers(ctx context.Context, txn *sql.Tx) (result i
|
||||||
return
|
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)
|
stmt := sqlutil.TxStmt(txn, s.countUsersLastSeenAfterStmt)
|
||||||
lastSeenAfter := time.Now().AddDate(0, 0, -30)
|
lastSeenAfter := time.Now().AddDate(0, 0, -30)
|
||||||
err = stmt.QueryRowContext(ctx,
|
err = stmt.QueryRowContext(ctx,
|
||||||
|
|
@ -243,12 +258,13 @@ func (s *statsStatements) MonthlyUsers(ctx context.Context, txn *sql.Tx) (result
|
||||||
return
|
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
|
- Users who have created their accounts more than 30 days ago
|
||||||
- Where last seen at most 30 days ago
|
- Where last seen at most 30 days ago
|
||||||
- Where account creation and last_seen are > 30 days apart
|
- 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)
|
stmt := sqlutil.TxStmt(txn, s.countR30UsersStmt)
|
||||||
lastSeenAfter := time.Now().AddDate(0, 0, -30)
|
lastSeenAfter := time.Now().AddDate(0, 0, -30)
|
||||||
diff := time.Hour * 24 * 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()
|
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
|
- 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.
|
- 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)
|
stmt := sqlutil.TxStmt(txn, s.countR30UsersV2Stmt)
|
||||||
sixtyDaysAgo := time.Now().AddDate(0, 0, -60)
|
sixtyDaysAgo := time.Now().AddDate(0, 0, -60)
|
||||||
thirtyDaysAgo := time.Now().AddDate(0, 0, -30)
|
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()
|
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 {
|
func (s *statsStatements) updateUserDailyVisits(ctx context.Context, txn *sql.Tx) error {
|
||||||
stmt := sqlutil.TxStmt(txn, s.updateUserDailyVisitsStmt)
|
stmt := sqlutil.TxStmt(txn, s.updateUserDailyVisitsStmt)
|
||||||
_ = stmt
|
_ = stmt
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/matrix-org/dendrite/userapi/types"
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
|
||||||
|
|
@ -773,24 +774,7 @@ func (d *Database) RemovePushers(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Database) AllUsers(ctx context.Context) (result int64, err error) {
|
// UserStatistics populates types.UserStatistics, used in reports.
|
||||||
return d.Stats.AllUsers(ctx, nil)
|
func (d *Database) UserStatistics(ctx context.Context) (*types.UserStatistics, *types.DatabaseEngine, error) {
|
||||||
}
|
return d.Stats.UserStatistics(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)
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import (
|
||||||
"github.com/matrix-org/dendrite/internal"
|
"github.com/matrix-org/dendrite/internal"
|
||||||
"github.com/matrix-org/dendrite/internal/sqlutil"
|
"github.com/matrix-org/dendrite/internal/sqlutil"
|
||||||
"github.com/matrix-org/dendrite/userapi/storage/tables"
|
"github.com/matrix-org/dendrite/userapi/storage/tables"
|
||||||
|
"github.com/matrix-org/dendrite/userapi/types"
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
@ -47,6 +48,12 @@ const countUsersLastSeenAfterSQL = "" +
|
||||||
" GROUP BY localpart" +
|
" GROUP BY localpart" +
|
||||||
" ) u"
|
" ) 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 = `
|
const countR30UsersSQL = `
|
||||||
SELECT platform, COUNT(*) FROM (
|
SELECT platform, COUNT(*) FROM (
|
||||||
SELECT users.localpart, platform, users.created_ts, MAX(uip.last_seen_ts)
|
SELECT users.localpart, platform, users.created_ts, MAX(uip.last_seen_ts)
|
||||||
|
|
@ -74,6 +81,11 @@ SELECT platform, COUNT(*) FROM (
|
||||||
) u GROUP BY PLATFORM
|
) 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 = `
|
const countR30UsersV2SQL = `
|
||||||
SELECT
|
SELECT
|
||||||
client_type,
|
client_type,
|
||||||
|
|
@ -139,6 +151,8 @@ ON CONFLICT (localpart, device_id, timestamp) DO NOTHING
|
||||||
;
|
;
|
||||||
`
|
`
|
||||||
|
|
||||||
|
const queryDBEngineVersion = "select sqlite_version();"
|
||||||
|
|
||||||
type statsStatements struct {
|
type statsStatements struct {
|
||||||
serverName gomatrixserverlib.ServerName
|
serverName gomatrixserverlib.ServerName
|
||||||
db *sql.DB
|
db *sql.DB
|
||||||
|
|
@ -149,6 +163,7 @@ type statsStatements struct {
|
||||||
updateUserDailyVisitsStmt *sql.Stmt
|
updateUserDailyVisitsStmt *sql.Stmt
|
||||||
countUserByAccountTypeStmt *sql.Stmt
|
countUserByAccountTypeStmt *sql.Stmt
|
||||||
countRegisteredUserByTypeStmt *sql.Stmt
|
countRegisteredUserByTypeStmt *sql.Stmt
|
||||||
|
dbEngineVersionStmt *sql.Stmt
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSQLiteStatsTable(db *sql.DB, serverName gomatrixserverlib.ServerName) (tables.StatsTable, error) {
|
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.updateUserDailyVisitsStmt, updateUserDailyVisitsSQL},
|
||||||
{&s.countUserByAccountTypeStmt, countUserByAccountTypeSQL},
|
{&s.countUserByAccountTypeStmt, countUserByAccountTypeSQL},
|
||||||
{&s.countRegisteredUserByTypeStmt, countRegisteredUserByTypeStmt},
|
{&s.countRegisteredUserByTypeStmt, countRegisteredUserByTypeStmt},
|
||||||
|
{&s.dbEngineVersionStmt, queryDBEngineVersion},
|
||||||
}.Prepare(db)
|
}.Prepare(db)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -185,7 +201,7 @@ func (s *statsStatements) startTimers() {
|
||||||
time.AfterFunc(time.Minute*5, updateStatsFunc)
|
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)
|
query := strings.Replace(countUserByAccountTypeSQL, "($1)", sqlutil.QueryVariadic(4), 1)
|
||||||
queryStmt, err := s.db.Prepare(query)
|
queryStmt, err := s.db.Prepare(query)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -198,7 +214,7 @@ func (s *statsStatements) AllUsers(ctx context.Context, txn *sql.Tx) (result int
|
||||||
return
|
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)
|
query := strings.Replace(countUserByAccountTypeSQL, "($1)", sqlutil.QueryVariadic(3), 1)
|
||||||
queryStmt, err := s.db.Prepare(query)
|
queryStmt, err := s.db.Prepare(query)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -211,7 +227,7 @@ func (s *statsStatements) NonBridgedUsers(ctx context.Context, txn *sql.Tx) (res
|
||||||
return
|
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)
|
stmt := sqlutil.TxStmt(txn, s.countRegisteredUserByTypeStmt)
|
||||||
registeredAfter := time.Now().AddDate(0, 0, -1)
|
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()
|
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)
|
stmt := sqlutil.TxStmt(txn, s.countUsersLastSeenAfterStmt)
|
||||||
lastSeenAfter := time.Now().AddDate(0, 0, -1)
|
lastSeenAfter := time.Now().AddDate(0, 0, -1)
|
||||||
err = stmt.QueryRowContext(ctx,
|
err = stmt.QueryRowContext(ctx,
|
||||||
|
|
@ -245,7 +261,7 @@ func (s *statsStatements) DailyUsers(ctx context.Context, txn *sql.Tx) (result i
|
||||||
return
|
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)
|
stmt := sqlutil.TxStmt(txn, s.countUsersLastSeenAfterStmt)
|
||||||
lastSeenAfter := time.Now().AddDate(0, 0, -30)
|
lastSeenAfter := time.Now().AddDate(0, 0, -30)
|
||||||
err = stmt.QueryRowContext(ctx,
|
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 last seen at most 30 days ago
|
||||||
- Where account creation and last_seen are > 30 days apart
|
- 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)
|
stmt := sqlutil.TxStmt(txn, s.countR30UsersStmt)
|
||||||
lastSeenAfter := time.Now().AddDate(0, 0, -30)
|
lastSeenAfter := time.Now().AddDate(0, 0, -30)
|
||||||
diff := time.Hour * 24 * 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
|
- 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.
|
- 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)
|
stmt := sqlutil.TxStmt(txn, s.countR30UsersV2Stmt)
|
||||||
sixtyDaysAgo := time.Now().AddDate(0, 0, -60)
|
sixtyDaysAgo := time.Now().AddDate(0, 0, -60)
|
||||||
thirtyDaysAgo := time.Now().AddDate(0, 0, -30)
|
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()
|
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 {
|
func (s *statsStatements) updateUserDailyVisits(ctx context.Context, txn *sql.Tx) error {
|
||||||
stmt := sqlutil.TxStmt(txn, s.updateUserDailyVisitsStmt)
|
stmt := sqlutil.TxStmt(txn, s.updateUserDailyVisitsStmt)
|
||||||
_ = stmt
|
_ = stmt
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import (
|
||||||
|
|
||||||
"github.com/matrix-org/dendrite/clientapi/auth/authtypes"
|
"github.com/matrix-org/dendrite/clientapi/auth/authtypes"
|
||||||
"github.com/matrix-org/dendrite/userapi/api"
|
"github.com/matrix-org/dendrite/userapi/api"
|
||||||
|
"github.com/matrix-org/dendrite/userapi/types"
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -113,13 +114,7 @@ type NotificationTable interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
type StatsTable interface {
|
type StatsTable interface {
|
||||||
AllUsers(ctx context.Context, txn *sql.Tx) (result int64, err error)
|
UserStatistics(ctx context.Context, txn *sql.Tx) (*types.UserStatistics, *types.DatabaseEngine, 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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type NotificationFilter uint32
|
type NotificationFilter uint32
|
||||||
|
|
|
||||||
30
userapi/types/statistics.go
Normal file
30
userapi/types/statistics.go
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
@ -25,7 +25,6 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/matrix-org/dendrite/internal"
|
"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/setup/config"
|
||||||
"github.com/matrix-org/dendrite/userapi/storage"
|
"github.com/matrix-org/dendrite/userapi/storage"
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
|
|
@ -38,7 +37,7 @@ type phoneHomeStats struct {
|
||||||
serverName gomatrixserverlib.ServerName
|
serverName gomatrixserverlib.ServerName
|
||||||
startTime time.Time
|
startTime time.Time
|
||||||
cfg *config.Dendrite
|
cfg *config.Dendrite
|
||||||
db storage.Database
|
db storage.Statistics
|
||||||
isMonolith bool
|
isMonolith bool
|
||||||
client *http.Client
|
client *http.Client
|
||||||
}
|
}
|
||||||
|
|
@ -48,13 +47,13 @@ type timestampToRUUsage struct {
|
||||||
usage syscall.Rusage
|
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{
|
p := phoneHomeStats{
|
||||||
startTime: startTime,
|
startTime: startTime,
|
||||||
serverName: cfg.Global.ServerName,
|
serverName: cfg.Global.ServerName,
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
db: userDB,
|
db: statsDB,
|
||||||
isMonolith: cfg.IsMonolith,
|
isMonolith: cfg.IsMonolith,
|
||||||
client: &http.Client{
|
client: &http.Client{
|
||||||
Timeout: time.Second * 30,
|
Timeout: time.Second * 30,
|
||||||
|
|
@ -91,8 +90,7 @@ func (p *phoneHomeStats) collect() {
|
||||||
// cpu and memory usage information
|
// cpu and memory usage information
|
||||||
err := getMemoryStats(p)
|
err := getMemoryStats(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.WithError(err).Error("unable to get memory/cpu stats")
|
logrus.WithError(err).Error("unable to get memory/cpu stats, using defaults")
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// configuration information
|
// configuration information
|
||||||
|
|
@ -109,35 +107,6 @@ func (p *phoneHomeStats) collect() {
|
||||||
p.stats["log_level"] = "info"
|
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
|
// message and room stats
|
||||||
// TODO: Find a solution to actually set these values
|
// TODO: Find a solution to actually set these values
|
||||||
p.stats["total_room_count"] = 0
|
p.stats["total_room_count"] = 0
|
||||||
|
|
@ -146,57 +115,24 @@ func (p *phoneHomeStats) collect() {
|
||||||
p.stats["daily_e2ee_messages"] = 0
|
p.stats["daily_e2ee_messages"] = 0
|
||||||
p.stats["daily_sent_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 {
|
if err != nil {
|
||||||
logrus.WithError(err).Error("unable to query AllUsers")
|
logrus.WithError(err).Error("unable to query userstats, using default values")
|
||||||
return
|
|
||||||
}
|
}
|
||||||
p.stats["total_users"] = count
|
p.stats["database_engine"] = db.Engine
|
||||||
|
p.stats["database_server_version"] = db.Version
|
||||||
count, err = p.db.NonBridgedUsers(ctx)
|
p.stats["total_users"] = userStats.AllUsers
|
||||||
if err != nil {
|
p.stats["total_nonbridged_users"] = userStats.NonBridgedUsers
|
||||||
logrus.WithError(err).Error("unable to query NonBridgedUsers")
|
p.stats["daily_active_users"] = userStats.DailyUsers
|
||||||
return
|
p.stats["monthly_active_users"] = userStats.MonthlyUsers
|
||||||
}
|
for t, c := range userStats.RegisteredUsersByType {
|
||||||
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["daily_user_type_"+t] = c
|
p.stats["daily_user_type_"+t] = c
|
||||||
}
|
}
|
||||||
|
for t, c := range userStats.R30Users {
|
||||||
res, err = p.db.R30Users(ctx)
|
|
||||||
if err != nil {
|
|
||||||
logrus.WithError(err).Error("unable to query R30Users")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for t, c := range res {
|
|
||||||
p.stats["r30_users_"+t] = c
|
p.stats["r30_users_"+t] = c
|
||||||
}
|
}
|
||||||
res, err = p.db.R30UsersV2(ctx)
|
for t, c := range userStats.R30UsersV2 {
|
||||||
if err != nil {
|
|
||||||
logrus.WithError(err).Error("unable to query R30UsersV2")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for t, c := range res {
|
|
||||||
p.stats["r30v2_users_"+t] = c
|
p.stats["r30v2_users_"+t] = c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue