Dumped gorm and refactored database...

This commit is contained in:
A. Svensson 2016-09-14 17:07:00 +02:00
parent 3ac516a822
commit 949a78da33
4 changed files with 199 additions and 107 deletions

195
src/db.go
View File

@ -1,99 +1,150 @@
package ss13
import (
"fmt"
"log"
"time"
"github.com/jinzhu/gorm"
"github.com/jmoiron/sqlx"
_ "github.com/mattn/go-sqlite3"
)
type DB struct {
*gorm.DB
const SCHEMA string = `
CREATE TABLE IF NOT EXISTS servers (
id INTEGER PRIMARY KEY,
last_updated DATETIME,
title TEXT UNIQUE,
game_url TEXT,
site_url TEXT,
players_current INTEGER,
players_avg INTEGER,
players_min INTEGER,
players_max INTEGER,
players_mon INTEGER,
players_tue INTEGER,
players_wed INTEGER,
players_thu INTEGER,
players_fri INTEGER,
players_sat INTEGER,
players_sun INTEGER
);
CREATE TABLE IF NOT EXISTS server_populations (
id INTEGER PRIMARY KEY,
timestamp DATETIME,
players INTEGER,
server_id INTEGER REFERENCES servers(id) ON DELETE CASCADE ON UPDATE CASCADE
);
CREATE INDEX IF NOT EXISTS server_pop_index ON server_populations(server_id);
`
type Database struct {
*sqlx.DB
}
func OpenSqliteDB(args ...interface{}) (*DB, error) {
db, e := gorm.Open("sqlite3", args...)
type TX struct {
*sqlx.Tx
}
func OpenDatabase(path string) (*Database, error) {
db, e := sqlx.Connect("sqlite3", path)
if e != nil {
return nil, e
}
db.AutoMigrate(&Server{})
db.AutoMigrate(&ServerPopulation{})
return &DB{db}, nil
_, e = db.Exec(SCHEMA)
if e != nil {
return nil, e
}
return &Database{db}, nil
}
func (db *DB) NewTransaction() *DB {
return &DB{db.Begin()}
}
func (db *DB) AllServers() []*Server {
func (db *Database) AllServers() ([]*Server, error) {
var tmp []*Server
db.Order("last_updated desc, players_current desc, players_avg desc, title").Find(&tmp)
return tmp
e := db.Select(&tmp, `SELECT * FROM servers ORDER BY
last_updated DESC,
players_current DESC,
players_avg DESC,
title;`)
if e != nil {
return nil, e
}
return tmp, nil
}
func (db *DB) GetServer(id int) (*Server, error) {
func (db *Database) GetServer(id int) (*Server, error) {
var tmp Server
if db.First(&tmp, id).RecordNotFound() {
return nil, fmt.Errorf("Server not found")
e := db.Get(&tmp, "SELECT * FROM servers WHERE id = ? LIMIT 1;", id)
if e != nil {
return nil, e
}
return &tmp, nil
}
func (db *DB) GetOldServers(ts time.Time) []*Server {
func (db *Database) GetOldServers(ts time.Time) ([]*Server, error) {
var tmp []*Server
db.Where("last_updated < ?", ts).Find(&tmp)
return tmp
e := db.Select(&tmp, "SELECT * FROM servers WHERE last_updated < ?;", ts)
if e != nil {
return nil, e
}
return tmp, nil
}
func (db *DB) RemoveOldServers(ts time.Time) {
db.Where("last_updated < datetime(?, '-7 days')", ts).Delete(Server{})
func (db *Database) RemoveOldServers(ts time.Time) error {
_, e := db.Exec("DELETE FROM servers WHERE last_updated < datetime(?, '-7 days');", ts)
return e
}
func (db *DB) GetServerPopulation(id int, d time.Duration) []*ServerPopulation {
func (db *Database) GetServerPopulation(id int, d time.Duration) ([]*ServerPopulation, error) {
var tmp []*ServerPopulation
t := time.Now().Add(-d)
db.Order("timestamp desc, server_id").Where("server_id = ? and timestamp > ?", id, t).Find(&tmp)
return tmp
}
func (db *DB) InsertOrSelect(s *RawServerData) int {
var tmp Server
newserver := Server{
LastUpdated: s.Timestamp,
Title: s.Title,
GameUrl: s.Game_url,
SiteUrl: s.Site_url,
PlayersCurrent: s.Players,
PlayersAvg: s.Players,
PlayersMin: s.Players,
PlayersMax: s.Players,
e := db.Select(&tmp, `SELECT * FROM server_populations WHERE
server_id = ? AND timestamp > ?
ORDER BY timestamp DESC, server_id;`, id, t)
if e != nil {
return nil, e
}
db.Where("title = ?", s.Title).Attrs(newserver).FirstOrCreate(&tmp)
return tmp.ID
return tmp, nil
}
func (db *DB) AddServerPopulation(id int, s *RawServerData, now time.Time) {
func (db *TX) InsertOrSelect(s *RawServerData) (int, error) {
var tmp Server
db.Where("id = ?", id).First(&tmp)
pop := ServerPopulation{
Timestamp: now,
Players: s.Players,
Server: tmp,
e := db.Get(&tmp, "SELECT * FROM servers WHERE title = ? LIMIT 1;", s.Title)
if e == nil {
return tmp.ID, nil
}
db.Create(&pop)
r, e := db.Exec(`INSERT INTO servers (
last_updated, title, game_url, site_url, players_current,
players_avg, players_min, players_max
) VALUES(?, ?, ?, ?, ?, ?, ?, ?);`,
s.Timestamp,
s.Title,
s.Game_url,
s.Site_url,
s.Players, s.Players, s.Players, s.Players)
if e != nil {
return -1, e
}
id, e := r.LastInsertId()
if e != nil {
return -1, e
}
return int(id), nil
}
func (db *DB) UpdateServerStats(id int, s *RawServerData, now time.Time) {
var tmp Server
func (db *TX) AddServerPopulation(id int, s *RawServerData, now time.Time) error {
_, e := db.Exec(`INSERT INTO server_populations (
timestamp, players, server_id
) VALUES (?, ?, ?);`, now, s.Players, id)
return e
}
func (db *TX) UpdateServerStats(id int, s *RawServerData, now time.Time) error {
period := now.Add(-time.Duration(30*24) * time.Hour)
db.Where("id = ?", id).First(&tmp)
rows, err := db.Table("server_populations").Where("server_id = ? AND timestamp > ?", tmp.ID, period).Select("timestamp, players").Order("timestamp desc").Rows()
if err != nil {
log.Panic(err)
return
rows, e := db.Queryx(`SELECT timestamp, players FROM server_populations
WHERE server_id = ? AND timestamp > ?
ORDER BY timestamp DESC`, id, period)
if e != nil {
return e
}
defer rows.Close()
@ -119,20 +170,18 @@ func (db *DB) UpdateServerStats(id int, s *RawServerData, now time.Time) {
day_counts[day]++
}
tmp.LastUpdated = s.Timestamp
tmp.Title = s.Title
tmp.GameUrl = s.Game_url
tmp.SiteUrl = s.Site_url
tmp.PlayersCurrent = s.Players
tmp.PlayersAvg = sum / count
tmp.PlayersMin = min
tmp.PlayersMax = max
tmp.PlayersMon = day_sums[1] / day_counts[1]
tmp.PlayersTue = day_sums[2] / day_counts[2]
tmp.PlayersWed = day_sums[3] / day_counts[3]
tmp.PlayersThu = day_sums[4] / day_counts[4]
tmp.PlayersFri = day_sums[5] / day_counts[5]
tmp.PlayersSat = day_sums[6] / day_counts[6]
tmp.PlayersSun = day_sums[0] / day_counts[0]
db.Save(&tmp)
_, e = db.Exec(`UPDATE servers SET last_updated = ?, players_current = ?,
players_avg = ?, players_min = ?, players_max = ?, players_mon = ?,
players_tue = ?, players_wed = ?, players_thu = ?, players_fri = ?,
players_sat = ?, players_sun = ?
WHERE id = ?;`, s.Timestamp, s.Players, sum/count, min, max,
day_sums[1]/day_counts[1],
day_sums[2]/day_counts[2],
day_sums[3]/day_counts[3],
day_sums[4]/day_counts[4],
day_sums[5]/day_counts[5],
day_sums[6]/day_counts[6],
day_sums[0]/day_counts[0],
id)
return e
}

View File

@ -9,23 +9,23 @@ import (
type Server struct {
ID int
LastUpdated time.Time
Title string `sql:"type:varchar(64);unique"`
GameUrl string
SiteUrl string
LastUpdated time.Time `db:"last_updated"`
Title string
GameUrl string `db:"game_url"`
SiteUrl string `db:"site_url"`
PlayersCurrent int
PlayersAvg int
PlayersMin int
PlayersMax int
PlayersCurrent int `db:"players_current"`
PlayersAvg int `db:"players_avg"`
PlayersMin int `db:"players_min"`
PlayersMax int `db:"players_max"`
PlayersMon int
PlayersTue int
PlayersWed int
PlayersThu int
PlayersFri int
PlayersSat int
PlayersSun int
PlayersMon int `db:"players_mon"`
PlayersTue int `db:"players_tue"`
PlayersWed int `db:"players_wed"`
PlayersThu int `db:"players_thu"`
PlayersFri int `db:"players_fri"`
PlayersSat int `db:"players_sat"`
PlayersSun int `db:"players_sun"`
}
// Check if a server's last_updated time since now is greater or equal to X hours.
@ -63,10 +63,5 @@ type ServerPopulation struct {
ID int
Timestamp time.Time
Players int
ServerID int `sql:"index;type:integer REFERENCES servers(id) ON DELETE CASCADE ON UPDATE CASCADE"`
Server Server
ServerID int `db:"server_id"`
}
// See https://github.com/jinzhu/gorm/issues/635 for why we have to manually add
// in a raw REFERENCES statement here.
// Hint: Foreign key creation is bugged when using gorm with sqlite.

View File

@ -51,24 +51,50 @@ func (i *Instance) UpdateServers() {
addServer(s)
}
tx := i.db.NewTransaction()
// TODO: move this block into db.go
//tx, e := i.db.NewTransaction()
t, e := i.db.Beginx()
if e != nil {
panic(e) // TODO
}
tx := &TX{t}
for _, s := range servers {
fmt.Println("Updating:", s.Title)
// get server's db id (or create)
id := tx.InsertOrSelect(s)
// create new player history point
tx.AddServerPopulation(id, s, now)
// update server (urls and player stats)
tx.UpdateServerStats(id, s, now)
id, e := tx.InsertOrSelect(s)
if e != nil {
continue
}
// create new player history point
e = tx.AddServerPopulation(id, s, now)
if e != nil {
continue
}
// update server (urls and player stats)
e = tx.UpdateServerStats(id, s, now)
if e != nil {
continue
}
}
e = tx.Commit()
if e != nil {
panic(e) // TODO
}
e = i.db.RemoveOldServers(Now())
if e != nil {
panic(e) // TODO
}
tx.RemoveOldServers(Now())
tx.Commit()
}
func (i *Instance) getOldServers() []*RawServerData {
// TODO: there's some bug that makes this func return all servers?
var tmp []*RawServerData
for _, old := range i.db.GetOldServers(Now()) {
servers, e := i.db.GetOldServers(Now())
if e != nil {
panic(e) // TODO
}
for _, old := range servers {
s := RawServerData{
Title: old.Title,
Game_url: old.GameUrl,

View File

@ -18,7 +18,7 @@ type Instance struct {
Config *Config
Debug bool
db *DB
db *Database
router *mux.Router
tmpls *template.Template
}
@ -43,7 +43,7 @@ func New(debug bool, path string) (*Instance, error) {
return nil, e
}
db, e := OpenSqliteDB(c.DatabasePath)
db, e := OpenDatabase(c.DatabasePath)
if e != nil {
return nil, e
}
@ -89,6 +89,11 @@ func (i *Instance) page_404(w http.ResponseWriter, r *http.Request) {
i.tmpls.ExecuteTemplate(w, "page_404.html", nil)
}
func (i *Instance) page_500(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusInternalServerError)
//i.tmpls.ExecuteTemplate(w, "page_500.html", nil)
}
func (i *Instance) page_static(w http.ResponseWriter, r *http.Request) {
p := path.Join("static", mux.Vars(r)["file"])
b, e := Asset(p)
@ -106,7 +111,12 @@ func (i *Instance) page_static(w http.ResponseWriter, r *http.Request) {
}
func (i *Instance) page_index(w http.ResponseWriter, r *http.Request) {
servers := i.db.AllServers()
servers, e := i.db.AllServers()
if e != nil {
Log("Error for AllServers(): %s", e)
i.page_500(w, r)
return
}
i.tmpls.ExecuteTemplate(w, "page_index.html", D{
"pagetitle": "Index",
"servers": servers,
@ -142,11 +152,23 @@ func (i *Instance) page_server(w http.ResponseWriter, r *http.Request) {
weekday{"Saturday", s.PlayersSat},
weekday{"Sunday", s.PlayersSun},
}
weekly, e := i.db.GetServerPopulation(int(id), time.Duration(7*24+12)*time.Hour)
if e != nil {
Log("Error for GetServerPopulation(): %s", e)
i.page_500(w, r)
return
}
monthly, e := i.db.GetServerPopulation(int(id), time.Duration(31*24)*time.Hour)
if e != nil {
Log("Error for GetServerPopulation(): %s", e)
i.page_500(w, r)
return
}
i.tmpls.ExecuteTemplate(w, "page_server.html", D{
"pagetitle": s.Title,
"server": s,
"weekhistory": i.db.GetServerPopulation(int(id), time.Duration(7*24+12)*time.Hour),
"monthhistory": i.db.GetServerPopulation(int(id), time.Duration(31*24)*time.Hour),
"weekhistory": weekly,
"monthhistory": monthly,
"weekdayavg": weekdayavg,
})
}