add database vacuum cleaner, move all sql statements to separate file, cron saves daily player/tribe stats

This commit is contained in:
Dawid Wysokiński 2020-06-25 13:24:06 +02:00 committed by Kichiyaki
parent 7bca0d25e5
commit ed8dd20dd8
7 changed files with 495 additions and 196 deletions

View File

@ -21,174 +21,6 @@ import (
const (
endpointGetServers = "/backend/get_servers.php"
serverPGFunctions = `
CREATE OR REPLACE FUNCTION ?0.log_tribe_change()
RETURNS trigger AS
$BODY$
BEGIN
IF NEW.tribe_id <> OLD.tribe_id THEN
INSERT INTO ?0.tribe_changes(player_id,old_tribe_id,new_tribe_id,created_at)
VALUES(OLD.id,OLD.tribe_id,NEW.tribe_id,now());
END IF;
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql VOLATILE;
CREATE OR REPLACE FUNCTION ?0.log_player_name_change()
RETURNS trigger AS
$BODY$
BEGIN
IF NEW.name <> OLD.name THEN
INSERT INTO player_name_changes(lang_version_tag,player_id,old_name,new_name,changed_on)
VALUES(?1,NEW.id,OLD.name,NEW.name,CURRENT_DATE)
ON CONFLICT DO NOTHING;
END IF;
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql VOLATILE;
CREATE OR REPLACE FUNCTION ?0.get_old_and_new_owner_tribe_id()
RETURNS trigger AS
$BODY$
BEGIN
IF NEW.old_owner_id <> 0 THEN
SELECT tribe_id INTO NEW.old_owner_tribe_id
FROM ?0.players
WHERE id = NEW.old_owner_id;
END IF;
IF NEW.old_owner_tribe_id IS NULL THEN
NEW.old_owner_tribe_id = 0;
END IF;
IF NEW.new_owner_id <> 0 THEN
SELECT tribe_id INTO NEW.new_owner_tribe_id
FROM ?0.players
WHERE id = NEW.new_owner_id;
END IF;
IF NEW.new_owner_tribe_id IS NULL THEN
NEW.new_owner_tribe_id = 0;
END IF;
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql VOLATILE;
CREATE OR REPLACE FUNCTION check_daily_growth()
RETURNS trigger AS
$BODY$
BEGIN
IF NEW.exist = false THEN
NEW.daily_growth = 0;
END IF;
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION check_existence()
RETURNS trigger AS
$BODY$
BEGIN
IF NEW.exist = false AND OLD.exist = true THEN
NEW.deleted_at = now();
END IF;
IF NEW.exist = true THEN
NEW.deleted_at = null;
END IF;
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION check_dominance()
RETURNS trigger AS
$BODY$
BEGIN
IF NEW.exist = false THEN
NEW.dominance = 0;
END IF;
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION ?0.insert_to_player_to_servers()
RETURNS trigger AS
$BODY$
BEGIN
INSERT INTO player_to_servers(server_key,player_id)
VALUES('?0', NEW.id)
ON CONFLICT DO NOTHING;
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql;
`
serverPGTriggers = `
DROP TRIGGER IF EXISTS ?0_tribe_changes ON ?0.players;
CREATE TRIGGER ?0_tribe_changes
AFTER UPDATE
ON ?0.players
FOR EACH ROW
EXECUTE PROCEDURE ?0.log_tribe_change();
DROP TRIGGER IF EXISTS ?0_name_change ON ?0.players;
CREATE TRIGGER ?0_name_change
AFTER UPDATE
ON ?0.players
FOR EACH ROW
EXECUTE PROCEDURE ?0.log_player_name_change();
DROP TRIGGER IF EXISTS ?0_check_daily_growth ON ?0.players;
CREATE TRIGGER ?0_check_daily_growth
BEFORE UPDATE
ON ?0.players
FOR EACH ROW
EXECUTE PROCEDURE check_daily_growth();
DROP TRIGGER IF EXISTS ?0_check_player_existence ON ?0.players;
CREATE TRIGGER ?0_check_player_existence
BEFORE UPDATE
ON ?0.players
FOR EACH ROW
EXECUTE PROCEDURE check_existence();
DROP TRIGGER IF EXISTS ?0_check_tribe_existence ON ?0.tribes;
CREATE TRIGGER ?0_check_tribe_existence
BEFORE UPDATE
ON ?0.tribes
FOR EACH ROW
EXECUTE PROCEDURE check_existence();
DROP TRIGGER IF EXISTS ?0_check_dominance ON ?0.tribes;
CREATE TRIGGER ?0_check_dominance
BEFORE UPDATE
ON ?0.tribes
FOR EACH ROW
EXECUTE PROCEDURE check_dominance();
DROP TRIGGER IF EXISTS ?0_update_ennoblement_old_and_new_owner_tribe_id ON ?0.ennoblements;
CREATE TRIGGER ?0_update_ennoblement_old_and_new_owner_tribe_id
BEFORE INSERT
ON ?0.ennoblements
FOR EACH ROW
EXECUTE PROCEDURE ?0.get_old_and_new_owner_tribe_id();
DROP TRIGGER IF EXISTS ?0_insert_to_player_to_servers ON ?0.players;
CREATE TRIGGER ?0_insert_to_player_to_servers
AFTER INSERT
ON ?0.players
FOR EACH ROW
EXECUTE PROCEDURE ?0.insert_to_player_to_servers();
`
)
type handler struct {
@ -210,8 +42,12 @@ func Attach(c *cron.Cron, db *pg.DB) error {
if _, err := c.AddFunc("30 1 * * *", h.updateStats); err != nil {
return err
}
if _, err := c.AddFunc("30 2 * * *", h.vacuumDatabase); err != nil {
return err
}
go func() {
h.updateServersData()
// h.updateServersData()
h.vacuumDatabase()
h.updateServersHistory()
h.updateStats()
}()
@ -266,6 +102,8 @@ func (h *handler) createSchema(server *models.Server) error {
(*models.TribeHistory)(nil),
(*models.PlayerHistory)(nil),
(*models.TribeChange)(nil),
(*models.DailyPlayerStats)(nil),
(*models.DailyTribeStats)(nil),
}
for _, model := range models {
@ -365,7 +203,7 @@ func (h *handler) updateServersData() {
}
var wg sync.WaitGroup
max := runtime.NumCPU() * 5
max := runtime.NumCPU() * 2
count := 0
for _, server := range servers {
@ -492,3 +330,42 @@ func (h *handler) updateStats() {
return
}
}
func (h *handler) vacuumDatabase() {
servers := []*models.Server{}
err := h.db.
Model(&servers).
Select()
if err != nil {
log.Fatal(errors.Wrap(err, "vacuumDatabase"))
return
}
var wg sync.WaitGroup
max := runtime.NumCPU() * 5
count := 0
for _, server := range servers {
if count >= max {
wg.Wait()
count = 0
}
sh := &vacuumServerDBHandler{
db: h.db.WithParam("SERVER", pg.Safe(server.Key)),
}
count++
wg.Add(1)
go func(server *models.Server, sh *vacuumServerDBHandler) {
defer wg.Done()
log.Printf("%s: vacuuming database", server.Key)
if err := sh.vacuum(); err != nil {
log.Println(errors.Wrap(err, server.Key))
return
} else {
log.Printf("%s: database vacuumed", server.Key)
}
}(server, sh)
}
wg.Wait()
}

60
cron/searchable_by_id.go Normal file
View File

@ -0,0 +1,60 @@
package cron
import "github.com/tribalwarshelp/shared/models"
type tribeSearchableByID struct {
*models.Tribe
}
func (t tribeSearchableByID) id() int {
return t.ID
}
type playerSearchableByID struct {
*models.Player
}
func (t playerSearchableByID) id() int {
return t.ID
}
type searchableByID interface {
id() int
}
func makePlayersSearchable(players []*models.Player) []searchableByID {
searchable := []searchableByID{}
for _, player := range players {
searchable = append(searchable, playerSearchableByID{player})
}
return searchable
}
func makeTribesSearchable(tribes []*models.Tribe) []searchableByID {
searchable := []searchableByID{}
for _, tribe := range tribes {
searchable = append(searchable, tribeSearchableByID{tribe})
}
return searchable
}
func searchByID(haystack []searchableByID, id int) int {
low := 0
high := len(haystack) - 1
for low <= high {
median := (low + high) / 2
if haystack[median].id() < id {
low = median + 1
} else {
high = median - 1
}
}
if low == len(haystack) || haystack[low].id() != id {
return 0
}
return low
}

172
cron/sql_statements.go Normal file
View File

@ -0,0 +1,172 @@
package cron
const (
serverPGFunctions = `
CREATE OR REPLACE FUNCTION ?0.log_tribe_change()
RETURNS trigger AS
$BODY$
BEGIN
IF NEW.tribe_id <> OLD.tribe_id THEN
INSERT INTO ?0.tribe_changes(player_id,old_tribe_id,new_tribe_id,created_at)
VALUES(OLD.id,OLD.tribe_id,NEW.tribe_id,now());
END IF;
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql VOLATILE;
CREATE OR REPLACE FUNCTION ?0.log_player_name_change()
RETURNS trigger AS
$BODY$
BEGIN
IF NEW.name <> OLD.name THEN
INSERT INTO player_name_changes(lang_version_tag,player_id,old_name,new_name,changed_on)
VALUES(?1,NEW.id,OLD.name,NEW.name,CURRENT_DATE)
ON CONFLICT DO NOTHING;
END IF;
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql VOLATILE;
CREATE OR REPLACE FUNCTION ?0.get_old_and_new_owner_tribe_id()
RETURNS trigger AS
$BODY$
BEGIN
IF NEW.old_owner_id <> 0 THEN
SELECT tribe_id INTO NEW.old_owner_tribe_id
FROM ?0.players
WHERE id = NEW.old_owner_id;
END IF;
IF NEW.old_owner_tribe_id IS NULL THEN
NEW.old_owner_tribe_id = 0;
END IF;
IF NEW.new_owner_id <> 0 THEN
SELECT tribe_id INTO NEW.new_owner_tribe_id
FROM ?0.players
WHERE id = NEW.new_owner_id;
END IF;
IF NEW.new_owner_tribe_id IS NULL THEN
NEW.new_owner_tribe_id = 0;
END IF;
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql VOLATILE;
CREATE OR REPLACE FUNCTION check_daily_growth()
RETURNS trigger AS
$BODY$
BEGIN
IF NEW.exist = false THEN
NEW.daily_growth = 0;
END IF;
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION check_existence()
RETURNS trigger AS
$BODY$
BEGIN
IF NEW.exist = false AND OLD.exist = true THEN
NEW.deleted_at = now();
END IF;
IF NEW.exist = true THEN
NEW.deleted_at = null;
END IF;
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION check_dominance()
RETURNS trigger AS
$BODY$
BEGIN
IF NEW.exist = false THEN
NEW.dominance = 0;
END IF;
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION ?0.insert_to_player_to_servers()
RETURNS trigger AS
$BODY$
BEGIN
INSERT INTO player_to_servers(server_key,player_id)
VALUES('?0', NEW.id)
ON CONFLICT DO NOTHING;
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql;
`
serverPGTriggers = `
DROP TRIGGER IF EXISTS ?0_tribe_changes ON ?0.players;
CREATE TRIGGER ?0_tribe_changes
AFTER UPDATE
ON ?0.players
FOR EACH ROW
EXECUTE PROCEDURE ?0.log_tribe_change();
DROP TRIGGER IF EXISTS ?0_name_change ON ?0.players;
CREATE TRIGGER ?0_name_change
AFTER UPDATE
ON ?0.players
FOR EACH ROW
EXECUTE PROCEDURE ?0.log_player_name_change();
DROP TRIGGER IF EXISTS ?0_check_daily_growth ON ?0.players;
CREATE TRIGGER ?0_check_daily_growth
BEFORE UPDATE
ON ?0.players
FOR EACH ROW
EXECUTE PROCEDURE check_daily_growth();
DROP TRIGGER IF EXISTS ?0_check_player_existence ON ?0.players;
CREATE TRIGGER ?0_check_player_existence
BEFORE UPDATE
ON ?0.players
FOR EACH ROW
EXECUTE PROCEDURE check_existence();
DROP TRIGGER IF EXISTS ?0_check_tribe_existence ON ?0.tribes;
CREATE TRIGGER ?0_check_tribe_existence
BEFORE UPDATE
ON ?0.tribes
FOR EACH ROW
EXECUTE PROCEDURE check_existence();
DROP TRIGGER IF EXISTS ?0_check_dominance ON ?0.tribes;
CREATE TRIGGER ?0_check_dominance
BEFORE UPDATE
ON ?0.tribes
FOR EACH ROW
EXECUTE PROCEDURE check_dominance();
DROP TRIGGER IF EXISTS ?0_update_ennoblement_old_and_new_owner_tribe_id ON ?0.ennoblements;
CREATE TRIGGER ?0_update_ennoblement_old_and_new_owner_tribe_id
BEFORE INSERT
ON ?0.ennoblements
FOR EACH ROW
EXECUTE PROCEDURE ?0.get_old_and_new_owner_tribe_id();
DROP TRIGGER IF EXISTS ?0_insert_to_player_to_servers ON ?0.players;
CREATE TRIGGER ?0_insert_to_player_to_servers
AFTER INSERT
ON ?0.players
FOR EACH ROW
EXECUTE PROCEDURE ?0.insert_to_player_to_servers();
`
)

View File

@ -399,6 +399,76 @@ func (h *updateServerDataHandler) getUnitConfig() (*models.UnitConfig, error) {
return cfg, nil
}
func (h *updateServerDataHandler) isDateTheSameAsServerHistoryUpdatedAt(t time.Time) bool {
return t.Year() == h.server.HistoryUpdatedAt.Year() &&
t.Month() == h.server.HistoryUpdatedAt.Month() &&
t.Day() == h.server.HistoryUpdatedAt.Day()
}
func (h *updateServerDataHandler) calculateODDifference(od1 models.OpponentsDefeated, od2 models.OpponentsDefeated) models.OpponentsDefeated {
return models.OpponentsDefeated{
RankAtt: (od1.RankAtt - od2.RankAtt) * -1,
ScoreAtt: od1.ScoreAtt - od2.ScoreAtt,
RankDef: (od1.RankDef - od2.RankDef) * -1,
ScoreDef: od1.ScoreDef - od2.ScoreDef,
RankSup: (od1.RankSup - od2.RankSup) * -1,
ScoreSup: od1.ScoreSup - od2.ScoreSup,
RankTotal: (od1.RankTotal - od2.RankTotal) * -1,
ScoreTotal: od1.ScoreTotal - od2.ScoreTotal,
}
}
func (h *updateServerDataHandler) calculateDailyTribeStats(tribes []*models.Tribe,
history []*models.TribeHistory) []*models.DailyTribeStats {
dailyStats := []*models.DailyTribeStats{}
for _, historyRecord := range history {
if !h.isDateTheSameAsServerHistoryUpdatedAt(historyRecord.CreatedAt) {
continue
}
if index := searchByID(makeTribesSearchable(tribes), historyRecord.TribeID); index != 0 {
tribe := tribes[index]
dailyStats = append(dailyStats, &models.DailyTribeStats{
TribeID: tribe.ID,
Members: tribe.TotalMembers - historyRecord.TotalMembers,
Villages: tribe.TotalVillages - historyRecord.TotalVillages,
Points: tribe.Points - historyRecord.Points,
AllPoints: tribe.AllPoints - historyRecord.AllPoints,
Rank: (tribe.Rank - historyRecord.Rank) * -1,
Dominance: tribe.Dominance - historyRecord.Dominance,
CreatedAt: historyRecord.CreatedAt,
OpponentsDefeated: h.calculateODDifference(tribe.OpponentsDefeated, historyRecord.OpponentsDefeated),
})
}
}
return dailyStats
}
func (h *updateServerDataHandler) calculateDailyPlayerStats(players []*models.Player,
history []*models.PlayerHistory) []*models.DailyPlayerStats {
dailyStats := []*models.DailyPlayerStats{}
for _, historyRecord := range history {
if !h.isDateTheSameAsServerHistoryUpdatedAt(historyRecord.CreatedAt) {
continue
}
if index := searchByID(makePlayersSearchable(players), historyRecord.PlayerID); index != 0 {
player := players[index]
dailyStats = append(dailyStats, &models.DailyPlayerStats{
PlayerID: player.ID,
Villages: player.TotalVillages - historyRecord.TotalVillages,
Points: player.Points - historyRecord.Points,
Rank: (player.Rank - historyRecord.Rank) * -1,
CreatedAt: historyRecord.CreatedAt,
OpponentsDefeated: h.calculateODDifference(player.OpponentsDefeated, historyRecord.OpponentsDefeated),
})
}
}
return dailyStats
}
func (h *updateServerDataHandler) update() error {
pod, err := h.getOD(false)
if err != nil {
@ -476,7 +546,34 @@ func (h *updateServerDataHandler) update() error {
Where("id NOT IN (?)", pg.In(ids)).
Set("exist = false").
Update(); err != nil && err != pg.ErrNoRows {
return errors.Wrap(err, "cannot update not existed tribes")
return errors.Wrap(err, "cannot update not exist tribes")
}
tribesHistory := []*models.TribeHistory{}
if err := tx.Model(&tribesHistory).
DistinctOn("tribe_id").
Column("*").
Where("tribe_id IN (?)", pg.In(ids)).
Order("tribe_id DESC", "created_at DESC").
Select(); err != nil && err != pg.ErrNoRows {
return errors.Wrap(err, "cannot select tribes history")
}
todaysTribesStats := h.calculateDailyTribeStats(tribes, tribesHistory)
if len(todaysTribesStats) > 0 {
if _, err := tx.
Model(&todaysTribesStats).
OnConflict("ON CONSTRAINT daily_tribe_stats_tribe_id_created_at_key DO UPDATE").
Set("members = EXCLUDED.members").
Set("villages = EXCLUDED.villages").
Set("points = EXCLUDED.points").
Set("all_points = EXCLUDED.all_points").
Set("rank = EXCLUDED.rank").
Set("dominance = EXCLUDED.dominance").
Apply(attachODSetClauses).
Insert(); err != nil {
return errors.Wrap(err, "cannot insert today's tribes stats")
}
}
}
if len(players) > 0 {
@ -502,7 +599,30 @@ func (h *updateServerDataHandler) update() error {
Where("id NOT IN (?)", pg.In(ids)).
Set("exist = false").
Update(); err != nil && err != pg.ErrNoRows {
return errors.Wrap(err, "cannot update not existed players")
return errors.Wrap(err, "cannot update not exist players")
}
playerHistory := []*models.PlayerHistory{}
if err := tx.Model(&playerHistory).
DistinctOn("player_id").
Column("*").
Where("player_id IN (?)", pg.In(ids)).
Order("player_id DESC", "created_at DESC").Select(); err != nil && err != pg.ErrNoRows {
return errors.Wrap(err, "cannot select players history")
}
todaysPlayersStats := h.calculateDailyPlayerStats(players, playerHistory)
if len(todaysPlayersStats) > 0 {
if _, err := tx.
Model(&todaysPlayersStats).
OnConflict("ON CONSTRAINT daily_player_stats_player_id_created_at_key DO UPDATE").
Set("villages = EXCLUDED.villages").
Set("points = EXCLUDED.points").
Set("rank = EXCLUDED.rank").
Apply(attachODSetClauses).
Insert(); err != nil {
return errors.Wrap(err, "cannot insert today's players stats")
}
}
}
if len(villages) > 0 {
@ -525,7 +645,7 @@ func (h *updateServerDataHandler) update() error {
if _, err := tx.Model(&models.Village{}).
Where("id NOT IN (?)", pg.In(ids)).
Delete(); err != nil && err != pg.ErrNoRows {
return errors.Wrap(err, "cannot delete not existed villages")
return errors.Wrap(err, "cannot delete not exist villages")
}
}
if len(ennoblements) > 0 {

View File

@ -0,0 +1,58 @@
package cron
import (
"time"
"github.com/go-pg/pg/v10"
"github.com/pkg/errors"
"github.com/tribalwarshelp/shared/models"
)
type vacuumServerDBHandler struct {
db *pg.DB
}
func (h *vacuumServerDBHandler) vacuum() error {
tx, err := h.db.Begin()
if err != nil {
return err
}
defer tx.Close()
withNotExitedPlayers := h.db.Model(&models.Player{}).Where("exist = false")
withNotExitedTribes := h.db.Model(&models.Tribe{}).Where("exist = false")
_, err = tx.Model(&models.PlayerHistory{}).
With("players", withNotExitedPlayers).
Where("player_id IN (Select id FROM players) OR player_history.created_at < ?", time.Now().Add(-1*24*time.Hour*90)).
Delete()
if err != nil {
return errors.Wrap(err, "cannot delete old player history")
}
_, err = tx.Model(&models.TribeHistory{}).
With("tribes", withNotExitedTribes).
Where("tribe_id IN (Select id FROM tribes) OR tribe_history.created_at < ?", time.Now().Add(-1*24*time.Hour*90)).
Delete()
if err != nil {
return errors.Wrap(err, "cannot delete old tribe history")
}
_, err = tx.Model(&models.DailyPlayerStats{}).
With("players", withNotExitedPlayers).
Where("player_id IN (Select id FROM players) OR daily_player_stats.created_at < ?", time.Now().Add(-1*24*time.Hour*90)).
Delete()
if err != nil {
return errors.Wrap(err, "cannot delete old player stats")
}
_, err = tx.Model(&models.DailyTribeStats{}).
With("tribes", withNotExitedTribes).
Where("tribe_id IN (Select id FROM tribes) OR daily_tribe_stats.created_at < ?", time.Now().Add(-1*24*time.Hour*90)).
Delete()
if err != nil {
return errors.Wrap(err, "cannot delete old tribe stats")
}
return tx.Commit()
}

10
go.mod
View File

@ -4,10 +4,14 @@ go 1.14
require (
github.com/Kichiyaki/go-php-serialize v0.0.0-20200601110855-47b6982acf83
github.com/go-pg/pg/v10 v10.0.0-beta.1
github.com/go-pg/pg/v10 v10.0.0-beta.2
github.com/joho/godotenv v1.3.0
github.com/pkg/errors v0.9.1
github.com/robfig/cron/v3 v3.0.1
github.com/tribalwarshelp/shared v0.0.0-20200624134544-636239c5fd17
golang.org/x/net v0.0.0-20200528225125-3c3fba18258b // indirect
github.com/segmentio/encoding v0.1.14 // indirect
github.com/tribalwarshelp/shared v0.0.0-20200625103607-9768e416aea9
golang.org/x/net v0.0.0-20200625001655-4c5254603344 // indirect
golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4 // indirect
google.golang.org/grpc v1.30.0 // indirect
google.golang.org/protobuf v1.25.0 // indirect
)

48
go.sum
View File

@ -20,8 +20,8 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/go-pg/pg/v10 v10.0.0-beta.1 h1:GD65aZVx9yR6fxxXdglBlook89oxxg2FYE11TDd9Now=
github.com/go-pg/pg/v10 v10.0.0-beta.1/go.mod h1:JYxBTzIz9dpSAa+bphD1U7A/PBCwiuw1o8pYNDSsQ+4=
github.com/go-pg/pg/v10 v10.0.0-beta.2 h1:8tNEJLtOEw5/Df0BLLBOHCiLaYAiu4uhdngjK955MK8=
github.com/go-pg/pg/v10 v10.0.0-beta.2/go.mod h1:UAuqGPC94ySi4rJ3DC5e4SY1rlwugZbJA/XoJ/kf5Rw=
github.com/go-pg/pg/v9 v9.0.0-beta.14/go.mod h1:T2Sr6bpTCOr2lUqOUMiXLMJqZHSUBKk1LdgSqjwhZfA=
github.com/go-pg/pg/v9 v9.0.3/go.mod h1:Tm/Q3Vt6gdQOH6TTN1H/xLlIXc+Qrka7TZ6uREtu/eA=
github.com/go-pg/pg/v9 v9.1.6 h1:IqBayenvp9EWjHncRE7//SRmQuktq60oeO1/MkEx3dY=
@ -53,6 +53,8 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
@ -80,20 +82,18 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/segmentio/encoding v0.1.10/go.mod h1:RWhr02uzMB9gQC1x+MfYxedtmBibb9cZ6Vv9VxRSSbw=
github.com/segmentio/encoding v0.1.12 h1:SwIDXReTDnlYqOcLachzJEczAEihST7Mx7nGlAWCJ3Q=
github.com/segmentio/encoding v0.1.12/go.mod h1:RWhr02uzMB9gQC1x+MfYxedtmBibb9cZ6Vv9VxRSSbw=
github.com/segmentio/encoding v0.1.13 h1:izH8HknGvMZvlqplu+kmCmbsW5VEvz4yBsZpdUUKUDM=
github.com/segmentio/encoding v0.1.13/go.mod h1:RWhr02uzMB9gQC1x+MfYxedtmBibb9cZ6Vv9VxRSSbw=
github.com/segmentio/encoding v0.1.14 h1:BfnglNbNRohLaBLf93uP5/IwKqeWrezXK/g6IRnj75c=
github.com/segmentio/encoding v0.1.14/go.mod h1:RWhr02uzMB9gQC1x+MfYxedtmBibb9cZ6Vv9VxRSSbw=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYmW5DyG0UqvY96Bu5QYsTLvCHdrgo=
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs=
github.com/tribalwarshelp/shared v0.0.0-20200623082627-128bf01a6570 h1:W2cBKRRvr1QLG2GzuJFuvtVe0dblHkkb+E2SlbdJBHo=
github.com/tribalwarshelp/shared v0.0.0-20200623082627-128bf01a6570/go.mod h1:tf+2yTHasV6jAF3V2deZ9slNoCyBzC0fMdTjI7clf6Y=
github.com/tribalwarshelp/shared v0.0.0-20200623144748-aa834a01dce6 h1:WZ1oxHysFtiPjHa2ADUqiGrzwcN3j0YpiVg/V2e/74o=
github.com/tribalwarshelp/shared v0.0.0-20200623144748-aa834a01dce6/go.mod h1:tf+2yTHasV6jAF3V2deZ9slNoCyBzC0fMdTjI7clf6Y=
github.com/tribalwarshelp/shared v0.0.0-20200624134544-636239c5fd17 h1:DUMz3ROe2nYRszZuu97LVIgdPrt8QBWAVouFlrucL8c=
github.com/tribalwarshelp/shared v0.0.0-20200624134544-636239c5fd17/go.mod h1:tf+2yTHasV6jAF3V2deZ9slNoCyBzC0fMdTjI7clf6Y=
github.com/tribalwarshelp/shared v0.0.0-20200625103607-9768e416aea9 h1:CHeznuISvmlW1pYpnA7Bz3nVdQ6kc9ORtGtIKEKH8M8=
github.com/tribalwarshelp/shared v0.0.0-20200625103607-9768e416aea9/go.mod h1:tf+2yTHasV6jAF3V2deZ9slNoCyBzC0fMdTjI7clf6Y=
github.com/vmihailenco/bufpool v0.1.5/go.mod h1:fL9i/PRTuS7AELqAHwSU1Zf1c70xhkhGe/cD5ud9pJk=
github.com/vmihailenco/bufpool v0.1.11 h1:gOq2WmBrq0i2yW5QJ16ykccQ4wH9UyEsgLm6czKAd94=
github.com/vmihailenco/bufpool v0.1.11/go.mod h1:AFf/MOy3l2CFTKbxwt0mp2MwnqjNEs5H/UxrkA5jxTQ=
@ -101,8 +101,8 @@ github.com/vmihailenco/msgpack/v4 v4.3.5/go.mod h1:DuaveEe48abshDmz5UBKyZ+yDugva
github.com/vmihailenco/msgpack/v4 v4.3.7/go.mod h1:Ii+PksJlvFT5ZRcB/4YLAInMIp6a0WOCm0L3BU0aNG4=
github.com/vmihailenco/msgpack/v4 v4.3.11 h1:Q47CePddpNGNhk4GCnAx9DDtASi2rasatE0cd26cZoE=
github.com/vmihailenco/msgpack/v4 v4.3.11/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4=
github.com/vmihailenco/msgpack/v5 v5.0.0-alpha.2 h1:0jVpYJSRJzGY7m21n9V5uIkl7Zre64W8DR1dxEKX2g4=
github.com/vmihailenco/msgpack/v5 v5.0.0-alpha.2/go.mod h1:LDfrk4wJpSFwkzNOJxrCWiSm8c7Iqw/hXNPT2fzQfE8=
github.com/vmihailenco/msgpack/v5 v5.0.0-beta.1 h1:d71/KA0LhvkrJ/Ok+Wx9qK7bU8meKA1Hk0jpVI5kJjk=
github.com/vmihailenco/msgpack/v5 v5.0.0-beta.1/go.mod h1:xlngVLeyQ/Qi05oQxhQ+oTuqa03RjMwMfk/7/TCs+QI=
github.com/vmihailenco/tagparser v0.1.0/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
github.com/vmihailenco/tagparser v0.1.1 h1:quXMXlA39OCbd2wAdTsGDlK9RkOk6Wuw+x37wVyIuWY=
github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
@ -114,8 +114,10 @@ golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3
golang.org/x/crypto v0.0.0-20191029031824-8986dd9e96cf/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20191128160524-b544559bb6d1/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37 h1:cg5LA/zNPRzIXIWSCxQW10Rvpy94aQh3LT/ShoCpkHw=
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9 h1:vEg9joUBmeBcK9iSJftGNf3coIG4HqZElCPehJsfAYM=
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
@ -133,10 +135,10 @@ golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200222033325-078779b8f2d8/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2 h1:eDrdRpKgkcCqKZQwyZRyeFZgfqt37SL7Kv3tok06cKE=
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200528225125-3c3fba18258b h1:IYiJPiJfzktmDAO1HQiwjMjwjlYKHAL7KzeD544RJPs=
golang.org/x/net v0.0.0-20200528225125-3c3fba18258b/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200602114024-627f9648deb9 h1:pNX+40auqi2JqRfOP1akLGtYcn15TUbkhwuCO3foqqM=
golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200625001655-4c5254603344 h1:vGXIOMxbNfDTk/aXCmfdLgkrSV+Z2tcbze+pEc3v5W4=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -148,8 +150,10 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121 h1:rITEj+UZHYC927n8GT97eC3zrpzXdb/voyeOuVKS46o=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980 h1:OjiUf46hAmXblsZdnoSXsEUSKU8r1UEzcL5RVZ4gO9Y=
golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4 h1:5/PjkGUjvEU5Gl6BxmvKRPpqo2uNMv4rcHBMwzk/st8=
golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
@ -177,6 +181,8 @@ google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.29.1 h1:EC2SB8S04d2r73uptxphDSUG+kTKVgjRPF+N3xpxRB4=
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
google.golang.org/grpc v1.30.0 h1:M5a8xTlYTxwMn5ZFkwhRabsygDY5G8TYLyQDBxJNAxE=
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@ -187,6 +193,8 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0 h1:UhZDfRO8JRQru4/+LlLE0BRKGF8L+PICnvYZmx/fEGA=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=