Files
syncthing/cmd/ursrv/aggregate/aggregate.go
T

227 lines
6.1 KiB
Go
Raw Normal View History

// Copyright (C) 2018 The Syncthing Authors.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.
package aggregate
2015-05-21 08:52:19 +02:00
import (
"database/sql"
"fmt"
2015-05-21 08:52:19 +02:00
"log"
"os"
"time"
2015-07-15 13:23:13 +02:00
_ "github.com/lib/pq"
2015-05-21 08:52:19 +02:00
)
type CLI struct {
DBConn string `env:"UR_DB_URL" default:"postgres://user:password@localhost/ur?sslmode=disable"`
2015-05-21 08:52:19 +02:00
}
func (cli *CLI) Run() error {
2015-05-21 08:52:19 +02:00
log.SetFlags(log.Ltime | log.Ldate)
log.SetOutput(os.Stdout)
db, err := sql.Open("postgres", cli.DBConn)
2015-05-21 08:52:19 +02:00
if err != nil {
return fmt.Errorf("database: %w", err)
2015-05-21 08:52:19 +02:00
}
err = setupDB(db)
if err != nil {
return fmt.Errorf("database: %w", err)
2015-05-21 08:52:19 +02:00
}
for {
runAggregation(db)
// Sleep until one minute past next midnight
sleepUntilNext(24*time.Hour, 1*time.Minute)
}
}
func runAggregation(db *sql.DB) {
2015-07-15 13:23:13 +02:00
since := maxIndexedDay(db, "VersionSummary")
log.Println("Aggregating VersionSummary data since", since)
2021-05-16 12:34:46 +01:00
rows, err := aggregateVersionSummary(db, since.Add(24*time.Hour))
2015-07-15 13:23:13 +02:00
if err != nil {
log.Println("aggregate:", err)
2015-07-15 13:23:13 +02:00
}
log.Println("Inserted", rows, "rows")
2016-09-06 20:15:18 +02:00
since = maxIndexedDay(db, "Performance")
log.Println("Aggregating Performance data since", since)
2021-05-16 12:34:46 +01:00
rows, err = aggregatePerformance(db, since.Add(24*time.Hour))
2016-09-06 20:15:18 +02:00
if err != nil {
log.Println("aggregate:", err)
2016-09-06 20:15:18 +02:00
}
log.Println("Inserted", rows, "rows")
2017-11-08 14:41:42 +00:00
since = maxIndexedDay(db, "BlockStats")
log.Println("Aggregating BlockStats data since", since)
2021-05-16 12:34:46 +01:00
rows, err = aggregateBlockStats(db, since.Add(24*time.Hour))
2017-11-08 14:41:42 +00:00
if err != nil {
log.Println("aggregate:", err)
2017-11-08 14:41:42 +00:00
}
log.Println("Inserted", rows, "rows")
2015-05-21 08:52:19 +02:00
}
func sleepUntilNext(intv, margin time.Duration) {
now := time.Now().UTC()
next := now.Truncate(intv).Add(intv).Add(margin)
log.Println("Sleeping until", next)
time.Sleep(next.Sub(now))
}
func setupDB(db *sql.DB) error {
_, err := db.Exec(`CREATE TABLE IF NOT EXISTS VersionSummary (
Day TIMESTAMP NOT NULL,
Version VARCHAR(8) NOT NULL,
Count INTEGER NOT NULL
)`)
if err != nil {
return err
}
2016-09-06 20:15:18 +02:00
_, err = db.Exec(`CREATE TABLE IF NOT EXISTS Performance (
Day TIMESTAMP NOT NULL,
TotFiles INTEGER NOT NULL,
TotMiB INTEGER NOT NULL,
SHA256Perf DOUBLE PRECISION NOT NULL,
MemorySize INTEGER NOT NULL,
MemoryUsageMiB INTEGER NOT NULL
)`)
if err != nil {
return err
}
2017-11-08 14:41:42 +00:00
_, err = db.Exec(`CREATE TABLE IF NOT EXISTS BlockStats (
Day TIMESTAMP NOT NULL,
Reports INTEGER NOT NULL,
2023-01-31 09:09:36 +01:00
Total BIGINT NOT NULL,
Renamed BIGINT NOT NULL,
Reused BIGINT NOT NULL,
Pulled BIGINT NOT NULL,
CopyOrigin BIGINT NOT NULL,
CopyOriginShifted BIGINT NOT NULL,
CopyElsewhere BIGINT NOT NULL
2017-11-08 14:41:42 +00:00
)`)
if err != nil {
return err
}
2016-09-06 20:15:18 +02:00
var t string
2015-05-21 08:52:19 +02:00
row := db.QueryRow(`SELECT 'UniqueDayVersionIndex'::regclass`)
2016-09-06 20:15:18 +02:00
if err := row.Scan(&t); err != nil {
_, _ = db.Exec(`CREATE UNIQUE INDEX UniqueDayVersionIndex ON VersionSummary (Day, Version)`)
2015-05-21 08:52:19 +02:00
}
2016-09-06 20:15:18 +02:00
row = db.QueryRow(`SELECT 'VersionDayIndex'::regclass`)
if err := row.Scan(&t); err != nil {
_, _ = db.Exec(`CREATE INDEX VersionDayIndex ON VersionSummary (Day)`)
2015-05-21 08:52:19 +02:00
}
2016-09-06 20:15:18 +02:00
row = db.QueryRow(`SELECT 'PerformanceDayIndex'::regclass`)
if err := row.Scan(&t); err != nil {
_, _ = db.Exec(`CREATE INDEX PerformanceDayIndex ON Performance (Day)`)
2016-09-06 20:15:18 +02:00
}
2017-11-08 14:41:42 +00:00
row = db.QueryRow(`SELECT 'BlockStatsDayIndex'::regclass`)
if err := row.Scan(&t); err != nil {
_, _ = db.Exec(`CREATE INDEX BlockStatsDayIndex ON BlockStats (Day)`)
2017-11-08 14:41:42 +00:00
}
return nil
2015-05-21 08:52:19 +02:00
}
2015-07-15 13:23:13 +02:00
func maxIndexedDay(db *sql.DB, table string) time.Time {
2015-05-21 08:52:19 +02:00
var t time.Time
2021-05-16 12:34:46 +01:00
row := db.QueryRow("SELECT MAX(DATE_TRUNC('day', Day)) FROM " + table)
2015-05-21 08:52:19 +02:00
err := row.Scan(&t)
if err != nil {
return time.Time{}
}
return t
}
2015-07-15 13:23:13 +02:00
func aggregateVersionSummary(db *sql.DB, since time.Time) (int64, error) {
2015-05-21 08:52:19 +02:00
res, err := db.Exec(`INSERT INTO VersionSummary (
SELECT
DATE_TRUNC('day', Received) AS Day,
SUBSTRING(Report->>'version' FROM '^v\d.\d+') AS Ver,
2015-05-21 08:52:19 +02:00
COUNT(*) AS Count
FROM ReportsJson
2015-05-21 08:52:19 +02:00
WHERE
2021-05-16 12:34:46 +01:00
Received > $1
AND Received < DATE_TRUNC('day', NOW())
AND Report->>'version' like 'v_.%'
2015-05-21 08:52:19 +02:00
GROUP BY Day, Ver
);
`, since)
if err != nil {
return 0, err
}
return res.RowsAffected()
}
2015-07-15 13:23:13 +02:00
2016-09-06 20:15:18 +02:00
func aggregatePerformance(db *sql.DB, since time.Time) (int64, error) {
res, err := db.Exec(`INSERT INTO Performance (
SELECT
DATE_TRUNC('day', Received) AS Day,
AVG((Report->>'totFiles')::numeric) As TotFiles,
AVG((Report->>'totMiB')::numeric) As TotMiB,
AVG((Report->>'sha256Perf')::numeric) As SHA256Perf,
AVG((Report->>'memorySize')::numeric) As MemorySize,
AVG((Report->>'memoryUsageMiB')::numeric) As MemoryUsageMiB
FROM ReportsJson
2016-09-06 20:15:18 +02:00
WHERE
2021-05-16 12:34:46 +01:00
Received > $1
AND Received < DATE_TRUNC('day', NOW())
AND Report->>'version' like 'v_.%'
2021-05-12 08:01:18 +01:00
/* Some custom implementation reported bytes when we expect megabytes, cap at petabyte */
AND (Report->>'memorySize')::numeric < 1073741824
2016-09-06 20:15:18 +02:00
GROUP BY Day
);
`, since)
if err != nil {
return 0, err
}
return res.RowsAffected()
}
2017-11-08 14:41:42 +00:00
func aggregateBlockStats(db *sql.DB, since time.Time) (int64, error) {
2017-11-09 22:22:47 +00:00
// Filter out anything prior 0.14.41 as that has sum aggregations which
// made no sense.
2017-11-08 14:41:42 +00:00
res, err := db.Exec(`INSERT INTO BlockStats (
SELECT
DATE_TRUNC('day', Received) AS Day,
COUNT(1) As Reports,
2023-01-31 09:09:36 +01:00
SUM((Report->'blockStats'->>'total')::numeric)::bigint AS Total,
SUM((Report->'blockStats'->>'renamed')::numeric)::bigint AS Renamed,
SUM((Report->'blockStats'->>'reused')::numeric)::bigint AS Reused,
SUM((Report->'blockStats'->>'pulled')::numeric)::bigint AS Pulled,
SUM((Report->'blockStats'->>'copyOrigin')::numeric)::bigint AS CopyOrigin,
SUM((Report->'blockStats'->>'copyOriginShifted')::numeric)::bigint AS CopyOriginShifted,
SUM((Report->'blockStats'->>'copyElsewhere')::numeric)::bigint AS CopyElsewhere
FROM ReportsJson
2017-11-08 14:41:42 +00:00
WHERE
2021-05-16 12:34:46 +01:00
Received > $1
AND Received < DATE_TRUNC('day', NOW())
AND (Report->>'urVersion')::numeric >= 3
AND Report->>'version' like 'v_.%'
AND Report->>'version' NOT LIKE 'v0.14.40%'
AND Report->>'version' NOT LIKE 'v0.14.39%'
AND Report->>'version' NOT LIKE 'v0.14.38%'
2017-11-09 22:22:47 +00:00
GROUP BY Day
2017-11-08 14:41:42 +00:00
);
`, since)
if err != nil {
return 0, err
}
return res.RowsAffected()
}