Commit Graph

48 Commits

Author SHA1 Message Date
rcourtman 848b4d5038 Preserve customer data across plan downgrades 2026-07-14 11:47:25 +01:00
rcourtman 5e9a94d375 Stabilize metrics rollup chunk proof
Keep the bounded rollup test inside raw retention and wait for startup maintenance before seeding source rows.
2026-07-03 21:57:14 +01:00
rcourtman 760eb32aaf Bound metrics rollup working set
Refs #1442
2026-07-03 20:35:02 +01:00
rcourtman 201819f8f8 test(go): skip perf-budget overruns on contended local hosts, enforce in CI
A full local go test run on 2026-06-11 failed pkg/metrics while vite
builds were saturating the machine. Reproduced under synthetic 8-core
load: only the TestSLO_* wall-clock p95 assertions failed, never the
functional tests. A latency budget measured on a shared dev machine
cannot distinguish host contention from a code regression; load storms
inflated medians up to ~4x with no code change, so no absolute
threshold separates the two.

Route the latency SLO assertions (pkg/metrics, internal/api,
internal/monitoring) and the load/stress perf assertions (internal/api)
through helpers that keep strict enforcement on GitHub Actions runners
(controlled environment, existing hosted-runner envelopes unchanged)
and skip locally on overrun, printing the full p50/p95/p99
distribution in the skip message. A local pass still means the budget
was genuinely met. Error-response and correctness checks remain hard
failures. CI -race runs are unaffected: these tests already skip under
the race detector.

Verified: pkg/metrics, internal/api, and internal/monitoring all pass
with 8 CPU burners saturating the host, the scenario that previously
turned TestSLO_RollupTierBatchedFleet and four other budget tests red.
2026-06-11 13:12:59 +01:00
rcourtman f136b1ef59 fix(mock): backfill the metrics store in mock mode so reports get real history
Mock mode seeded rich in-memory chart history but passed a nil store to the
seeder and the live tick, so the sqlite metrics store held almost nothing:
vmware/truenas/docker resources got one row per poll from the platform
ingestion paths and mock Proxmox guests got nothing at all (the unified sync
skips every resource in mock mode). Performance reports rendered
"Data Points: 0" for PVE guests and "Samples: 1" elsewhere, with no charts.

Re-enable store seeding behind an explicit PULSE_MOCK_SEED_METRICS_STORE
opt-in that scripts/hot-dev.sh and scripts/toggle-mock.sh export exactly
where they point PULSE_DATA_DIR at the isolated tmp/mock-data dir. Without
the opt-in (a production install flipping PULSE_MOCK_MODE on its real data
dir) the store stays untouched, which is what the old nil guard protected.

Replace the dormant dense seeding policy (every in-memory timestamp written
to both hourly and daily tiers, ~11M rows at current fixture scale) with
tier-correct backfill driven by the deterministic mock.SampleMetric runtime:
daily 30d at 4h spacing, hourly 7d at 2h, minute 24h at 15m, raw left to the
live tick (now also store-connected under the opt-in). Timestamps sit on the
spacing grid and a new Store.MaxTimestampsForTier coverage query fills only
the gap since the previous boot, so restarts neither duplicate rows nor
re-pay the seed: a fresh seed wrote 264,960 rows in 8.4s (71MB) and a
restart wrote only the gap plus fixture series whose mock IDs are not
boot-stable (k8s pod names, ceph FSID, a pre-existing generator defect).

Verified live in mock mode: 30-day PDF reports for a mock PVE VM
(checkout-web-01), a vSphere VM, the TrueNAS host, and a Docker container
now show 1260/1260/1260/540 data points with rendered time-series charts.
2026-06-10 21:25:02 +01:00
rcourtman b707512e38 Clear all errcheck and gofmt violations so make lint gates on real findings
golangci-lint run ./... failed on ~190 pre-existing errcheck violations and
5 unformatted files, burying any new regression in noise. Fix all of them:

