speed up vm trace pages
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
2026-02-09 14:19:24 +11:00
parent c4097ca608
commit 59b16db04f
12 changed files with 702 additions and 208 deletions

View File

@@ -3,20 +3,22 @@ package handler
import (
"fmt"
"net/http"
"net/url"
"strings"
"time"
"vctp/components/views"
"vctp/db"
)
// VmTrace shows per-snapshot details for a VM across all snapshots.
// VmTrace shows VM history in either hourly-detail or daily-aggregated mode.
// @Summary Trace VM history
// @Description Shows VM resource history across snapshots, with chart and table.
// @Description Shows VM resource history with an hourly detail view and a daily aggregated view, with chart and table output.
// @Tags vm
// @Produce text/html
// @Param vm_id query string false "VM ID"
// @Param vm_uuid query string false "VM UUID"
// @Param name query string false "VM name"
// @Param view query string false "hourly|daily (default: hourly)"
// @Success 200 {string} string "HTML page"
// @Failure 400 {string} string "Missing identifier"
// @Router /vm/trace [get]
@@ -25,6 +27,11 @@ func (h *Handler) VmTrace(w http.ResponseWriter, r *http.Request) {
vmID := r.URL.Query().Get("vm_id")
vmUUID := r.URL.Query().Get("vm_uuid")
name := r.URL.Query().Get("name")
viewType := strings.ToLower(strings.TrimSpace(r.URL.Query().Get("view")))
if viewType != "daily" {
viewType = "hourly"
}
meta := buildVmTraceMeta(viewType, vmID, vmUUID, name)
var entries []views.VmTraceEntry
chart := views.VmTraceChart{}
@@ -39,12 +46,20 @@ func (h *Handler) VmTrace(w http.ResponseWriter, r *http.Request) {
// Only fetch data when a query is provided; otherwise render empty page with form.
if vmID != "" || vmUUID != "" || name != "" {
h.Logger.Info("vm trace request", "vm_id", vmID, "vm_uuid", vmUUID, "name", name)
h.Logger.Info("vm trace request", "view", viewType, "vm_id", vmID, "vm_uuid", vmUUID, "name", name)
lifecycle, lifeErr := db.FetchVmLifecycle(ctx, h.Database.DB(), vmID, vmUUID, name)
if lifeErr != nil {
h.Logger.Warn("failed to fetch VM lifecycle", "error", lifeErr)
}
rows, err := db.FetchVmTrace(ctx, h.Database.DB(), vmID, vmUUID, name)
var (
rows []db.VmTraceRow
err error
)
if viewType == "daily" {
rows, err = db.FetchVmTraceDaily(ctx, h.Database.DB(), vmID, vmUUID, name)
} else {
rows, err = db.FetchVmTrace(ctx, h.Database.DB(), vmID, vmUUID, name)
}
if err != nil {
h.Logger.Error("failed to fetch VM trace", "error", err)
http.Error(w, fmt.Sprintf("failed to fetch VM trace: %v", err), http.StatusInternalServerError)
@@ -99,11 +114,47 @@ func (h *Handler) VmTrace(w http.ResponseWriter, r *http.Request) {
}
w.Header().Set("Content-Type", "text/html; charset=utf-8")
if err := views.VmTracePage(queryLabel, displayQuery, vmID, vmUUID, name, creationLabel, deletionLabel, creationApprox, entries, chart).Render(ctx, w); err != nil {
if err := views.VmTracePage(queryLabel, displayQuery, vmID, vmUUID, name, creationLabel, deletionLabel, creationApprox, entries, chart, meta).Render(ctx, w); err != nil {
http.Error(w, "Failed to render template", http.StatusInternalServerError)
}
}
func buildVmTraceMeta(viewType, vmID, vmUUID, name string) views.VmTraceMeta {
if viewType != "daily" {
viewType = "hourly"
}
addQuery := func(targetView string) string {
q := url.Values{}
q.Set("view", targetView)
if strings.TrimSpace(vmID) != "" {
q.Set("vm_id", vmID)
}
if strings.TrimSpace(vmUUID) != "" {
q.Set("vm_uuid", vmUUID)
}
if strings.TrimSpace(name) != "" {
q.Set("name", name)
}
return "/vm/trace?" + q.Encode()
}
meta := views.VmTraceMeta{
ViewType: viewType,
TypeLabel: "Hourly",
HourlyLink: addQuery("hourly"),
DailyLink: addQuery("daily"),
HourlyClass: "web3-button",
DailyClass: "web3-button",
}
if viewType == "daily" {
meta.TypeLabel = "Daily"
meta.DailyClass = "web3-button active"
} else {
meta.HourlyClass = "web3-button active"
}
return meta
}
func buildVmTraceChart(entries []views.VmTraceEntry) views.VmTraceChart {
if len(entries) == 0 {
return views.VmTraceChart{}