feat: new job - clean up (#147)
All checks were successful
continuous-integration/drone/push Build is passing

Reviewed-on: twhelp/core#147
This commit is contained in:
Dawid Wysokiński 2022-12-27 08:57:44 +00:00
parent b50c3845a3
commit 2e27c63324
30 changed files with 578 additions and 80 deletions

View File

@ -16,7 +16,7 @@ import (
)
const (
jobTimeout = 30 * time.Second
jobDefaultTimeout = 30 * time.Second
)
func New() *cli.Command {
@ -26,6 +26,7 @@ func New() *cli.Command {
Subcommands: []*cli.Command{
newCreateCommand(),
newUpdateCommand(),
newCleanUpCommand(),
},
}
}
@ -39,6 +40,25 @@ func newCreateCommand() *cli.Command {
}
}
func newCreateSnapshotsCommand() *cli.Command {
return &cli.Command{
Name: "snapshots",
Usage: "Launches snapshot update",
Action: func(c *cli.Context) error {
return runJob(
c,
jobDefaultTimeout,
func(ctx context.Context, job *service.Job) error {
if err := job.CreateSnapshots(ctx); err != nil {
return fmt.Errorf("Job.CreateSnapshots: %w", err)
}
return nil
},
)
},
}
}
func newUpdateCommand() *cli.Command {
return &cli.Command{
Name: "update",
@ -56,6 +76,7 @@ func newUpdateDataCommand() *cli.Command {
Action: func(c *cli.Context) error {
return runJob(
c,
jobDefaultTimeout,
func(ctx context.Context, job *service.Job) error {
if err := job.UpdateData(ctx); err != nil {
return fmt.Errorf("Job.UpdateData: %w", err)
@ -74,6 +95,7 @@ func newUpdateEnnoblementsCommand() *cli.Command {
Action: func(c *cli.Context) error {
return runJob(
c,
jobDefaultTimeout,
func(ctx context.Context, job *service.Job) error {
if err := job.UpdateEnnoblements(ctx); err != nil {
return fmt.Errorf("Job.UpdateEnnoblements: %w", err)
@ -85,16 +107,17 @@ func newUpdateEnnoblementsCommand() *cli.Command {
}
}
func newCreateSnapshotsCommand() *cli.Command {
func newCleanUpCommand() *cli.Command {
return &cli.Command{
Name: "snapshots",
Usage: "Launches snapshot update",
Name: "cleanup",
Usage: "Launches database cleanup",
Action: func(c *cli.Context) error {
return runJob(
c,
time.Hour,
func(ctx context.Context, job *service.Job) error {
if err := job.CreateSnapshots(ctx); err != nil {
return fmt.Errorf("Job.CreateSnapshots: %w", err)
if err := job.CleanUp(ctx); err != nil {
return fmt.Errorf("Job.CleanUp: %w", err)
}
return nil
},
@ -105,7 +128,7 @@ func newCreateSnapshotsCommand() *cli.Command {
type runJobFunc func(ctx context.Context, job *service.Job) error
func runJob(c *cli.Context, fn runJobFunc) error {
func runJob(c *cli.Context, timeout time.Duration, fn runJobFunc) error {
db, err := internal.NewBunDB()
if err != nil {
return fmt.Errorf("internal.NewBunDB: %w", err)
@ -120,19 +143,23 @@ func runJob(c *cli.Context, fn runJobFunc) error {
}
marshaler := internal.NewCommandEventMarshaler()
ctx, cancel := context.WithTimeout(c.Context, jobTimeout)
ctx, cancel := context.WithTimeout(c.Context, timeout)
defer cancel()
err = fn(ctx, service.NewJob(
service.NewVersion(bundb.NewVersion(db)),
service.NewServer(bundb.NewServer(db), internal.NewTWClient(c.App.Version)),
client := internal.NewTWClient(c.App.Version)
versionSvc := service.NewVersion(bundb.NewVersion(db))
serverSvc := service.NewServer(bundb.NewServer(db), client)
tribeChangeSvc := service.NewTribeChange(bundb.NewTribeChange(db))
playerSvc := service.NewPlayer(bundb.NewPlayer(db), tribeChangeSvc, client)
playerSnapshotSvc := service.NewPlayerSnapshot(bundb.NewPlayerSnapshot(db), playerSvc)
return fn(ctx, service.NewJob(
versionSvc,
serverSvc,
msg.NewServerPublisher(publisher, marshaler),
msg.NewEnnoblementPublisher(publisher, marshaler),
msg.NewSnapshotPublisher(publisher, marshaler),
[]service.CleanUper{playerSnapshotSvc},
))
if err != nil {
return err
}
return nil
}

View File

@ -251,6 +251,30 @@ func (f *bunfixture) ennoblement(tb testing.TB, id string) domain.Ennoblement {
return e.ToDomain()
}
func (f *bunfixture) playerSnapshots(tb testing.TB) []domain.PlayerSnapshot {
tb.Helper()
//nolint:lll
ids := []string{"pl169-chcesz-remont-2021-09-02", "pl169-chcesz-remont-2021-09-03", "pl169-maddov-2021-09-08", "de188-1scoo-2021-06-25", "de188-1scoo-2021-06-26"}
snapshots := make([]domain.PlayerSnapshot, 0, len(ids))
for _, id := range ids {
snapshots = append(snapshots, f.playerSnapshot(tb, id))
}
return snapshots
}
func (f *bunfixture) playerSnapshot(tb testing.TB, id string) domain.PlayerSnapshot {
tb.Helper()
row, err := f.Row("PlayerSnapshot." + id)
require.NoError(tb, err)
p, ok := row.(*model.PlayerSnapshot)
require.True(tb, ok)
return p.ToDomain()
}
func generateSchema() string {
return strings.TrimFunc(strings.ReplaceAll(uuid.NewString(), "-", "_"), unicode.IsNumber)
}

View File

@ -83,7 +83,7 @@ func TestEnnoblement_Create(t *testing.T) {
for _, p := range params {
var found bool
for _, e := range ennoblements {
p2 := domain.CreateEnnoblementParams{
if cmp.Equal(p, domain.CreateEnnoblementParams{
VillageID: e.VillageID,
NewOwnerID: e.NewOwnerID,
NewTribeID: e.NewTribeID,
@ -92,9 +92,7 @@ func TestEnnoblement_Create(t *testing.T) {
Points: e.Points,
CreatedAt: e.CreatedAt,
ServerKey: e.ServerKey,
}
if cmp.Equal(p, p2, cmpopts.EquateApproxTime(1*time.Second)) {
}, cmpopts.EquateApproxTime(1*time.Second)) {
found = true
break
}

View File

@ -68,34 +68,34 @@ func init() {
return fmt.Errorf("couldn't select servers from the db: %w", err)
}
for _, s := range servers {
for _, srv := range servers {
if _, err = db.NewUpdate().
Model(&model.Player{}).
Set("best_rank = rank").
Set("best_rank_at = ?", s.PlayerDataUpdatedAt).
Set("best_rank_at = ?", srv.PlayerDataUpdatedAt).
Set("most_points = points").
Set("most_points_at = ?", s.PlayerDataUpdatedAt).
Set("most_points_at = ?", srv.PlayerDataUpdatedAt).
Set("most_villages = num_villages").
Set("most_villages_at = ?", s.PlayerDataUpdatedAt).
Set("last_activity_at = ?", s.PlayerDataUpdatedAt).
Set("most_villages_at = ?", srv.PlayerDataUpdatedAt).
Set("last_activity_at = ?", srv.PlayerDataUpdatedAt).
Where("deleted_at IS NULL").
Where("server_key = ?", s.Key).
Where("server_key = ?", srv.Key).
Exec(ctx); err != nil {
return fmt.Errorf("couldn't update players (server=%s): %w", s.Key, err)
return fmt.Errorf("couldn't update players (server=%s): %w", srv.Key, err)
}
if _, err = db.NewUpdate().
Model(&model.Tribe{}).
Set("best_rank = rank").
Set("best_rank_at = ?", s.TribeDataUpdatedAt).
Set("best_rank_at = ?", srv.TribeDataUpdatedAt).
Set("most_points = points").
Set("most_points_at = ?", s.TribeDataUpdatedAt).
Set("most_points_at = ?", srv.TribeDataUpdatedAt).
Set("most_villages = num_villages").
Set("most_villages_at = ?", s.TribeDataUpdatedAt).
Set("most_villages_at = ?", srv.TribeDataUpdatedAt).
Where("deleted_at IS NULL").
Where("server_key = ?", s.Key).
Where("server_key = ?", srv.Key).
Exec(ctx); err != nil {
return fmt.Errorf("couldn't update tribes (server=%s): %w", s.Key, err)
return fmt.Errorf("couldn't update tribes (server=%s): %w", srv.Key, err)
}
}

View File

@ -2,7 +2,10 @@ package bundb
import (
"context"
"database/sql"
"errors"
"fmt"
"time"
"gitea.dwysokinski.me/twhelp/core/internal/bundb/internal/model"
"gitea.dwysokinski.me/twhelp/core/internal/domain"
@ -36,3 +39,55 @@ func (p *PlayerSnapshot) Create(ctx context.Context, params ...domain.CreatePlay
return nil
}
func (p *PlayerSnapshot) List(ctx context.Context, params domain.ListPlayerSnapshotsParams) ([]domain.PlayerSnapshot, error) {
var snapshots []model.PlayerSnapshot
if err := p.db.NewSelect().
Model(&snapshots).
Order("server_key ASC", "date ASC").
Apply(listPlayerSnapshotsParamsApplier{params}.apply).
Scan(ctx); err != nil && !errors.Is(err, sql.ErrNoRows) {
return nil, fmt.Errorf("couldn't select player snapshots from the db: %w", err)
}
result := make([]domain.PlayerSnapshot, 0, len(snapshots))
for _, snapshot := range snapshots {
result = append(result, snapshot.ToDomain())
}
return result, nil
}
func (p *PlayerSnapshot) Delete(ctx context.Context, serverKey string, createdAtLTE time.Time) error {
if _, err := p.db.NewDelete().
Model(&model.PlayerSnapshot{}).
Where("server_key = ?", serverKey).
Where("created_at <= ?", createdAtLTE).
Returning("NULL").
Exec(ctx); err != nil {
return fmt.Errorf("couldn't delete player snapshots: %w", err)
}
return nil
}
type listPlayerSnapshotsParamsApplier struct {
params domain.ListPlayerSnapshotsParams
}
func (l listPlayerSnapshotsParamsApplier) apply(q *bun.SelectQuery) *bun.SelectQuery {
return l.applyPagination(l.applyFilters(q))
}
func (l listPlayerSnapshotsParamsApplier) applyFilters(q *bun.SelectQuery) *bun.SelectQuery {
if l.params.ServerKeys != nil {
q = q.Where("ps.server_key IN (?)", bun.In(l.params.ServerKeys))
}
return q
}
func (l listPlayerSnapshotsParamsApplier) applyPagination(q *bun.SelectQuery) *bun.SelectQuery {
return (paginationApplier{pagination: l.params.Pagination}).apply(q)
}

View File

@ -7,6 +7,8 @@ import (
"gitea.dwysokinski.me/twhelp/core/internal/bundb"
"gitea.dwysokinski.me/twhelp/core/internal/domain"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/jackc/pgerrcode"
"github.com/stretchr/testify/assert"
"github.com/uptrace/bun/driver/pgdriver"
@ -59,6 +61,30 @@ func TestPlayerSnapshot_Create(t *testing.T) {
}
assert.NoError(t, repo.Create(context.Background(), params...))
snapshots, err := repo.List(context.Background(), domain.ListPlayerSnapshotsParams{
ServerKeys: []string{fixture.server(t, "de188").Key},
})
assert.NoError(t, err)
for _, p := range params {
var found bool
for _, s := range snapshots {
if cmp.Equal(p, domain.CreatePlayerSnapshotParams{
OpponentsDefeated: s.OpponentsDefeated,
PlayerID: s.PlayerID,
NumVillages: s.NumVillages,
Points: s.Points,
Rank: s.Rank,
TribeID: s.TribeID,
ServerKey: s.ServerKey,
Date: s.Date,
}, cmpopts.EquateApproxTime(24*time.Hour)) {
found = true
break
}
}
assert.True(t, found)
}
})
t.Run("OK: len(params) == 0", func(t *testing.T) {
@ -102,7 +128,7 @@ func TestPlayerSnapshot_Create(t *testing.T) {
assert.Equal(t, "player_snapshots_player_id_server_key_date_key", pgErr.Field('n'))
})
t.Run("ERR: server must exist in the db", func(t *testing.T) {
t.Run("ERR: server must exist", func(t *testing.T) {
t.Parallel()
playerRiou89 := fixture.player(t, "de188-riou89")
@ -127,7 +153,7 @@ func TestPlayerSnapshot_Create(t *testing.T) {
assert.Equal(t, "player_snapshots_server_key_fkey", pgErr.Field('n'))
})
t.Run("ERR: player must exist in the db", func(t *testing.T) {
t.Run("ERR: player must exist", func(t *testing.T) {
t.Parallel()
playerRiou89 := fixture.player(t, "de188-riou89")
@ -152,3 +178,151 @@ func TestPlayerSnapshot_Create(t *testing.T) {
assert.Equal(t, "player_snapshots_player_id_server_key_fkey", pgErr.Field('n'))
})
}
func TestPlayerSnapshot_List(t *testing.T) {
t.Parallel()
db := newDB(t)
fixture := loadFixtures(t, db)
repo := bundb.NewPlayerSnapshot(db)
snapshots := fixture.playerSnapshots(t)
type expectedSnapshots struct {
id int64
serverKey string
}
allSnapshots := make([]expectedSnapshots, 0, len(snapshots))
for _, p := range snapshots {
allSnapshots = append(allSnapshots, expectedSnapshots{
id: p.ID,
serverKey: p.ServerKey,
})
}
//nolint:prealloc
var snapshotsDE188 []expectedSnapshots
for _, p := range snapshots {
if p.ServerKey != "de188" {
continue
}
snapshotsDE188 = append(snapshotsDE188, expectedSnapshots{
id: p.ID,
serverKey: p.ServerKey,
})
}
tests := []struct {
name string
params domain.ListPlayerSnapshotsParams
expectedSnapshots []expectedSnapshots
}{
{
name: "Empty struct",
params: domain.ListPlayerSnapshotsParams{},
expectedSnapshots: allSnapshots,
},
{
name: "ServerKey=[de188]",
params: domain.ListPlayerSnapshotsParams{
ServerKeys: []string{"de188"},
},
expectedSnapshots: snapshotsDE188,
},
{
name: "ServerKey=[pl169],Limit=2",
params: domain.ListPlayerSnapshotsParams{
ServerKeys: []string{"pl169"},
Pagination: domain.Pagination{
Limit: 2,
},
},
expectedSnapshots: []expectedSnapshots{
{
id: 100000,
serverKey: "pl169",
},
{
id: 100001,
serverKey: "pl169",
},
},
},
{
name: "ServerKey=[pl169],Offset=1",
params: domain.ListPlayerSnapshotsParams{
ServerKeys: []string{"pl169"},
Pagination: domain.Pagination{
Offset: 1,
},
},
expectedSnapshots: []expectedSnapshots{
{
id: 100001,
serverKey: "pl169",
},
{
id: 100050,
serverKey: "pl169",
},
},
},
{
name: "ServerKey=[pl169],Offset=1,Limit=1",
params: domain.ListPlayerSnapshotsParams{
ServerKeys: []string{"pl169"},
Pagination: domain.Pagination{
Limit: 1,
Offset: 1,
},
},
expectedSnapshots: []expectedSnapshots{
{
id: 100001,
serverKey: "pl169",
},
},
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
res, err := repo.List(context.Background(), tt.params)
assert.NoError(t, err)
assert.Len(t, res, len(tt.expectedSnapshots))
for _, expSnapshot := range tt.expectedSnapshots {
found := false
for _, snapshot := range res {
if snapshot.ID == expSnapshot.id && snapshot.ServerKey == expSnapshot.serverKey {
found = true
break
}
}
assert.True(t, found, "snapshot (id=%d,serverkey=%s) not found", expSnapshot.id, expSnapshot.serverKey)
}
})
}
}
func TestPlayerSnapshot_Delete(t *testing.T) {
t.Parallel()
db := newDB(t)
fixture := loadFixtures(t, db)
repo := bundb.NewPlayerSnapshot(db)
snapshotsBeforeDelete, err := repo.List(context.Background(), domain.ListPlayerSnapshotsParams{})
assert.NoError(t, err)
assert.Greater(t, len(snapshotsBeforeDelete), 0)
assert.NoError(t, repo.Delete(context.Background(), fixture.server(t, "pl169").Key, time.Date(2021, time.September, 3, 23, 0, 0, 0, time.UTC)))
snapshotsAfterDelete, err := repo.List(context.Background(), domain.ListPlayerSnapshotsParams{})
assert.NoError(t, err)
assert.Equal(t, len(snapshotsBeforeDelete)-2, len(snapshotsAfterDelete))
}

View File

@ -142,7 +142,7 @@ func TestPlayer_CreateOrUpdate(t *testing.T) {
assert.NoError(t, repo.CreateOrUpdate(context.Background()))
})
t.Run("ERR: server must exist in the db", func(t *testing.T) {
t.Run("ERR: server must exist", func(t *testing.T) {
t.Parallel()
err := repo.CreateOrUpdate(context.Background(), domain.CreatePlayerParams{

View File

@ -7283,3 +7283,95 @@
old_tribe_id: 31
points: 5123
created_at: 2022-04-22T15:00:10.000Z
- model: PlayerSnapshot
rows:
- rank_att: 5
score_att: 38213157
rank_def: 2
score_def: 51137731
rank_sup: 104
score_sup: 2282683
rank_total: 1
score_total: 91633571
_id: pl169-chcesz-remont-2021-09-02
id: 100000
player_id: 6180190
server_key: pl169
num_villages: 665
points: 7298055
rank: 1
tribe_id: 27
date: 2021-09-02T00:00:00.000Z
created_at: 2021-09-02T21:01:03.000Z
- rank_att: 5
score_att: 38213157
rank_def: 2
score_def: 51137731
rank_sup: 104
score_sup: 2282683
rank_total: 1
score_total: 91633571
_id: pl169-chcesz-remont-2021-09-03
id: 100001
player_id: 6180190
server_key: pl169
num_villages: 665
points: 7298055
rank: 1
tribe_id: 27
date: 2021-09-03T00:00:00.000Z
created_at: 2021-09-03T21:01:03.000Z
- rank_att: 13
score_att: 23124653
rank_def: 112
score_def: 5547007
rank_sup: 10
score_sup: 10651344
rank_total: 23
score_total: 39323004
_id: pl169-maddov-2021-09-08
id: 100050
player_id: 8419570
server_key: pl169
num_villages: 643
points: 6292398
rank: 2
tribe_id: 2
date: 2021-09-08T00:00:00.000Z
created_at: 2021-09-08T20:01:11.000Z
- rank_att: 10
score_att: 27638462
rank_def: 35
score_def: 5555868
rank_sup: 3
score_sup: 9815277
rank_total: 16
score_total: 43009607
_id: de188-1scoo-2021-06-25
id: 100100
player_id: 1577279214
server_key: de188
num_villages: 878
points: 9199775
rank: 1
tribe_id: 772
date: 2021-06-25T00:00:00.000Z
created_at: 2021-06-25T11:00:53.000Z
- rank_att: 10
score_att: 27638462
rank_def: 35
score_def: 5555868
rank_sup: 3
score_sup: 9815277
rank_total: 16
score_total: 43009607
_id: de188-1scoo-2021-06-26
id: 100101
player_id: 1577279214
server_key: de188
num_villages: 878
points: 9199775
rank: 1
tribe_id: 772
date: 2021-06-26T00:00:00.000Z
created_at: 2021-06-26T11:00:53.000Z

View File

@ -61,7 +61,7 @@ func TestTribeChange_Create(t *testing.T) {
assert.NoError(t, repo.Create(context.Background()))
})
t.Run("ERR: server must exist in the db", func(t *testing.T) {
t.Run("ERR: server must exist", func(t *testing.T) {
t.Parallel()
serverKey := "random"
@ -83,7 +83,7 @@ func TestTribeChange_Create(t *testing.T) {
assert.Equal(t, "tribe_changes_server_key_fkey", pgErr.Field('n'))
})
t.Run("ERR: player must exist in the db", func(t *testing.T) {
t.Run("ERR: player must exist", func(t *testing.T) {
t.Parallel()
playerRiou89 := fixture.player(t, "de188-riou89")

View File

@ -112,7 +112,7 @@ func TestTribeSnapshot_Create(t *testing.T) {
assert.Equal(t, "tribe_snapshots_tribe_id_server_key_date_key", pgErr.Field('n'))
})
t.Run("ERR: server must exist in the db", func(t *testing.T) {
t.Run("ERR: server must exist", func(t *testing.T) {
t.Parallel()
serverKey := "random"
@ -140,7 +140,7 @@ func TestTribeSnapshot_Create(t *testing.T) {
assert.Equal(t, "tribe_snapshots_server_key_fkey", pgErr.Field('n'))
})
t.Run("ERR: tribe must exist in the db", func(t *testing.T) {
t.Run("ERR: tribe must exist", func(t *testing.T) {
t.Parallel()
tribeTT := fixture.tribe(t, "pl169-tt")

View File

@ -142,7 +142,7 @@ func TestTribe_CreateOrUpdate(t *testing.T) {
assert.NoError(t, repo.CreateOrUpdate(context.Background()))
})
t.Run("ERR: server must exist in the db", func(t *testing.T) {
t.Run("ERR: server must exist", func(t *testing.T) {
t.Parallel()
err := repo.CreateOrUpdate(context.Background(), domain.CreateTribeParams{

View File

@ -96,7 +96,7 @@ func TestVillage_CreateOrUpdate(t *testing.T) {
assert.NoError(t, repo.CreateOrUpdate(context.Background()))
})
t.Run("ERR: server must exist in the db", func(t *testing.T) {
t.Run("ERR: server must exist", func(t *testing.T) {
t.Parallel()
err := repo.CreateOrUpdate(context.Background(), domain.CreateVillageParams{

View File

@ -28,6 +28,11 @@ type CreatePlayerSnapshotParams struct {
Date time.Time
}
type ListPlayerSnapshotsParams struct {
ServerKeys []string
Pagination Pagination
}
type TribeSnapshot struct {
OpponentsDefeated

View File

@ -29,12 +29,17 @@ type SnapshotPublisher interface {
CmdCreateTribes(ctx context.Context, payloads ...domain.CreateSnapshotsCmdPayload) error
}
type CleanUper interface {
CleanUp(ctx context.Context, srv domain.Server) error
}
type Job struct {
versionSvc VersionLister
serverSvc ServerLister
serverPublisher ServerPublisher
ennoblementPublisher EnnoblementPublisher
snapshotPublisher SnapshotPublisher
cleaners []CleanUper
}
func NewJob(
@ -43,6 +48,7 @@ func NewJob(
serverPublisher ServerPublisher,
ennoblementPublisher EnnoblementPublisher,
snapshotPublisher SnapshotPublisher,
cleaners []CleanUper,
) *Job {
return &Job{
versionSvc: versionSvc,
@ -50,6 +56,7 @@ func NewJob(
serverPublisher: serverPublisher,
ennoblementPublisher: ennoblementPublisher,
snapshotPublisher: snapshotPublisher,
cleaners: cleaners,
}
}
@ -201,12 +208,49 @@ func (j *Job) publishCreateSnapshotsCmds(
return nil
}
func (j *Job) CleanUp(ctx context.Context) error {
versions, err := j.versionSvc.List(ctx)
if err != nil {
return fmt.Errorf("VersionService: %w", err)
}
for _, v := range versions {
servers, err := j.serverSvc.List(ctx, domain.ListServersParams{
Special: domain.NullBool{
Bool: false,
Valid: true,
},
Open: domain.NullBool{
Bool: false,
Valid: true,
},
VersionCodes: []string{v.Code},
Pagination: domain.Pagination{
Limit: serverMaxLimit,
},
})
if err != nil {
return fmt.Errorf("%s: ServerService.List: %w", v.Code, err)
}
for _, srv := range servers {
for _, c := range j.cleaners {
if err := c.CleanUp(ctx, srv); err != nil {
return fmt.Errorf("%s: %w", srv.Key, err)
}
}
}
}
return nil
}
func newCreateSnapshotsCmdPayloads(servers []domain.Server, date time.Time) []domain.CreateSnapshotsCmdPayload {
payloads := make([]domain.CreateSnapshotsCmdPayload, 0, len(servers))
for _, s := range servers {
for _, srv := range servers {
payloads = append(payloads, domain.CreateSnapshotsCmdPayload{
Key: s.Key,
VersionCode: s.VersionCode,
Key: srv.Key,
VersionCode: srv.VersionCode,
Date: date,
})
}

View File

@ -16,6 +16,7 @@ type PlayerLister interface {
//counterfeiter:generate -o internal/mock/player_snapshot_repository.gen.go . PlayerSnapshotRepository
type PlayerSnapshotRepository interface {
Create(ctx context.Context, params ...domain.CreatePlayerSnapshotParams) error
Delete(ctx context.Context, serverKey string, createdAtLTE time.Time) error
}
type PlayerSnapshot struct {
@ -78,3 +79,16 @@ func (p *PlayerSnapshot) Create(ctx context.Context, key string, date time.Time)
return nil
}
func (p *PlayerSnapshot) CleanUp(ctx context.Context, srv domain.Server) error {
if srv.PlayerSnapshotsCreatedAt.After(time.Now().Add(-30 * 24 * time.Hour)) { // 30 days
return nil
}
// delete snapshots older than 6 months
if err := p.repo.Delete(ctx, srv.Key, time.Now().Add(-180*24*time.Hour)); err != nil {
return fmt.Errorf("couldn't delete old player snapshots: %w", err)
}
return nil
}

View File

@ -82,3 +82,29 @@ func TestPlayerSnapshot_Create(t *testing.T) {
assert.Equal(t, date, params[i].Date)
}
}
func TestPlayerSnapshot_CleanUp(t *testing.T) {
t.Parallel()
serverKey := "pl151"
playerSvc := &mock.FakePlayerLister{}
repo := &mock.FakePlayerSnapshotRepository{}
repo.DeleteReturns(nil)
svc := service.NewPlayerSnapshot(repo, playerSvc)
assert.NoError(t, svc.CleanUp(context.Background(), domain.Server{
Key: serverKey,
PlayerSnapshotsCreatedAt: time.Now(),
}))
assert.Equal(t, 0, repo.DeleteCallCount()) // only servers with PlayerSnapshotsCreatedAt < now - 30 days
assert.NoError(t, svc.CleanUp(context.Background(), domain.Server{
Key: serverKey,
PlayerSnapshotsCreatedAt: time.Now().Add(-30 * 24 * time.Hour),
}))
require.Equal(t, 1, repo.DeleteCallCount())
_, argsServerKey, argsCreatedAtLTE := repo.DeleteArgsForCall(0)
assert.Equal(t, serverKey, argsServerKey)
assert.WithinDuration(t, time.Now().Add(-180*24*time.Hour), argsCreatedAtLTE, time.Second)
}

View File

@ -30,8 +30,6 @@ spec:
value: "10"
- name: DB_MAX_IDLE_CONNECTIONS
value: "5"
- name: OTEL_ENABLED
value: "false"
- name: API_SWAGGER_ENABLED
value: "true"
livenessProbe:

View File

@ -32,8 +32,6 @@ spec:
secretKeyRef:
name: twhelp-secret
key: amqp-uri
- name: OTEL_ENABLED
value: "false"
livenessProbe:
exec:
command: [ "cat", "/tmp/healthy" ]

View File

@ -33,8 +33,6 @@ spec:
secretKeyRef:
name: twhelp-secret
key: amqp-uri
- name: OTEL_ENABLED
value: "false"
resources:
requests:
cpu: 50m
@ -79,8 +77,6 @@ spec:
secretKeyRef:
name: twhelp-secret
key: amqp-uri
- name: OTEL_ENABLED
value: "false"
resources:
requests:
cpu: 50m
@ -125,8 +121,50 @@ spec:
secretKeyRef:
name: twhelp-secret
key: amqp-uri
- name: OTEL_ENABLED
value: "false"
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
cpu: 100m
memory: 128Mi
---
apiVersion: batch/v1
kind: CronJob
metadata:
name: twhelp-job-clean-up
spec:
schedule: "35 0 * * *"
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 3
concurrencyPolicy: Forbid
jobTemplate:
spec:
parallelism: 1
template:
spec:
restartPolicy: Never
containers:
- name: twhelp-job-clean-up
image: twhelp
args: ["job", "cleanup"]
env:
- name: APP_MODE
value: development
- name: DB_DSN
valueFrom:
secretKeyRef:
name: twhelp-secret
key: db-dsn
- name: DB_MAX_OPEN_CONNECTIONS
value: "2"
- name: DB_MAX_IDLE_CONNECTIONS
value: "2"
- name: AMQP_URI
valueFrom:
secretKeyRef:
name: twhelp-secret
key: amqp-uri
resources:
requests:
cpu: 50m

View File

@ -32,8 +32,6 @@ spec:
secretKeyRef:
name: twhelp-secret
key: amqp-uri
- name: OTEL_ENABLED
value: "false"
livenessProbe:
exec:
command: [ "cat", "/tmp/healthy" ]

View File

@ -32,8 +32,6 @@ spec:
secretKeyRef:
name: twhelp-secret
key: amqp-uri
- name: OTEL_ENABLED
value: "false"
livenessProbe:
exec:
command: [ "cat", "/tmp/healthy" ]

View File

@ -32,8 +32,6 @@ spec:
secretKeyRef:
name: twhelp-secret
key: amqp-uri
- name: OTEL_ENABLED
value: "false"
livenessProbe:
exec:
command: [ "cat", "/tmp/healthy" ]

View File

@ -32,8 +32,6 @@ spec:
secretKeyRef:
name: twhelp-secret
key: amqp-uri
- name: OTEL_ENABLED
value: "false"
livenessProbe:
exec:
command: [ "cat", "/tmp/healthy" ]

View File

@ -20,8 +20,6 @@ spec:
value: "12"
- name: DB_MAX_IDLE_CONNECTIONS
value: "8"
- name: OTEL_ENABLED
value: "false"
- name: API_SWAGGER_ENABLED
value: "true"
- name: API_SWAGGER_HOST

View File

@ -26,5 +26,3 @@ spec:
secretKeyRef:
name: twhelp-secret
key: amqp-uri
- name: OTEL_ENABLED
value: "false"

View File

@ -28,8 +28,6 @@ spec:
secretKeyRef:
name: twhelp-secret
key: amqp-uri
- name: OTEL_ENABLED
value: "false"
---
apiVersion: batch/v1
kind: CronJob
@ -61,8 +59,6 @@ spec:
secretKeyRef:
name: twhelp-secret
key: amqp-uri
- name: OTEL_ENABLED
value: "false"
---
apiVersion: batch/v1
kind: CronJob
@ -94,8 +90,37 @@ spec:
secretKeyRef:
name: twhelp-secret
key: amqp-uri
- name: OTEL_ENABLED
value: "false"
---
apiVersion: batch/v1
kind: CronJob
metadata:
name: twhelp-job-clean-up
spec:
jobTemplate:
spec:
template:
spec:
restartPolicy: Never
containers:
- name: twhelp-job-clean-up
image: twhelp
env:
- name: APP_MODE
value: production
- name: DB_DSN
valueFrom:
secretKeyRef:
name: twhelp-secret
key: db-dsn
- name: DB_MAX_OPEN_CONNECTIONS
value: "2"
- name: DB_MAX_IDLE_CONNECTIONS
value: "2"
- name: AMQP_URI
valueFrom:
secretKeyRef:
name: twhelp-secret
key: amqp-uri
---
apiVersion: batch/v1
kind: Job

View File

@ -25,5 +25,3 @@ spec:
secretKeyRef:
name: twhelp-secret
key: amqp-uri
- name: OTEL_ENABLED
value: "false"

View File

@ -25,5 +25,3 @@ spec:
secretKeyRef:
name: twhelp-secret
key: amqp-uri
- name: OTEL_ENABLED
value: "false"

View File

@ -25,5 +25,3 @@ spec:
secretKeyRef:
name: twhelp-secret
key: amqp-uri
- name: OTEL_ENABLED
value: "false"

View File

@ -26,5 +26,3 @@ spec:
secretKeyRef:
name: twhelp-secret
key: amqp-uri
- name: OTEL_ENABLED
value: "false"