ensure we logout, fix aggregations
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
2026-01-16 20:29:40 +11:00
parent a12fe5cad0
commit 1874b2c621
12 changed files with 416 additions and 111 deletions

View File

@@ -280,6 +280,11 @@ func (c *CronTask) aggregateDailySummaryGo(ctx context.Context, dayStart, dayEnd
return err
}
// Persist rollup cache for monthly aggregation.
if err := c.persistDailyRollup(ctx, dayStart.Unix(), aggMap, totalSamples); err != nil {
c.Logger.Warn("failed to persist daily rollup cache", "error", err, "date", dayStart.Format("2006-01-02"))
}
// Refine lifecycle with existing SQL helper to pick up first-after deletions.
if err := db.RefineCreationDeletionFromUnion(ctx, dbConn, summaryTable, unionQuery); err != nil {
c.Logger.Warn("failed to refine creation/deletion times", "error", err, "table", summaryTable)
@@ -727,3 +732,44 @@ func btoi(b bool) int64 {
}
return 0
}
// persistDailyRollup stores per-day aggregates into vm_daily_rollup to speed monthly aggregation.
func (c *CronTask) persistDailyRollup(ctx context.Context, dayUnix int64, agg map[dailyAggKey]*dailyAggVal, totalSamples int) error {
dbConn := c.Database.DB()
for _, v := range agg {
if strings.EqualFold(strings.TrimSpace(v.isTemplate), "true") || v.isTemplate == "1" {
continue
}
row := db.VmDailyRollupRow{
Vcenter: v.key.Vcenter,
VmId: v.key.VmId,
VmUuid: v.key.VmUuid,
Name: v.key.Name,
CreationTime: v.creation,
DeletionTime: v.deletion,
SamplesPresent: v.samples,
TotalSamples: int64(totalSamples),
SumVcpu: float64(v.sumVcpu),
SumRam: float64(v.sumRam),
SumDisk: v.sumDisk,
TinHits: v.tinHits,
BronzeHits: v.bronzeHits,
SilverHits: v.silverHits,
GoldHits: v.goldHits,
LastResourcePool: v.resourcePool,
LastDatacenter: v.datacenter,
LastCluster: v.cluster,
LastFolder: v.folder,
LastProvisionedDisk: v.lastDisk,
LastVcpuCount: v.lastVcpu,
LastRamGB: v.lastRam,
IsTemplate: v.isTemplate,
PoweredOn: v.poweredOn,
SrmPlaceholder: v.srmPlaceholder,
}
if err := db.UpsertVmDailyRollup(ctx, dbConn, dayUnix, row); err != nil {
return err
}
}
return nil
}

View File