- Test files that hand-rolled mock-mode set/restore (vmware, truenas, and
  friends) now use the canonical setMockModeForTest/testutil.SetMockMode
  helper instead of drift copies that ignored SetEnabled errors.
- internal/mock and internal/monitoring tests get package-local
  mustSetEnabled/mustSetMockEnabled/mustSetMonitorMockMode helpers that
  fail the test on toggle errors.
- pkg/auth/sqlite_manager.go, pkg/metrics/store.go, pkg/server/server.go:
  rollbacks in defers use the explicit-discard idiom, migration renames and
  rollup commits log failures, the hosted reaper goroutine logs an error
  exit, shutdown mock-disable logs failures.
- Remaining test sites check errors with t.Fatalf/t.Errorf or explicitly
  discard best-effort calls (restore-chmods, handler-closure unmarshals)
  per existing repo style.
- gofmt: internal/api/maintenance_verification.go, internal/ai/demo.go and
  three findings test files.

Only dupl findings remain (44 pre-existing production-code duplication
pairs) — those need real refactors, not mechanical fixes.

Full test suites pass for every touched package.
2026-06-09 21:42:21 +01:00
rcourtman 2f918a3b03 metrics: skip checkpoint when freelist empty in reclaimFreePages
Follow-up to 2b8ce06c0: that commit's contract update states reclaim skips
the WAL checkpoint entirely when the freelist is empty, but the prior commit
captured the pre-refinement store.go (the re-stage was lost to a .git/index.lock
race). Make the code match the contract: return early on a freelist_count read
error and when freelist == 0, so a steady-state no-op cycle does not run an
extra hourly wal_checkpoint(TRUNCATE).
2026-06-05 13:32:44 +01:00
rcourtman 2b8ce06c03 metrics: drain freelist proportionally so metrics.db stops bloating
runRetention reclaimed only PRAGMA incremental_vacuum(5000) (~20MB) and
only when that hourly cycle deleted rows. On instances where an hourly
retention pass frees more than 5000 pages, the freelist grows net-positive
every cycle and the sqlite file bloats unboundedly (5GB+ of free pages over
~60MB of live data) even though row retention works. This is the documented
'50+ resources -> 5GB+' symptom the 5000-page batch was meant to fix.

Reclaim every cycle (so a pre-existing backlog drains even in a no-delete
hour) and size each reclaim to the current freelist, capped at 50000 pages
(~200MB) so a large backlog drains over several cycles without holding the
write lock for minutes. Skip the checkpoint when the freelist is empty so
steady-state WAL cadence is unchanged.

Surfaced by the demo server (pulse-relay): metrics.db had grown to 5.9GB
(5GB freelist), filling its 25GB disk and breaking the nightly backup.

Adds TestStoreRetentionReclaimsFreePages to the perf-and-scalability proof
set and documents the reclaim invariant in the subsystem contract.
2026-06-05 13:29:48 +01:00
rcourtman bd6f77e093 Prepare v6.0.0 release candidate
Tighten v5-to-v6 upgrade safety, release installability, provider MSP mode handling, AI cost accounting, metrics flushing, and frontend guardrails for the v6.0.0 GA candidate.
2026-06-04 14:07:14 +01:00
rcourtman 1019235b88 Coalesce duplicate metrics write batches 2026-05-13 13:19:16 +01:00
rcourtman edae6d1edc refactor: split alert config and callbacks
Extract alert config types, normalization, and identity helpers into internal/alerts/config while preserving the existing alerts package API through aliases and wrappers.

Move Manager callback lifecycle state into a same-package callbackBus, keeping public Set/Subscribe methods unchanged.

Harden metrics SQLite artifacts to owner-only permissions and cover permissive umask behavior.

