package database import ( "fmt" "gorm.io/gorm" ) // The running totals in a webhook's event database keep the webhook // page's lifetime figures right after retention has removed the rows // they count, and let the page show them without counting every row. // Each total changes in the transaction that writes or deletes the // rows it counts. // EventTotals is the single row counting a webhook's events: every // event ever stored, and how many of them retention has deleted. type EventTotals struct { ID int64 `gorm:"primaryKey"` Events int64 `gorm:"not null"` EventsRemoved int64 `gorm:"not null"` } // TableName names the table AddEventTotals updates. func (EventTotals) TableName() string { return "event_totals" } // TargetTotals is one row per target counting its deliveries: every // delivery ever created, how many became delivered and how many // failed, and how many deliveries and failed deliveries retention has // deleted. The webhook's delivery figures are these rows summed. type TargetTotals struct { TargetID string `gorm:"type:uuid;primaryKey"` Deliveries int64 `gorm:"not null"` Delivered int64 `gorm:"not null"` Failed int64 `gorm:"not null"` DeliveriesRemoved int64 `gorm:"not null"` FailedRemoved int64 `gorm:"not null"` } // TableName names the table AddTargetTotals updates. func (TargetTotals) TableName() string { return "target_totals" } // AddEventTotals adds each count in add to the webhook's event totals. // Call it on the transaction that writes or deletes the events it // counts. func AddEventTotals(tx *gorm.DB, add EventTotals) error { err := tx.Exec( `UPDATE event_totals SET events = events + ?, events_removed = events_removed + ?`, add.Events, add.EventsRemoved, ).Error if err != nil { return fmt.Errorf("adding to event totals: %w", err) } return nil } // AddTargetTotals adds each count in add to the totals of the target // add.TargetID names, creating its row the first time. Call it on the // transaction that writes or deletes the deliveries it counts. func AddTargetTotals(tx *gorm.DB, add TargetTotals) error { err := tx.Exec( `INSERT INTO target_totals (target_id, deliveries, delivered, failed, deliveries_removed, failed_removed) VALUES (?, ?, ?, ?, ?, ?) ON CONFLICT (target_id) DO UPDATE SET deliveries = deliveries + excluded.deliveries, delivered = delivered + excluded.delivered, failed = failed + excluded.failed, deliveries_removed = deliveries_removed + excluded.deliveries_removed, failed_removed = failed_removed + excluded.failed_removed`, add.TargetID, add.Deliveries, add.Delivered, add.Failed, add.DeliveriesRemoved, add.FailedRemoved, ).Error if err != nil { return fmt.Errorf( "adding to totals of target %s: %w", add.TargetID, err, ) } return nil }