@@ -102,7 +102,7 @@ func (c *CronTask) RunVcenterSnapshotHourly(ctx context.Context, logger *slog.Lo
if err != nil {
return err
}
minIntervalSeconds := intWithDefault(c.Settings.Values.Settings.VcenterInventorySnapshotSeconds, 3600)
minIntervalSeconds := intWithDefault(c.Settings.Values.Settings.VcenterInventorySnapshotSeconds, 3600) / 2
if !lastSnapshot.IsZero() && startTime.Sub(lastSnapshot) < time.Duration(minIntervalSeconds)*time.Second {
c.Logger.Info("Skipping hourly snapshot, last snapshot too recent",
"last_snapshot", lastSnapshot,
@@ -882,8 +882,12 @@ func (c *CronTask) captureHourlySnapshotForVcenter(ctx context.Context, startTim
return fmt.Errorf("unable to connect to vcenter: %w", err)
}
defer func() {
if err := vc.Logout(); err != nil {
logCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := vc.Logout(logCtx); err != nil {
c.Logger.Warn("vcenter logout failed", "url", url, "error", err)
} else {
c.Logger.Debug("vcenter logout succeeded", "url", url)
}
}()

View File

@@ -110,7 +110,7 @@ func (c *CronTask) RunVcenterPoll(ctx context.Context, logger *slog.Logger) erro
}
}
c.Logger.Debug("Finished checking vcenter", "url", url)
vc.Logout()
_ = vc.Logout(ctx)
}
c.Logger.Debug("Finished polling vcenters")

View File

@@ -173,6 +173,14 @@ func (c *CronTask) aggregateMonthlySummaryGo(ctx context.Context, monthStart, mo
if err != nil {
return err
}
if len(aggMap) == 0 {
cacheAgg, cacheErr := c.scanDailyRollup(ctx, monthStart, monthEnd)
if cacheErr == nil && len(cacheAgg) > 0 {
aggMap = cacheAgg
} else if cacheErr != nil {
c.Logger.Warn("failed to read daily rollup cache; using table scan", "error", cacheErr)
}
}
if len(aggMap) == 0 {
return fmt.Errorf("no VM records aggregated for %s", monthStart.Format("2006-01"))
}
@@ -439,6 +447,89 @@ FROM %s
return result, rows.Err()
}
// scanDailyRollup aggregates monthly data from vm_daily_rollup cache.
func (c *CronTask) scanDailyRollup(ctx context.Context, start, end time.Time) (map[monthlyAggKey]*monthlyAggVal, error) {
dbConn := c.Database.DB()
if !db.TableExists(ctx, dbConn, "vm_daily_rollup") {
return map[monthlyAggKey]*monthlyAggVal{}, nil
}
query := `
SELECT
"Date","Vcenter","VmId","VmUuid","Name","CreationTime","DeletionTime",
"SamplesPresent","TotalSamples","SumVcpu","SumRam","SumDisk",
"TinHits","BronzeHits","SilverHits","GoldHits",
"LastResourcePool","LastDatacenter","LastCluster","LastFolder",
"LastProvisionedDisk","LastVcpuCount","LastRamGB","IsTemplate","PoweredOn","SrmPlaceholder"
FROM vm_daily_rollup
WHERE "Date" >= ? AND "Date" < ?
`
bind := dbConn.Rebind(query)
rows, err := dbConn.QueryxContext(ctx, bind, start.Unix(), end.Unix())
if err != nil {
return nil, err
}
defer rows.Close()
agg := make(map[monthlyAggKey]*monthlyAggVal, 512)
for rows.Next() {
var (
date sql.NullInt64
vcenter, vmId, vmUuid, name string
creation, deletion sql.NullInt64
samplesPresent, totalSamples sql.NullInt64
sumVcpu, sumRam, sumDisk sql.NullFloat64
tinHits, bronzeHits, silverHits, goldHits sql.NullInt64
lastPool, lastDc, lastCluster, lastFolder sql.NullString
lastDisk, lastVcpu, lastRam sql.NullFloat64
isTemplate, poweredOn, srmPlaceholder sql.NullString
)
if err := rows.Scan(
&date, &vcenter, &vmId, &vmUuid, &name, &creation, &deletion,
&samplesPresent, &totalSamples, &sumVcpu, &sumRam, &sumDisk,
&tinHits, &bronzeHits, &silverHits, &goldHits,
&lastPool, &lastDc, &lastCluster, &lastFolder,
&lastDisk, &lastVcpu, &lastRam, &isTemplate, &poweredOn, &srmPlaceholder,
); err != nil {
continue
}
if strings.EqualFold(strings.TrimSpace(isTemplate.String), "true") || isTemplate.String == "1" {
continue
}
key := monthlyAggKey{Vcenter: vcenter, VmId: vmId, VmUuid: vmUuid, Name: name}
val := &monthlyAggVal{
key: key,
resourcePool: lastPool.String,
datacenter: lastDc.String,
cluster: lastCluster.String,
folder: lastFolder.String,
isTemplate: isTemplate.String,
poweredOn: poweredOn.String,
srmPlaceholder: srmPlaceholder.String,
provisioned: lastDisk.Float64,
vcpuCount: int64(lastVcpu.Float64),
ramGB: int64(lastRam.Float64),
creation: creation.Int64,
deletion: deletion.Int64,
lastSnapshot: time.Unix(date.Int64, 0),
samplesPresent: samplesPresent.Int64,
totalSamples: float64(totalSamples.Int64),
sumVcpu: sumVcpu.Float64,
sumRam: sumRam.Float64,
sumDisk: sumDisk.Float64,
tinWeighted: float64(tinHits.Int64),
bronzeWeighted: float64(bronzeHits.Int64),
silverWeighted: float64(silverHits.Int64),
goldWeighted: float64(goldHits.Int64),
}
if existing, ok := agg[key]; ok {
mergeMonthlyAgg(existing, val)
} else {
agg[key] = val
}
}
return agg, rows.Err()
}
func (c *CronTask) insertMonthlyAggregates(ctx context.Context, summaryTable string, aggMap map[monthlyAggKey]*monthlyAggVal) error {
dbConn := c.Database.DB()
columns := []string{

View File

@@ -165,10 +165,7 @@ func (c *CronTask) RunVmCheck(ctx context.Context, logger *slog.Logger) error {
poweredOn = "TRUE"
}
err = vc.Logout()
if err != nil {
c.Logger.Error("unable to logout of vcenter", "error", err)
}
_ = vc.Logout(ctx)
if foundVm {
c.Logger.Debug("Adding to Inventory table", "vm_name", evt.VmName.String, "vcpus", numVcpus, "ram", numRam, "dc", evt.DatacenterId.String)