mirror of https://github.com/hashicorp/boundary
The purge package contains a job that is used to purge records from the foo_deleted tables when they become older than 30 days.pull/4202/head
parent
78541ad05e
commit
7c996d0e02
@ -0,0 +1,53 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
// Package purge implements a scheduler job used to purge old
|
||||
// rows from the deleted IDs tables kept for pagination purposes.
|
||||
package purge
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/hashicorp/boundary/internal/errors"
|
||||
"github.com/hashicorp/boundary/internal/scheduler"
|
||||
"github.com/hashicorp/boundary/internal/util"
|
||||
)
|
||||
|
||||
// RegisterJobs registers the purge job for each deletion table with the provided scheduler.
|
||||
func RegisterJobs(ctx context.Context, s *scheduler.Scheduler, r db.Reader, w db.Writer) error {
|
||||
const op = "purge.RegisterJobs"
|
||||
if s == nil {
|
||||
return errors.New(ctx, errors.InvalidParameter, "nil scheduler", op, errors.WithoutEvent())
|
||||
}
|
||||
if util.IsNil(r) {
|
||||
return errors.New(ctx, errors.Internal, "nil DB reader", op, errors.WithoutEvent())
|
||||
}
|
||||
if util.IsNil(w) {
|
||||
return errors.New(ctx, errors.Internal, "nil DB writer", op, errors.WithoutEvent())
|
||||
}
|
||||
|
||||
rows, err := r.Query(ctx, getDeletionTablesQuery, nil)
|
||||
if err != nil {
|
||||
return errors.Wrap(ctx, err, op, errors.WithMsg("unable to query for deletion tables"))
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var table string
|
||||
err = rows.Scan(&table)
|
||||
if err != nil {
|
||||
return errors.Wrap(ctx, err, op, errors.WithMsg("unable to scan rows for deletion tables"))
|
||||
}
|
||||
|
||||
purgeJob, err := newPurgeJob(ctx, w, table)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating purge job: %w", err)
|
||||
}
|
||||
if err := s.RegisterJob(ctx, purgeJob); err != nil {
|
||||
return errors.Wrap(ctx, err, op)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -0,0 +1,126 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package purge
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/hashicorp/boundary/internal/errors"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestPurgeTables(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
rw := db.New(conn)
|
||||
|
||||
db, err := conn.SqlDB(ctx)
|
||||
if err != nil {
|
||||
t.Errorf("error getting db connection %s", err)
|
||||
}
|
||||
|
||||
rows, err := db.Query("select get_deletion_tables()")
|
||||
if err != nil {
|
||||
t.Errorf("unable to query for deletion tables %s", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var table string
|
||||
err = rows.Scan(&table)
|
||||
if err != nil {
|
||||
t.Errorf("unable to scan rows for deletion tables %s", err)
|
||||
}
|
||||
_, err = db.Exec(fmt.Sprintf("insert into %s (public_id, delete_time) values ('p1234567890', $1)", table), time.Now())
|
||||
if err != nil {
|
||||
t.Errorf("error updating %s %s", table, err)
|
||||
}
|
||||
_, err = db.Exec(fmt.Sprintf("insert into %s (public_id, delete_time) values ('p9876543210', $1)", table), time.Now().AddDate(0, -2, 0))
|
||||
if err != nil {
|
||||
t.Errorf("error updating %s %s", table, err)
|
||||
}
|
||||
|
||||
query := fmt.Sprintf("delete from %s where delete_time < now() - interval '30 days'", table)
|
||||
sJob := purgeJob{
|
||||
w: rw,
|
||||
table: table,
|
||||
query: query,
|
||||
}
|
||||
|
||||
err = sJob.Run(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
var count int
|
||||
err = db.QueryRowContext(ctx, fmt.Sprintf("select count(public_id) from %s", table)).Scan(&count)
|
||||
if err != nil {
|
||||
t.Errorf("error checking %s table %s", table, err)
|
||||
}
|
||||
require.Equal(t, 1, count)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewPurgeJob(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
rw := db.New(conn)
|
||||
|
||||
type args struct {
|
||||
w db.Writer
|
||||
table string
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantIsErr errors.Code
|
||||
wantErrMsg string
|
||||
}{
|
||||
{
|
||||
name: "valid",
|
||||
args: args{
|
||||
w: rw,
|
||||
table: "valid-table",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "nil-writer",
|
||||
args: args{
|
||||
w: nil,
|
||||
table: "valid-table",
|
||||
},
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
wantErrMsg: "purgeJob.newPurgeJob: missing db.Writer: parameter violation: error #100",
|
||||
},
|
||||
{
|
||||
name: "no table",
|
||||
args: args{
|
||||
w: rw,
|
||||
table: "",
|
||||
},
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
wantErrMsg: "purgeJob.newPurgeJob: missing table: parameter violation: error #100",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
got, err := newPurgeJob(ctx, tt.args.w, tt.args.table)
|
||||
if tt.wantIsErr != 0 {
|
||||
assert.Truef(errors.Match(errors.T(tt.wantIsErr), err), "Unexpected error %s", err)
|
||||
assert.Equal(tt.wantErrMsg, err.Error())
|
||||
return
|
||||
}
|
||||
assert.NoError(err)
|
||||
require.NotNil(got)
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package purge
|
||||
|
||||
const (
|
||||
getDeletionTablesQuery = `
|
||||
select get_deletion_tables();
|
||||
`
|
||||
deleteQueryTemplate = `
|
||||
delete from %s where delete_time < now() - interval '30 days'
|
||||
`
|
||||
)
|
||||
Loading…
Reference in new issue