refact(billing): Use sql functions instead of views

These functions will ensure that UTC time is used for billing queries,
eliminating the need for the repository to set the timezone. This also
means the repository no longern needs a writer.
This commit is contained in:
Timothy Messier committed 2024-02-23 15:44:09 +00:00
1 parent 7c14e42897
commit e09bf09a95
5 files changed
+27 -57

No files matched your search

+3 -6
View File
@@ -6,17 +6,14 @@ package billing
const (
activeUsersLastTwoMonthsQuery = `
select *
from hcp_billing_monthly_active_users_last_2_months
from hcp_billing_monthly_active_users_last_2_months();
`
activeUsersWithStartTimeQuery = `
select *
from hcp_billing_monthly_active_users_all
where start_time >= @start_time
from hcp_billing_monthly_active_users_all(@start_time);
`
activeUsersWithStartTimeAndEndTimeQuery = `
select *
from hcp_billing_monthly_active_users_all
where start_time >= @start_time
and end_time < @end_time
from hcp_billing_monthly_active_users_all(@start_time, @end_time);
`
)
+21 -35
View File
@@ -24,23 +24,19 @@ import (
// cumulative count up to the present date.
type Repository struct {
reader db.Reader
writer db.Writer
}
// NewRepository creates a new Repository. The returned repository is not safe for concurrent go
// routines to access it.
func NewRepository(ctx context.Context, r db.Reader, w db.Writer) (*Repository, error) {
func NewRepository(ctx context.Context, r db.Reader) (*Repository, error) {
const op = "billing.NewRepository"
switch {
case r == nil:
return nil, errors.New(ctx, errors.InvalidParameter, op, "nil db reader")
case w == nil:
return nil, errors.New(ctx, errors.InvalidParameter, op, "nil db writer")
}
return &Repository{
reader: r,
writer: w,
}, nil
}
@@ -81,40 +77,30 @@ func (r *Repository) MonthlyActiveUsers(ctx context.Context, opt ...Option) ([]A
}
var activeUsers []ActiveUsers
if _, err := r.writer.DoTx(ctx, db.StdRetryCnt, db.ExpBackoff{}, func(r db.Reader, w db.Writer) error {
_, err := w.Exec(ctx, `set timezone to 'utc'`, nil)
if err != nil {
return err
rows, err := r.reader.Query(ctx, query, args)
if err != nil {
return nil, err
}
defer rows.Close()
if err := rows.Err(); err != nil {
return nil, err
}
for rows.Next() {
var startTime time.Time
var endTime time.Time
var count uint64
if err := rows.Scan(&startTime, &endTime, &count); err != nil {
return nil, err
}
rows, err := r.Query(ctx, query, args)
if err != nil {
return err
// set start and end times to be in UTC
auUTC := ActiveUsers{
ActiveUsersCount: count,
StartTime: startTime.UTC(),
EndTime: endTime.UTC(),
}
defer rows.Close()
if err := rows.Err(); err != nil {
return err
}
for rows.Next() {
var start_time time.Time
var end_time time.Time
var count uint64
if err := rows.Scan(&start_time, &end_time, &count); err != nil {
return err
}
activeUsers = append(activeUsers, auUTC)
// set start and end times to be in UTC
auUTC := ActiveUsers{
ActiveUsersCount: count,
StartTime: start_time.UTC(),
EndTime: end_time.UTC(),
}
activeUsers = append(activeUsers, auUTC)
}
return nil
}); err != nil {
return nil, errors.Wrap(ctx, err, op)
}
return activeUsers, nil
+1 -14
View File
@@ -35,39 +35,26 @@ func TestRepository_New(t *testing.T) {
name: "valid",
args: args{
r: rw,
w: rw,
},
want: &Repository{
reader: rw,
writer: rw,
},
},
{
name: "nil-reader",
args: args{
r: nil,
w: rw,
},
want: nil,
wantIsErr: errors.InvalidParameter,
wantErrMsg: "billing.NewRepository: nil db reader: parameter violation: error #100",
},
{
name: "nil-writer",
args: args{
r: rw,
w: nil,
},
want: nil,
wantIsErr: errors.InvalidParameter,
wantErrMsg: "billing.NewRepository: nil db writer: parameter violation: error #100",
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
assert := assert.New(t)
got, err := NewRepository(context.Background(), tt.args.r, tt.args.w)
got, err := NewRepository(context.Background(), tt.args.r)
if tt.wantIsErr != 0 {
assert.Truef(errors.Match(errors.T(tt.wantIsErr), err), "Unexpected error %s", err)
assert.Equal(tt.wantErrMsg, err.Error())
+1 -1
View File
@@ -88,7 +88,7 @@ func TestRepo(t testing.TB, conn *db.DB) *Repository {
require := require.New(t)
rw := db.New(conn)
repo, err := NewRepository(ctx, rw, rw)
repo, err := NewRepository(ctx, rw)
require.NoError(err)
return repo
}
+1 -1
View File
@@ -451,7 +451,7 @@ func New(ctx context.Context, conf *Config) (*Controller, error) {
return server.NewRepositoryStorage(ctx, dbase, dbase, c.kms)
}
c.BillingRepoFn = func() (*billing.Repository, error) {
return billing.NewRepository(ctx, dbase, dbase)
return billing.NewRepository(ctx, dbase)
}
// Check that credentials are available at startup, to avoid some harmless