improve aggregation logic
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
@@ -664,6 +664,21 @@ func validateTableName(name string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func tableHasRows(ctx context.Context, dbConn *sqlx.DB, table string) (bool, error) {
|
||||||
|
if err := validateTableName(table); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
query := fmt.Sprintf(`SELECT 1 FROM %s LIMIT 1`, table)
|
||||||
|
var exists int
|
||||||
|
if err := dbConn.GetContext(ctx, &exists, query); err != nil {
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
func tableColumns(ctx context.Context, dbConn *sqlx.DB, tableName string) ([]string, error) {
|
func tableColumns(ctx context.Context, dbConn *sqlx.DB, tableName string) ([]string, error) {
|
||||||
driver := strings.ToLower(dbConn.DriverName())
|
driver := strings.ToLower(dbConn.DriverName())
|
||||||
switch driver {
|
switch driver {
|
||||||
@@ -908,6 +923,9 @@ func buildHourlyTotals(ctx context.Context, dbConn *sqlx.DB, records []SnapshotR
|
|||||||
if err := validateTableName(record.TableName); err != nil {
|
if err := validateTableName(record.TableName); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if rowsExist, err := tableHasRows(ctx, dbConn, record.TableName); err != nil || !rowsExist {
|
||||||
|
continue
|
||||||
|
}
|
||||||
query := fmt.Sprintf(`
|
query := fmt.Sprintf(`
|
||||||
SELECT
|
SELECT
|
||||||
COUNT(DISTINCT "VmId") AS vm_count,
|
COUNT(DISTINCT "VmId") AS vm_count,
|
||||||
@@ -955,6 +973,9 @@ func buildDailyTotals(ctx context.Context, dbConn *sqlx.DB, records []SnapshotRe
|
|||||||
if err := validateTableName(record.TableName); err != nil {
|
if err := validateTableName(record.TableName); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if rowsExist, err := tableHasRows(ctx, dbConn, record.TableName); err != nil || !rowsExist {
|
||||||
|
continue
|
||||||
|
}
|
||||||
query := fmt.Sprintf(`
|
query := fmt.Sprintf(`
|
||||||
SELECT
|
SELECT
|
||||||
COUNT(DISTINCT "VmId") AS vm_count,
|
COUNT(DISTINCT "VmId") AS vm_count,
|
||||||
|
|||||||
@@ -170,6 +170,7 @@ func (c *CronTask) aggregateDailySummary(ctx context.Context, targetTime time.Ti
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
hourlySnapshots = filterSnapshotsWithRows(ctx, dbConn, hourlySnapshots)
|
||||||
if len(hourlySnapshots) == 0 {
|
if len(hourlySnapshots) == 0 {
|
||||||
return fmt.Errorf("no hourly snapshot tables found for %s", dayStart.Format("2006-01-02"))
|
return fmt.Errorf("no hourly snapshot tables found for %s", dayStart.Format("2006-01-02"))
|
||||||
}
|
}
|
||||||
@@ -202,6 +203,7 @@ func (c *CronTask) aggregateDailySummary(ctx context.Context, targetTime time.Ti
|
|||||||
prevEnd := dayStart
|
prevEnd := dayStart
|
||||||
prevSnapshots, err := report.ListSnapshotsByRange(ctx, c.Database, "hourly", prevStart, prevEnd)
|
prevSnapshots, err := report.ListSnapshotsByRange(ctx, c.Database, "hourly", prevStart, prevEnd)
|
||||||
if err == nil && len(prevSnapshots) > 0 {
|
if err == nil && len(prevSnapshots) > 0 {
|
||||||
|
prevSnapshots = filterSnapshotsWithRows(ctx, dbConn, prevSnapshots)
|
||||||
prevTables := make([]string, 0, len(prevSnapshots))
|
prevTables := make([]string, 0, len(prevSnapshots))
|
||||||
for _, snapshot := range prevSnapshots {
|
for _, snapshot := range prevSnapshots {
|
||||||
prevTables = append(prevTables, snapshot.TableName)
|
prevTables = append(prevTables, snapshot.TableName)
|
||||||
@@ -228,6 +230,9 @@ func (c *CronTask) aggregateDailySummary(ctx context.Context, targetTime time.Ti
|
|||||||
}
|
}
|
||||||
|
|
||||||
insertQuery := fmt.Sprintf(`
|
insertQuery := fmt.Sprintf(`
|
||||||
|
WITH snapshots AS (
|
||||||
|
%s
|
||||||
|
)
|
||||||
INSERT INTO %s (
|
INSERT INTO %s (
|
||||||
"InventoryId", "Name", "Vcenter", "VmId", "EventKey", "CloudId", "CreationTime", "DeletionTime",
|
"InventoryId", "Name", "Vcenter", "VmId", "EventKey", "CloudId", "CreationTime", "DeletionTime",
|
||||||
"ResourcePool", "Datacenter", "Cluster", "Folder", "ProvisionedDisk", "VcpuCount",
|
"ResourcePool", "Datacenter", "Cluster", "Folder", "ProvisionedDisk", "VcpuCount",
|
||||||
@@ -240,7 +245,16 @@ SELECT
|
|||||||
"InventoryId", "Name", "Vcenter", "VmId", "EventKey", "CloudId",
|
"InventoryId", "Name", "Vcenter", "VmId", "EventKey", "CloudId",
|
||||||
COALESCE(NULLIF("CreationTime", 0), MIN(CASE WHEN "IsPresent" = 'TRUE' THEN "SnapshotTime" END), 0) AS "CreationTime",
|
COALESCE(NULLIF("CreationTime", 0), MIN(CASE WHEN "IsPresent" = 'TRUE' THEN "SnapshotTime" END), 0) AS "CreationTime",
|
||||||
"DeletionTime",
|
"DeletionTime",
|
||||||
"ResourcePool", "Datacenter", "Cluster", "Folder", "ProvisionedDisk", "VcpuCount",
|
(
|
||||||
|
SELECT s2."ResourcePool"
|
||||||
|
FROM snapshots s2
|
||||||
|
WHERE s2."VmId" = snapshots."VmId"
|
||||||
|
AND s2."Vcenter" = snapshots."Vcenter"
|
||||||
|
AND s2."IsPresent" = 'TRUE'
|
||||||
|
ORDER BY s2."SnapshotTime" DESC
|
||||||
|
LIMIT 1
|
||||||
|
) AS "ResourcePool",
|
||||||
|
"Datacenter", "Cluster", "Folder", "ProvisionedDisk", "VcpuCount",
|
||||||
"RamGB", "IsTemplate", "PoweredOn", "SrmPlaceholder", "VmUuid",
|
"RamGB", "IsTemplate", "PoweredOn", "SrmPlaceholder", "VmUuid",
|
||||||
SUM(CASE WHEN "IsPresent" = 'TRUE' THEN 1 ELSE 0 END) AS "SamplesPresent",
|
SUM(CASE WHEN "IsPresent" = 'TRUE' THEN 1 ELSE 0 END) AS "SamplesPresent",
|
||||||
AVG(CASE WHEN "IsPresent" = 'TRUE' AND "VcpuCount" IS NOT NULL THEN "VcpuCount" END) AS "AvgVcpuCount",
|
AVG(CASE WHEN "IsPresent" = 'TRUE' AND "VcpuCount" IS NOT NULL THEN "VcpuCount" END) AS "AvgVcpuCount",
|
||||||
@@ -263,14 +277,12 @@ SELECT
|
|||||||
/ NULLIF(SUM(CASE WHEN "IsPresent" = 'TRUE' THEN 1 ELSE 0 END), 0) AS "Silver",
|
/ NULLIF(SUM(CASE WHEN "IsPresent" = 'TRUE' THEN 1 ELSE 0 END), 0) AS "Silver",
|
||||||
100.0 * SUM(CASE WHEN "IsPresent" = 'TRUE' AND LOWER("ResourcePool") = 'gold' THEN 1 ELSE 0 END)
|
100.0 * SUM(CASE WHEN "IsPresent" = 'TRUE' AND LOWER("ResourcePool") = 'gold' THEN 1 ELSE 0 END)
|
||||||
/ NULLIF(SUM(CASE WHEN "IsPresent" = 'TRUE' THEN 1 ELSE 0 END), 0) AS "Gold"
|
/ NULLIF(SUM(CASE WHEN "IsPresent" = 'TRUE' THEN 1 ELSE 0 END), 0) AS "Gold"
|
||||||
FROM (
|
FROM snapshots
|
||||||
%s
|
|
||||||
) snapshots
|
|
||||||
GROUP BY
|
GROUP BY
|
||||||
"InventoryId", "Name", "Vcenter", "VmId", "EventKey", "CloudId", "CreationTime", "DeletionTime",
|
"InventoryId", "Name", "Vcenter", "VmId", "EventKey", "CloudId", "CreationTime", "DeletionTime",
|
||||||
"ResourcePool", "Datacenter", "Cluster", "Folder", "ProvisionedDisk", "VcpuCount",
|
"Datacenter", "Cluster", "Folder", "ProvisionedDisk", "VcpuCount",
|
||||||
"RamGB", "IsTemplate", "PoweredOn", "SrmPlaceholder", "VmUuid";
|
"RamGB", "IsTemplate", "PoweredOn", "SrmPlaceholder", "VmUuid";
|
||||||
`, summaryTable, unionQuery)
|
`, unionQuery, summaryTable)
|
||||||
|
|
||||||
if _, err := dbConn.ExecContext(ctx, insertQuery); err != nil {
|
if _, err := dbConn.ExecContext(ctx, insertQuery); err != nil {
|
||||||
c.Logger.Error("failed to aggregate daily inventory", "error", err, "date", dayStart.Format("2006-01-02"))
|
c.Logger.Error("failed to aggregate daily inventory", "error", err, "date", dayStart.Format("2006-01-02"))
|
||||||
@@ -311,6 +323,9 @@ func (c *CronTask) aggregateMonthlySummary(ctx context.Context, targetMonth time
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dbConn := c.Database.DB()
|
||||||
|
dailySnapshots = filterSnapshotsWithRows(ctx, dbConn, dailySnapshots)
|
||||||
if len(dailySnapshots) == 0 {
|
if len(dailySnapshots) == 0 {
|
||||||
return fmt.Errorf("no hourly snapshot tables found for %s", targetMonth.Format("2006-01"))
|
return fmt.Errorf("no hourly snapshot tables found for %s", targetMonth.Format("2006-01"))
|
||||||
}
|
}
|
||||||
@@ -320,7 +335,6 @@ func (c *CronTask) aggregateMonthlySummary(ctx context.Context, targetMonth time
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
dbConn := c.Database.DB()
|
|
||||||
if err := ensureMonthlySummaryTable(ctx, dbConn, monthlyTable); err != nil {
|
if err := ensureMonthlySummaryTable(ctx, dbConn, monthlyTable); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -349,7 +363,7 @@ func (c *CronTask) aggregateMonthlySummary(ctx context.Context, targetMonth time
|
|||||||
`"InventoryId"`, `"Name"`, `"Vcenter"`, `"VmId"`, `"EventKey"`, `"CloudId"`, `"CreationTime"`,
|
`"InventoryId"`, `"Name"`, `"Vcenter"`, `"VmId"`, `"EventKey"`, `"CloudId"`, `"CreationTime"`,
|
||||||
`"DeletionTime"`, `"ResourcePool"`, `"Datacenter"`, `"Cluster"`, `"Folder"`,
|
`"DeletionTime"`, `"ResourcePool"`, `"Datacenter"`, `"Cluster"`, `"Folder"`,
|
||||||
`"ProvisionedDisk"`, `"VcpuCount"`, `"RamGB"`, `"IsTemplate"`, `"PoweredOn"`,
|
`"ProvisionedDisk"`, `"VcpuCount"`, `"RamGB"`, `"IsTemplate"`, `"PoweredOn"`,
|
||||||
`"SrmPlaceholder"`, `"VmUuid"`, `"IsPresent"`,
|
`"SrmPlaceholder"`, `"VmUuid"`, `"SnapshotTime"`, `"IsPresent"`,
|
||||||
}, templateExclusionFilter())
|
}, templateExclusionFilter())
|
||||||
if strings.TrimSpace(unionQuery) == "" {
|
if strings.TrimSpace(unionQuery) == "" {
|
||||||
return fmt.Errorf("no valid daily snapshot tables found for %s", targetMonth.Format("2006-01"))
|
return fmt.Errorf("no valid daily snapshot tables found for %s", targetMonth.Format("2006-01"))
|
||||||
@@ -369,6 +383,9 @@ func (c *CronTask) aggregateMonthlySummary(ctx context.Context, targetMonth time
|
|||||||
}
|
}
|
||||||
|
|
||||||
insertQuery := fmt.Sprintf(`
|
insertQuery := fmt.Sprintf(`
|
||||||
|
WITH snapshots AS (
|
||||||
|
%s
|
||||||
|
)
|
||||||
INSERT INTO %s (
|
INSERT INTO %s (
|
||||||
"InventoryId", "Name", "Vcenter", "VmId", "EventKey", "CloudId", "CreationTime", "DeletionTime",
|
"InventoryId", "Name", "Vcenter", "VmId", "EventKey", "CloudId", "CreationTime", "DeletionTime",
|
||||||
"ResourcePool", "Datacenter", "Cluster", "Folder", "ProvisionedDisk", "VcpuCount",
|
"ResourcePool", "Datacenter", "Cluster", "Folder", "ProvisionedDisk", "VcpuCount",
|
||||||
@@ -379,7 +396,16 @@ INSERT INTO %s (
|
|||||||
)
|
)
|
||||||
SELECT
|
SELECT
|
||||||
"InventoryId", "Name", "Vcenter", "VmId", "EventKey", "CloudId", "CreationTime", "DeletionTime",
|
"InventoryId", "Name", "Vcenter", "VmId", "EventKey", "CloudId", "CreationTime", "DeletionTime",
|
||||||
"ResourcePool", "Datacenter", "Cluster", "Folder", "ProvisionedDisk", "VcpuCount",
|
(
|
||||||
|
SELECT s2."ResourcePool"
|
||||||
|
FROM snapshots s2
|
||||||
|
WHERE s2."VmId" = snapshots."VmId"
|
||||||
|
AND s2."Vcenter" = snapshots."Vcenter"
|
||||||
|
AND s2."IsPresent" = 'TRUE'
|
||||||
|
ORDER BY s2."SnapshotTime" DESC
|
||||||
|
LIMIT 1
|
||||||
|
) AS "ResourcePool",
|
||||||
|
"Datacenter", "Cluster", "Folder", "ProvisionedDisk", "VcpuCount",
|
||||||
"RamGB", "IsTemplate", "PoweredOn", "SrmPlaceholder", "VmUuid",
|
"RamGB", "IsTemplate", "PoweredOn", "SrmPlaceholder", "VmUuid",
|
||||||
AVG(CASE WHEN "VcpuCount" IS NOT NULL THEN "VcpuCount" END) AS "AvgVcpuCount",
|
AVG(CASE WHEN "VcpuCount" IS NOT NULL THEN "VcpuCount" END) AS "AvgVcpuCount",
|
||||||
AVG(CASE WHEN "RamGB" IS NOT NULL THEN "RamGB" END) AS "AvgRamGB",
|
AVG(CASE WHEN "RamGB" IS NOT NULL THEN "RamGB" END) AS "AvgRamGB",
|
||||||
@@ -401,14 +427,12 @@ SELECT
|
|||||||
/ NULLIF(SUM(CASE WHEN "IsPresent" = 'TRUE' THEN 1 ELSE 0 END), 0) AS "Silver",
|
/ NULLIF(SUM(CASE WHEN "IsPresent" = 'TRUE' THEN 1 ELSE 0 END), 0) AS "Silver",
|
||||||
100.0 * SUM(CASE WHEN "IsPresent" = 'TRUE' AND LOWER("ResourcePool") = 'gold' THEN 1 ELSE 0 END)
|
100.0 * SUM(CASE WHEN "IsPresent" = 'TRUE' AND LOWER("ResourcePool") = 'gold' THEN 1 ELSE 0 END)
|
||||||
/ NULLIF(SUM(CASE WHEN "IsPresent" = 'TRUE' THEN 1 ELSE 0 END), 0) AS "Gold"
|
/ NULLIF(SUM(CASE WHEN "IsPresent" = 'TRUE' THEN 1 ELSE 0 END), 0) AS "Gold"
|
||||||
FROM (
|
FROM snapshots
|
||||||
%s
|
|
||||||
) snapshots
|
|
||||||
GROUP BY
|
GROUP BY
|
||||||
"InventoryId", "Name", "Vcenter", "VmId", "EventKey", "CloudId", "CreationTime", "DeletionTime",
|
"InventoryId", "Name", "Vcenter", "VmId", "EventKey", "CloudId", "CreationTime", "DeletionTime",
|
||||||
"ResourcePool", "Datacenter", "Cluster", "Folder", "ProvisionedDisk", "VcpuCount",
|
"Datacenter", "Cluster", "Folder", "ProvisionedDisk", "VcpuCount",
|
||||||
"RamGB", "IsTemplate", "PoweredOn", "SrmPlaceholder", "VmUuid";
|
"RamGB", "IsTemplate", "PoweredOn", "SrmPlaceholder", "VmUuid";
|
||||||
`, monthlyTable, unionQuery)
|
`, unionQuery, monthlyTable)
|
||||||
|
|
||||||
if _, err := dbConn.ExecContext(ctx, insertQuery); err != nil {
|
if _, err := dbConn.ExecContext(ctx, insertQuery); err != nil {
|
||||||
c.Logger.Error("failed to aggregate monthly inventory", "error", err, "month", targetMonth.Format("2006-01"))
|
c.Logger.Error("failed to aggregate monthly inventory", "error", err, "month", targetMonth.Format("2006-01"))
|
||||||
@@ -870,6 +894,16 @@ func tableHasRows(ctx context.Context, dbConn *sqlx.DB, table string) (bool, err
|
|||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func filterSnapshotsWithRows(ctx context.Context, dbConn *sqlx.DB, snapshots []report.SnapshotRecord) []report.SnapshotRecord {
|
||||||
|
filtered := snapshots[:0]
|
||||||
|
for _, snapshot := range snapshots {
|
||||||
|
if rowsExist, err := tableHasRows(ctx, dbConn, snapshot.TableName); err == nil && rowsExist {
|
||||||
|
filtered = append(filtered, snapshot)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filtered
|
||||||
|
}
|
||||||
|
|
||||||
type snapshotTotals struct {
|
type snapshotTotals struct {
|
||||||
VmCount int64 `db:"vm_count"`
|
VmCount int64 `db:"vm_count"`
|
||||||
VcpuTotal int64 `db:"vcpu_total"`
|
VcpuTotal int64 `db:"vcpu_total"`
|
||||||
|
|||||||
Reference in New Issue
Block a user