Skip to content

Commit

Permalink
Add monitoring to db queries.
Browse files Browse the repository at this point in the history
  • Loading branch information
alesstimec committed Jun 11, 2024
1 parent c706fa5 commit d1215ee
Show file tree
Hide file tree
Showing 17 changed files with 600 additions and 137 deletions.
42 changes: 36 additions & 6 deletions internal/db/applicationoffer.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,21 @@ import (

"github.com/canonical/jimm/internal/dbmodel"
"github.com/canonical/jimm/internal/errors"
"github.com/canonical/jimm/internal/servermon"
)

// AddApplicationOffer stores the application offer information.
func (d *Database) AddApplicationOffer(ctx context.Context, offer *dbmodel.ApplicationOffer) error {
func (d *Database) AddApplicationOffer(ctx context.Context, offer *dbmodel.ApplicationOffer) (err error) {
const op = errors.Op("db.AddApplicationOffer")

if err := d.ready(); err != nil {
return errors.E(op, err)
}

durationObserver := servermon.DurationObserver(servermon.QueryDurationHistogram, string(op))
defer durationObserver()
defer servermon.ErrorCounter(servermon.QueryErrorCount, &err, string(op))

db := d.DB.WithContext(ctx)

result := db.Create(offer)
Expand All @@ -27,13 +34,19 @@ func (d *Database) AddApplicationOffer(ctx context.Context, offer *dbmodel.Appli
}

// UpdateApplicationOffer updates the application offer information.
func (d *Database) UpdateApplicationOffer(ctx context.Context, offer *dbmodel.ApplicationOffer) error {
func (d *Database) UpdateApplicationOffer(ctx context.Context, offer *dbmodel.ApplicationOffer) (err error) {
const op = errors.Op("db.UpdateApplicationOffer")

if err := d.ready(); err != nil {
return errors.E(op, err)
}

durationObserver := servermon.DurationObserver(servermon.QueryDurationHistogram, string(op))
defer durationObserver()
defer servermon.ErrorCounter(servermon.QueryErrorCount, &err, string(op))

db := d.DB.WithContext(ctx)
err := db.Transaction(func(tx *gorm.DB) error {
err = db.Transaction(func(tx *gorm.DB) error {
tx.Omit("Connections", "Endpoints", "Spaces").Save(offer)
tx.Model(offer).Association("Connections").Replace(offer.Connections)
tx.Model(offer).Association("Endpoints").Replace(offer.Endpoints)
Expand All @@ -53,11 +66,16 @@ func (d *Database) UpdateApplicationOffer(ctx context.Context, offer *dbmodel.Ap

// GetApplicationOffer returns application offer information based on the
// offer UUID or URL.
func (d *Database) GetApplicationOffer(ctx context.Context, offer *dbmodel.ApplicationOffer) error {
func (d *Database) GetApplicationOffer(ctx context.Context, offer *dbmodel.ApplicationOffer) (err error) {
const op = errors.Op("db.GetApplicationOffer")

if err := d.ready(); err != nil {
return errors.E(op, err)
}

durationObserver := servermon.DurationObserver(servermon.QueryDurationHistogram, string(op))
defer durationObserver()
defer servermon.ErrorCounter(servermon.QueryErrorCount, &err, string(op))
db := d.DB.WithContext(ctx)

if offer.UUID != "" {
Expand All @@ -82,11 +100,17 @@ func (d *Database) GetApplicationOffer(ctx context.Context, offer *dbmodel.Appli
}

// DeleteApplicationOffer deletes the application offer.
func (d *Database) DeleteApplicationOffer(ctx context.Context, offer *dbmodel.ApplicationOffer) error {
func (d *Database) DeleteApplicationOffer(ctx context.Context, offer *dbmodel.ApplicationOffer) (err error) {
const op = errors.Op("db.DeleteApplicationOffer")

if err := d.ready(); err != nil {
return errors.E(op, err)
}

durationObserver := servermon.DurationObserver(servermon.QueryDurationHistogram, string(op))
defer durationObserver()
defer servermon.ErrorCounter(servermon.QueryErrorCount, &err, string(op))

db := d.DB.WithContext(ctx)

result := db.Delete(offer)
Expand Down Expand Up @@ -153,14 +177,20 @@ func ApplicationOfferFilterByEndpoint(endpoint dbmodel.ApplicationOfferRemoteEnd
}

// FindApplicationOffers returns application offers matching criteria specified by the filters.
func (d *Database) FindApplicationOffers(ctx context.Context, filters ...ApplicationOfferFilter) ([]dbmodel.ApplicationOffer, error) {
func (d *Database) FindApplicationOffers(ctx context.Context, filters ...ApplicationOfferFilter) (_ []dbmodel.ApplicationOffer, err error) {
const op = errors.Op("db.FindApplicationOffer")

if len(filters) == 0 {
return nil, errors.E(op, errors.CodeBadRequest, "no filters specified")
}
if err := d.ready(); err != nil {
return nil, errors.E(op, err)
}

durationObserver := servermon.DurationObserver(servermon.QueryDurationHistogram, string(op))
defer durationObserver()
defer servermon.ErrorCounter(servermon.QueryErrorCount, &err, string(op))

db := d.DB.WithContext(ctx)
db = db.Table("application_offers AS offers")

Expand Down
26 changes: 21 additions & 5 deletions internal/db/audit.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,17 @@ import (
)

// AddAuditLogEntry adds a new entry to the audit log.
func (d *Database) AddAuditLogEntry(ctx context.Context, ale *dbmodel.AuditLogEntry) error {
func (d *Database) AddAuditLogEntry(ctx context.Context, ale *dbmodel.AuditLogEntry) (err error) {
const op = errors.Op("db.AddAuditLogEntry")

if err := d.ready(); err != nil {
return errors.E(op, err)
}

durationObserver := servermon.DurationObserver(servermon.QueryDurationHistogram, string(op))
defer durationObserver()
defer servermon.ErrorCounter(servermon.QueryErrorCount, &err, string(op))

if err := d.DB.WithContext(ctx).Create(ale).Error; err != nil {
return errors.E(op, dbError(err))
}
Expand Down Expand Up @@ -64,12 +69,16 @@ type AuditLogFilter struct {
// ForEachAuditLogEntry iterates through all audit log entries that match
// the given filter calling f for each entry. If f returns an error
// iteration stops immediately and the error is retuned unmodified.
func (d *Database) ForEachAuditLogEntry(ctx context.Context, filter AuditLogFilter, f func(*dbmodel.AuditLogEntry) error) error {
func (d *Database) ForEachAuditLogEntry(ctx context.Context, filter AuditLogFilter, f func(*dbmodel.AuditLogEntry) error) (err error) {
const op = errors.Op("db.ForEachAuditLogEntry")
if err := d.ready(); err != nil {
return errors.E(op, err)
}

durationObserver := servermon.DurationObserver(servermon.QueryDurationHistogram, string(op))
defer durationObserver()
defer servermon.ErrorCounter(servermon.QueryErrorCount, &err, string(op))

db := d.DB.WithContext(ctx).Model(&dbmodel.AuditLogEntry{})
if !filter.Start.IsZero() {
db = db.Where("time >= ?", filter.Start)
Expand Down Expand Up @@ -114,15 +123,22 @@ func (d *Database) ForEachAuditLogEntry(ctx context.Context, filter AuditLogFilt

// CleanupAuditLogs cleans up audit logs after the auditLogRetentionPeriodInDays,
// HARD deleting them from the database.
func (d *Database) DeleteAuditLogsBefore(ctx context.Context, before time.Time) (int64, error) {
func (d *Database) DeleteAuditLogsBefore(ctx context.Context, before time.Time) (_ int64, err error) {
const op = errors.Op("db.DeleteAuditLogsBefore")
now := time.Now()

if err := d.ready(); err != nil {
return 0, errors.E(op, err)
}

durationObserver := servermon.DurationObserver(servermon.QueryDurationHistogram, string(op))
defer durationObserver()
defer servermon.ErrorCounter(servermon.QueryErrorCount, &err, string(op))

tx := d.DB.
WithContext(ctx).
Unscoped().
Where("time < ?", before).
Delete(&dbmodel.AuditLogEntry{})
servermon.QueryTimeAuditLogCleanUpHistogram.Observe(time.Since(now).Seconds())
if tx.Error != nil {
return 0, errors.E(op, dbError(tx.Error))
}
Expand Down
51 changes: 42 additions & 9 deletions internal/db/cloud.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,22 @@ import (

"github.com/canonical/jimm/internal/dbmodel"
"github.com/canonical/jimm/internal/errors"
"github.com/canonical/jimm/internal/servermon"
)

// AddCloud adds the given cloud to the database. AddCloud returns an error
// with a code of CodeAlreadyExists if there is already a cloud with the
// same name.
func (d *Database) AddCloud(ctx context.Context, c *dbmodel.Cloud) error {
func (d *Database) AddCloud(ctx context.Context, c *dbmodel.Cloud) (err error) {
const op = errors.Op("db.AddCloud")
if err := d.ready(); err != nil {
return errors.E(op, err)
}

durationObserver := servermon.DurationObserver(servermon.QueryDurationHistogram, string(op))
defer durationObserver()
defer servermon.ErrorCounter(servermon.QueryErrorCount, &err, string(op))

db := d.DB.WithContext(ctx)
if err := db.Create(c).Error; err != nil {
err := dbError(err)
Expand All @@ -35,12 +40,16 @@ func (d *Database) AddCloud(ctx context.Context, c *dbmodel.Cloud) error {
// GetCloud fills in the given cloud document based on the cloud name. If
// no cloud is found with the matching name then an error with a code of
// CodeNotFound will be returned.
func (d *Database) GetCloud(ctx context.Context, c *dbmodel.Cloud) error {
func (d *Database) GetCloud(ctx context.Context, c *dbmodel.Cloud) (err error) {
const op = errors.Op("db.GetCloud")
if err := d.ready(); err != nil {
return errors.E(op, err)
}

durationObserver := servermon.DurationObserver(servermon.QueryDurationHistogram, string(op))
defer durationObserver()
defer servermon.ErrorCounter(servermon.QueryErrorCount, &err, string(op))

db := d.DB.WithContext(ctx)
db = db.Where("name = ?", c.Name)
db = preloadCloud("", db)
Expand All @@ -55,12 +64,16 @@ func (d *Database) GetCloud(ctx context.Context, c *dbmodel.Cloud) error {
}

// GetClouds retrieves all the clouds from the database.
func (d *Database) GetClouds(ctx context.Context) ([]dbmodel.Cloud, error) {
func (d *Database) GetClouds(ctx context.Context) (_ []dbmodel.Cloud, err error) {
const op = errors.Op("db.GetClouds")
if err := d.ready(); err != nil {
return nil, errors.E(op, err)
}

durationObserver := servermon.DurationObserver(servermon.QueryDurationHistogram, string(op))
defer durationObserver()
defer servermon.ErrorCounter(servermon.QueryErrorCount, &err, string(op))

var clouds []dbmodel.Cloud
db := d.DB.WithContext(ctx)
db = preloadCloud("", db)
Expand All @@ -73,13 +86,17 @@ func (d *Database) GetClouds(ctx context.Context) ([]dbmodel.Cloud, error) {
// UpdateCloud updates the database definition of the cloud to match the
// given cloud. UpdateCloud does not update any user information, nor does
// it remove any information - this is an additive method.
func (d *Database) UpdateCloud(ctx context.Context, c *dbmodel.Cloud) error {
func (d *Database) UpdateCloud(ctx context.Context, c *dbmodel.Cloud) (err error) {
const op = errors.Op("db.UpdateCloud")
if err := d.ready(); err != nil {
return errors.E(op, err)
}

err := d.DB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
durationObserver := servermon.DurationObserver(servermon.QueryDurationHistogram, string(op))
defer durationObserver()
defer servermon.ErrorCounter(servermon.QueryErrorCount, &err, string(op))

err = d.DB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
if err := tx.Save(c).Error; err != nil {
return err
}
Expand Down Expand Up @@ -114,12 +131,16 @@ func preloadCloud(prefix string, db *gorm.DB) *gorm.DB {
// AddCloudRegion adds a new cloud-region to a cloud. AddCloudRegion
// returns an error with a code of CodeAlreadyExists if there is already a
// region with the same name on the cloud.
func (d *Database) AddCloudRegion(ctx context.Context, cr *dbmodel.CloudRegion) error {
func (d *Database) AddCloudRegion(ctx context.Context, cr *dbmodel.CloudRegion) (err error) {
const op = errors.Op("db.AddCloudRegion")
if err := d.ready(); err != nil {
return errors.E(op, err)
}

durationObserver := servermon.DurationObserver(servermon.QueryDurationHistogram, string(op))
defer durationObserver()
defer servermon.ErrorCounter(servermon.QueryErrorCount, &err, string(op))

db := d.DB.WithContext(ctx)
if err := db.Create(cr).Error; err != nil {
err := dbError(err)
Expand All @@ -133,12 +154,16 @@ func (d *Database) AddCloudRegion(ctx context.Context, cr *dbmodel.CloudRegion)

// FindRegion finds a region with the given name on a cloud with the given
// provider type.
func (d *Database) FindRegion(ctx context.Context, providerType, name string) (*dbmodel.CloudRegion, error) {
func (d *Database) FindRegion(ctx context.Context, providerType, name string) (_ *dbmodel.CloudRegion, err error) {
const op = errors.Op("db.FindRegion")
if err := d.ready(); err != nil {
return nil, errors.E(op, err)
}

durationObserver := servermon.DurationObserver(servermon.QueryDurationHistogram, string(op))
defer durationObserver()
defer servermon.ErrorCounter(servermon.QueryErrorCount, &err, string(op))

db := d.DB.WithContext(ctx)
db = db.Preload("Cloud").Preload("Controllers").Preload("Controllers.Controller")
db = db.Model(dbmodel.CloudRegion{}).Joins("INNER JOIN clouds ON clouds.name = cloud_regions.cloud_name").Where("clouds.type = ? AND cloud_regions.name = ?", providerType, name)
Expand All @@ -151,13 +176,17 @@ func (d *Database) FindRegion(ctx context.Context, providerType, name string) (*
}

// DeleteCloud deletes the given cloud.
func (d *Database) DeleteCloud(ctx context.Context, c *dbmodel.Cloud) error {
func (d *Database) DeleteCloud(ctx context.Context, c *dbmodel.Cloud) (err error) {
const op = errors.Op("db.DeleteCloud")

if err := d.ready(); err != nil {
return errors.E(op, err)
}

durationObserver := servermon.DurationObserver(servermon.QueryDurationHistogram, string(op))
defer durationObserver()
defer servermon.ErrorCounter(servermon.QueryErrorCount, &err, string(op))

db := d.DB.WithContext(ctx)
if err := db.Delete(c).Error; err != nil {
return errors.E(op, dbError(err))
Expand All @@ -166,13 +195,17 @@ func (d *Database) DeleteCloud(ctx context.Context, c *dbmodel.Cloud) error {
}

// DeleteCloudRegionControllerPriority deletes the given cloud region controller priority entry.
func (d *Database) DeleteCloudRegionControllerPriority(ctx context.Context, c *dbmodel.CloudRegionControllerPriority) error {
func (d *Database) DeleteCloudRegionControllerPriority(ctx context.Context, c *dbmodel.CloudRegionControllerPriority) (err error) {
const op = errors.Op("db.DeleteCloudRegionControllerPriority")

if err := d.ready(); err != nil {
return errors.E(op, err)
}

durationObserver := servermon.DurationObserver(servermon.QueryDurationHistogram, string(op))
defer durationObserver()
defer servermon.ErrorCounter(servermon.QueryErrorCount, &err, string(op))

db := d.DB.WithContext(ctx)
if err := db.Delete(c).Error; err != nil {
return errors.E(op, dbError(err))
Expand Down
30 changes: 26 additions & 4 deletions internal/db/cloudcredential.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,20 @@ import (

"github.com/canonical/jimm/internal/dbmodel"
"github.com/canonical/jimm/internal/errors"
"github.com/canonical/jimm/internal/servermon"
)

// SetCloudCredential upserts the cloud credential information.
func (d *Database) SetCloudCredential(ctx context.Context, cred *dbmodel.CloudCredential) error {
func (d *Database) SetCloudCredential(ctx context.Context, cred *dbmodel.CloudCredential) (err error) {
const op = errors.Op("db.SetCloudCredential")
if err := d.ready(); err != nil {
return errors.E(op, err)
}

durationObserver := servermon.DurationObserver(servermon.QueryDurationHistogram, string(op))
defer durationObserver()
defer servermon.ErrorCounter(servermon.QueryErrorCount, &err, string(op))

if cred.CloudName == "" || cred.OwnerIdentityName == "" || cred.Name == "" {
return errors.E(op, errors.CodeBadRequest, fmt.Sprintf("invalid cloudcredential tag %q", cred.CloudName+"/"+cred.OwnerIdentityName+"/"+cred.Name))
}
Expand All @@ -39,11 +44,16 @@ func (d *Database) SetCloudCredential(ctx context.Context, cred *dbmodel.CloudCr

// GetCloudCredential returns cloud credential information based on the
// cloud, owner and name.
func (d *Database) GetCloudCredential(ctx context.Context, cred *dbmodel.CloudCredential) error {
func (d *Database) GetCloudCredential(ctx context.Context, cred *dbmodel.CloudCredential) (err error) {
const op = errors.Op("db.GetCloudCredential")
if err := d.ready(); err != nil {
return errors.E(op, err)
}

durationObserver := servermon.DurationObserver(servermon.QueryDurationHistogram, string(op))
defer durationObserver()
defer servermon.ErrorCounter(servermon.QueryErrorCount, &err, string(op))

if cred.CloudName == "" || cred.OwnerIdentityName == "" || cred.Name == "" {
return errors.E(op, errors.CodeNotFound, fmt.Sprintf("cloudcredential %q not found", cred.CloudName+"/"+cred.OwnerIdentityName+"/"+cred.Name))
}
Expand All @@ -64,13 +74,17 @@ func (d *Database) GetCloudCredential(ctx context.Context, cred *dbmodel.CloudCr
// the given identity calling the given function with each one. If cloud is
// specified then the cloud-credentials are filtered to only return
// credentials for that cloud.
func (d *Database) ForEachCloudCredential(ctx context.Context, identityName, cloud string, f func(*dbmodel.CloudCredential) error) error {
func (d *Database) ForEachCloudCredential(ctx context.Context, identityName, cloud string, f func(*dbmodel.CloudCredential) error) (err error) {
const op = errors.Op("db.ForEachCloudCredential")

if err := d.ready(); err != nil {
return errors.E(op, err)
}

durationObserver := servermon.DurationObserver(servermon.QueryDurationHistogram, string(op))
defer durationObserver()
defer servermon.ErrorCounter(servermon.QueryErrorCount, &err, string(op))

db := d.DB.WithContext(ctx)
mdb := db.Model(dbmodel.CloudCredential{})
if cloud == "" {
Expand Down Expand Up @@ -103,9 +117,17 @@ func (d *Database) ForEachCloudCredential(ctx context.Context, identityName, clo
}

// DeleteCloudCredential removes the given CloudCredential from the database.
func (d *Database) DeleteCloudCredential(ctx context.Context, cred *dbmodel.CloudCredential) error {
func (d *Database) DeleteCloudCredential(ctx context.Context, cred *dbmodel.CloudCredential) (err error) {
const op = errors.Op("db.DeleteCloudCredential")

if err := d.ready(); err != nil {
return errors.E(op, err)
}

durationObserver := servermon.DurationObserver(servermon.QueryDurationHistogram, string(op))
defer durationObserver()
defer servermon.ErrorCounter(servermon.QueryErrorCount, &err, string(op))

db := d.DB.WithContext(ctx)
if err := db.Delete(cred).Error; err != nil {
err = dbError(err)
Expand Down
Loading

0 comments on commit d1215ee

Please sign in to comment.