Proof: go test -json ./internal/api -count=1; go test ./internal/alerts/... ./internal/monitoring ./internal/ai/... ./internal/websocket ./internal/config ./pkg/metrics; go test ./internal/alerts/... ./pkg/metrics
2026-05-06 13:01:32 +01:00
rcourtman db97478566 Reduce metrics rollup write amplification
Refs #1124
2026-05-03 21:43:20 +01:00
rcourtman 575f432183 Make metrics writes idempotent for duplicate samples
Refs #1442
2026-05-01 20:28:11 +01:00
rcourtman 29d96aec33 Reduce metrics store transaction churn
Refs #1124
2026-04-19 17:11:14 +01:00
rcourtman a928cce126 Add hosted-runner budget for metrics query SLO 2026-04-11 19:57:49 +01:00
rcourtman 11f9ea4479 Rebaseline concurrent metrics CI SLO 2026-04-11 19:30:09 +01:00
rcourtman fb4fb55461 Adjust metrics query SLO for GitHub runners 2026-04-11 17:02:07 +01:00
rcourtman 6372eccbf4 Rebaseline metrics store SLO envelopes 2026-04-11 15:23:02 +01:00
rcourtman 347a013e79 Stabilize RC release proof contracts 2026-04-11 14:51:10 +01:00
rcourtman 4ca488101e Optimize compact storage summary chart path 2026-04-10 19:09:12 +01:00
rcourtman cebf97f3b4 Calibrate metrics batch-query RC envelope 2026-04-09 23:43:36 +01:00
rcourtman b4199a4b96 Fix remaining RC backend proof drift 2026-04-09 23:06:49 +01:00
rcourtman a6ba806af9 Stabilize remaining RC dry-run proofs 2026-04-09 22:43:09 +01:00
rcourtman 6af7c18936 Calibrate remaining metrics CI SLO envelopes 2026-04-09 21:50:38 +01:00
rcourtman 0ec2dec65e Calibrate RC CI SLO envelopes 2026-04-09 21:27:03 +01:00
rcourtman 81fef82bdd Restore RC backend proof regressions 2026-04-09 20:15:17 +01:00
rcourtman 4e16f35347 Defer metrics store startup maintenance 2026-04-01 13:29:39 +01:00
rcourtman bf4820733e Canonicalize metrics store database paths 2026-03-29 15:35:49 +01:00
rcourtman 893d003c0a Merge origin/pulse/v6-release and fix reconciled API regressions 2026-03-28 21:14:54 +00:00
rcourtman 27507a47ad perf(metrics): keep downsampled series ordered 2026-03-27 12:16:31 +00:00
Richard Courtman 257dd55f12 perf(metrics): stream downsampled batch aggregation 2026-03-26 17:59:41 +00:00
Richard Courtman dbd470398a perf(metrics): reduce downsampled batch query tail latency 2026-03-26 17:37:25 +00:00
rcourtman b90fdc7e2c test(metrics): rebaseline github actions slo budgets 2026-03-26 13:51:08 +00:00
rcourtman 778a2577b6 feat: Pulse v6 release 2026-03-18 16:06:30 +00:00
rcourtman 9d8f8b45b5 fix(docker,metrics): preserve container metadata on update and reduce DB writes
Docker container URL preserved on update (#1054): container updates
recreate the container with a new runtime ID. The agent now includes
{oldContainerId, newContainerId} in the completion ACK payload; the
server uses this to copy persisted metadata (custom URLs, descriptions,
tags) to the new ID so nothing is lost. Migration is a copy, not a move,
so rollback scenarios still find metadata under the original ID.

Reduce metrics.db write amplification (#1124): add a UNIQUE index on
(resource_type, resource_id, metric_type, timestamp, tier) so rollup
reprocessing after a failed checkpoint uses INSERT OR IGNORE instead of
creating duplicate rows. Existing duplicates are deduplicated once on
startup if the index creation would otherwise fail. Also sets
wal_autocheckpoint(500) to checkpoint the WAL more frequently, preventing
unbounded WAL growth.

Fixes #1054
Fixes #1124
2026-02-18 12:56:46 +00:00
rcourtman c2d2e7de0e fix: run retention before auto-vacuum migration to reduce VACUUM cost
VACUUM creates a full copy of the database. Running retention first
deletes stale data (5GB → ~60MB live), so the VACUUM copies far less
data — faster startup and much less temporary disk space needed.
2026-02-11 13:37:29 +00:00
rcourtman 06a9f9694f fix: migrate existing metrics databases to incremental auto-vacuum
The auto_vacuum(INCREMENTAL) pragma from the previous commit only takes
effect on new databases. SQLite requires a full VACUUM to restructure
existing files when switching from NONE to INCREMENTAL. Without this,
users upgrading from bloated 5GB+ databases would never reclaim space.

Adds a one-time migration on startup that detects the current auto_vacuum
mode and runs VACUUM to convert if needed. Subsequent startups skip the
migration since the mode is already INCREMENTAL.
2026-02-11 13:35:03 +00:00
rcourtman 284bdd7ade fix: prevent metrics.db bloat with automatic vacuum and WAL checkpointing
The metrics database could grow to 5GB+ for modest setups because:
1. Retention deletes rows hourly but SQLite never reclaims the space
2. WAL file grows unbounded without explicit checkpointing
3. No cleanup runs on startup, so restarts accumulate stale data

Fixes:
- Enable auto_vacuum=INCREMENTAL so deleted pages can be reclaimed
- Run incremental_vacuum after each retention cleanup
- Force WAL checkpoint(TRUNCATE) after deletes to prevent WAL bloat
- Run retention on startup to clean stale data immediately

Expected DB size for a 50-resource setup drops from 5GB+ to ~60-70MB.

Ref: GitHub Discussion #1231
2026-02-10 23:13:32 +00:00
rcourtman 35eedcb5ac Fix: metrics store tier fallback for mock mode sparklines
When querying short time ranges (1h, 6h), the metrics store only looked
in TierRaw and TierMinute which were empty in mock mode. The seeded data
was stored in TierHourly and TierDaily.

Updated tierFallbacks to include coarser tiers as fallbacks:
- TierRaw now falls back to TierMinute, then TierHourly
- TierMinute now falls back to TierRaw, then TierHourly

This ensures sparkline data is available in mock/demo mode where
historical data is seeded into coarser tiers.
2026-02-03 12:03:06 +00:00
rcourtman ed5ab5eebf Fix: flaky metrics fallback test — use WriteBatchSync for deterministic writes 2026-02-02 23:32:28 +00:00
rcourtman eb2d07e48f Chore: enhance core api and metrics testability
Refactor Router to allow HTTP client injection for install script proxying. Add tests for unified agent install mechanism and additional metrics store coverage.
2026-02-02 22:01:36 +00:00
rcourtman 3b347b6548 fix: harden SQLite against I/O contention causing persistent lock errors
- Move all SQLite pragmas from db.Exec() to DSN parameters so every
  connection the pool creates gets busy_timeout and other settings.
  Previously only the first connection had these applied.
- Set MaxOpenConns(1) on audit, RBAC, and notification databases
  (metrics already had this). Fixes potential for multiple connections
  where new ones lack busy_timeout.
- Increase busy_timeout from 5s to 30s across all databases to
  tolerate disk I/O pressure during backup windows.
- Fix nested query deadlocks in GetRoles(), GetUserAssignments(), and
  CancelByAlertIDs() that would deadlock with MaxOpenConns(1).
- Fix circuit breaker retryInterval not resetting on recovery, which
  caused the next trip to start at 5-minute backoff instead of 5s.

Related to #1156
2026-02-02 17:29:14 +00:00
rcourtman 8963d69764 feat: add metrics store point limiting and mock improvements
- Add point limiting to metrics queries
- Improve mock metrics history for testing
- Add monitor enhancements
2026-01-22 22:29:56 +00:00
rcourtman 5f56efa88a Refactor: Core monitoring and update managers multi-tenancy
- Updated monitoring reload and metrics history to be tenant-aware
- Refactored update manager and checksum validation for multi-tenancy
- Enhanced test coverage for agent updates and metrics storage
2026-01-22 16:43:24 +00:00
rcourtman c75972d57c Fix mock metrics history and guest drawer controls 2026-01-22 09:39:53 +00:00
rcourtman c8b6cbfc6d feat(pro): long-term metrics history (30d/90d)
- Add FeatureLongTermMetrics license feature for Pro tier
- Implement tiered storage in metrics store (raw, minute, hourly, daily)
- Add covering index for unified history query performance
- Seed mock data for 90 days with appropriate aggregation tiers
- Update PULSE_PRO.md to document the feature
- 7-day history remains free, 30d/90d requires Pro license
2026-01-22 00:42:41 +00:00
rcourtman a6a8efaa65 test: Add comprehensive test coverage across packages
New test files with expanded coverage:

API tests:
- ai_handler_test.go: AI handler unit tests with mocking
- agent_profiles_tools_test.go: Profile management tests
- alerts_endpoints_test.go: Alert API endpoint tests
- alerts_test.go: Updated for interface changes
- audit_handlers_test.go: Audit handler tests
- frontend_embed_test.go: Frontend embedding tests
- metadata_handlers_test.go, metadata_provider_test.go: Metadata tests
- notifications_test.go: Updated for interface changes
- profile_suggestions_test.go: Profile suggestion tests
- saml_service_test.go: SAML authentication tests
- sensor_proxy_gate_test.go: Sensor proxy tests
- updates_test.go: Updated for interface changes

Agent tests:
- dockeragent/signature_test.go: Docker agent signature tests
- hostagent/agent_metrics_test.go: Host agent metrics tests
- hostagent/commands_test.go: Command execution tests
- hostagent/network_helpers_test.go: Network helper tests
- hostagent/proxmox_setup_test.go: Updated setup tests
- kubernetesagent/*_test.go: Kubernetes agent tests

Core package tests:
- monitoring/kubernetes_agents_test.go, reload_test.go
- remoteconfig/client_test.go, signature_test.go
- sensors/collector_test.go
- updates/adapter_installsh_*_test.go: Install adapter tests
- updates/manager_*_test.go: Update manager tests
- websocket/hub_*_test.go: WebSocket hub tests

Library tests:
- pkg/audit/export_test.go: Audit export tests
- pkg/metrics/store_test.go: Metrics store tests
- pkg/proxmox/*_test.go: Proxmox client tests
- pkg/reporting/reporting_test.go: Reporting tests
- pkg/server/*_test.go: Server tests
- pkg/tlsutil/extra_test.go: TLS utility tests

Total: ~8000 lines of new test code
2026-01-19 19:26:18 +00:00
rcourtman 2a8f55d719 feat(enterprise): add Advanced Reporting and Audit Webhooks integration
This commit adds enterprise-grade reporting and audit capabilities:

Reporting:
- Refactored metrics store from internal/ to pkg/ for enterprise access
- Added pkg/reporting with shared interfaces for report generation
- Created API endpoint: GET /api/admin/reports/generate
- New ReportingPanel.tsx for PDF/CSV report configuration

Audit Webhooks:
- Extended pkg/audit with webhook URL management interface
- Added API endpoint: GET/POST /api/admin/webhooks/audit
- New AuditWebhookPanel.tsx for webhook configuration
- Updated Settings.tsx with Reporting and Webhooks tabs

Server Hardening:
- Enterprise hooks now execute outside mutex with panic recovery
- Removed dbPath from metrics Stats API to prevent path disclosure
- Added storage metrics persistence to polling loop

Documentation:
- Updated README.md feature table
- Updated docs/API.md with new endpoints
- Updated docs/PULSE_PRO.md with feature descriptions
- Updated docs/WEBHOOKS.md with audit webhooks section
2026-01-09 21:31:49 +00:00