From 5130d37632dda8994579aa7926c573e6578e5c46 Mon Sep 17 00:00:00 2001 From: Nathan Coad Date: Wed, 14 Jan 2026 10:27:24 +1100 Subject: [PATCH] ensure we dont collect hourly snapshot too soon after startup --- internal/report/snapshots.go | 32 ++++++++++++++++++++++++++++ internal/tasks/inventorySnapshots.go | 30 ++++++++++++++++++++------ internal/tasks/tasks.go | 9 ++++---- main.go | 9 ++++---- 4 files changed, 66 insertions(+), 14 deletions(-) diff --git a/internal/report/snapshots.go b/internal/report/snapshots.go index a543c02..b417782 100644 --- a/internal/report/snapshots.go +++ b/internal/report/snapshots.go @@ -361,6 +361,38 @@ ORDER BY snapshot_time ASC, table_name ASC return records, rows.Err() } +func LatestSnapshotTime(ctx context.Context, database db.Database, snapshotType string) (time.Time, error) { + dbConn := database.DB() + driver := strings.ToLower(dbConn.DriverName()) + + var maxTime sql.NullInt64 + switch driver { + case "sqlite": + if err := dbConn.GetContext(ctx, &maxTime, ` +SELECT MAX(snapshot_time) +FROM snapshot_registry +WHERE snapshot_type = ? +`, snapshotType); err != nil { + return time.Time{}, err + } + case "pgx", "postgres": + if err := dbConn.GetContext(ctx, &maxTime, ` +SELECT MAX(snapshot_time) +FROM snapshot_registry +WHERE snapshot_type = $1 +`, snapshotType); err != nil { + return time.Time{}, err + } + default: + return time.Time{}, fmt.Errorf("unsupported driver for listing snapshots: %s", driver) + } + + if !maxTime.Valid || maxTime.Int64 <= 0 { + return time.Time{}, nil + } + return time.Unix(maxTime.Int64, 0), nil +} + func FormatSnapshotLabel(snapshotType string, snapshotTime time.Time, tableName string) string { switch snapshotType { case "hourly": diff --git a/internal/tasks/inventorySnapshots.go b/internal/tasks/inventorySnapshots.go index e55a31d..42fd5e4 100644 --- a/internal/tasks/inventorySnapshots.go +++ b/internal/tasks/inventorySnapshots.go @@ -51,6 +51,30 @@ func (c *CronTask) RunVcenterSnapshotHourly(ctx context.Context, logger *slog.Lo logger.Info("Hourly snapshot job finished", "duration", time.Since(startedAt)) }() startTime := time.Now() + + // reload settings in case vcenter list has changed + c.Settings.ReadYMLSettings() + + if c.FirstHourlySnapshotCheck { + if err := report.EnsureSnapshotRegistry(ctx, c.Database); err != nil { + return err + } + lastSnapshot, err := report.LatestSnapshotTime(ctx, c.Database, "hourly") + if err != nil { + return err + } + minIntervalSeconds := intWithDefault(c.Settings.Values.Settings.VcenterInventorySnapshotSeconds, 3600) + if !lastSnapshot.IsZero() && startTime.Sub(lastSnapshot) < time.Duration(minIntervalSeconds)*time.Second { + c.Logger.Info("Skipping hourly snapshot, last snapshot too recent", + "last_snapshot", lastSnapshot, + "min_interval_seconds", minIntervalSeconds, + ) + c.FirstHourlySnapshotCheck = false + return nil + } + c.FirstHourlySnapshotCheck = false + } + tableName, err := hourlyInventoryTableName(startTime) if err != nil { return err @@ -60,16 +84,10 @@ func (c *CronTask) RunVcenterSnapshotHourly(ctx context.Context, logger *slog.Lo if err := ensureDailyInventoryTable(ctx, dbConn, tableName); err != nil { return err } - if err := report.EnsureSnapshotRegistry(ctx, c.Database); err != nil { - return err - } if err := report.RegisterSnapshot(ctx, c.Database, "hourly", tableName, startTime); err != nil { c.Logger.Warn("failed to register hourly snapshot", "error", err, "table", tableName) } - // reload settings in case vcenter list has changed - c.Settings.ReadYMLSettings() - var wg sync.WaitGroup var errCount int64 concurrencyLimit := c.Settings.Values.Settings.HourlySnapshotConcurrency diff --git a/internal/tasks/tasks.go b/internal/tasks/tasks.go index 4aa3c9d..1dd807e 100644 --- a/internal/tasks/tasks.go +++ b/internal/tasks/tasks.go @@ -9,8 +9,9 @@ import ( // CronTask stores runtime information to be used by tasks type CronTask struct { - Logger *slog.Logger - Database db.Database - Settings *settings.Settings - VcCreds *vcenter.VcenterLogin + Logger *slog.Logger + Database db.Database + Settings *settings.Settings + VcCreds *vcenter.VcenterLogin + FirstHourlySnapshotCheck bool } diff --git a/main.go b/main.go index ff9e45e..1342541 100644 --- a/main.go +++ b/main.go @@ -161,10 +161,11 @@ func main() { // Pass useful information to the cron jobs ct := &tasks.CronTask{ - Logger: logger, - Database: database, - Settings: s, - VcCreds: &creds, + Logger: logger, + Database: database, + Settings: s, + VcCreds: &creds, + FirstHourlySnapshotCheck: true, } /*