perf(daemon): record render coordinator burst metrics
- commit
- 0e2e6e99a42af3c45862b0b14e1377253349bf05
- author
- brice · · 2026-07-11 15:09 UTC
- parent
- df0fe5f
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
2 files · +22 -3
M internal/usecase/daemon/render_coordinator.go +20 -1
| @@ -3,6 +3,7 @@ package daemon | |||
| 3 | 3 | import ( | |
| 4 | 4 | "runtime" | |
| 5 | 5 | "sync" | |
| 6 | + | "sync/atomic" | |
| 6 | 7 | "time" | |
| 7 | 8 | ||
| 8 | 9 | "github.com/bnema/vev/internal/domain" | |
| @@ -82,6 +83,15 @@ type renderCoordinatorOptions struct { | |||
| 82 | 83 | } | |
| 83 | 84 | ||
| 84 | 85 | // renderCoordinator fans in producer invalidations for one attached session. | |
| 86 | + | // renderCoordinatorBurstMetrics is deliberately observational. It permits | |
| 87 | + | // benchmark/pprof callers to measure producer bursts without influencing | |
| 88 | + | // scheduling or transport policy. | |
| 89 | + | type renderCoordinatorBurstMetrics struct { | |
| 90 | + | invalidations atomic.Uint64 | |
| 91 | + | wakes atomic.Uint64 | |
| 92 | + | coalesced atomic.Uint64 | |
| 93 | + | } | |
| 94 | + | ||
| 85 | 95 | type renderCoordinator struct { | |
| 86 | 96 | mu sync.Mutex | |
| 87 | 97 | opts renderCoordinatorOptions | |
| @@ -102,7 +112,8 @@ type renderCoordinator struct { | |||
| 102 | 112 | attachment *attachedClient | |
| 103 | 113 | torndown bool | |
| 104 | 114 | ||
| 105 | - | resize resizeRequestMetadata | |
| 115 | + | resize resizeRequestMetadata | |
| 116 | + | metrics renderCoordinatorBurstMetrics | |
| 106 | 117 | // generation invalidates callbacks from superseded deadline/watchdog timers. | |
| 107 | 118 | generation uint64 | |
| 108 | 119 | armed bool | |
| @@ -127,6 +138,7 @@ func (c *renderCoordinator) invalidate(inv renderInvalidation) { | |||
| 127 | 138 | if c.opts.onInvalidate != nil { | |
| 128 | 139 | c.opts.onInvalidate(inv) | |
| 129 | 140 | } | |
| 141 | + | c.metrics.invalidations.Add(1) | |
| 130 | 142 | wasPending, wasUrgent := c.pending, c.pendingUrgent | |
| 131 | 143 | c.pending = true | |
| 132 | 144 | c.pendingReset = c.pendingReset || inv.reset | |
| @@ -281,6 +293,11 @@ func (c *renderCoordinator) resizeSnapshot() resizeRequestMetadata { | |||
| 281 | 293 | return c.resize | |
| 282 | 294 | } | |
| 283 | 295 | ||
| 296 | + | // burstMetrics returns an observational snapshot for benchmark reporting. | |
| 297 | + | func (c *renderCoordinator) burstMetrics() (invalidations, wakes, coalesced uint64) { | |
| 298 | + | return c.metrics.invalidations.Load(), c.metrics.wakes.Load(), c.metrics.coalesced.Load() | |
| 299 | + | } | |
| 300 | + | ||
| 284 | 301 | // renderCoordinator returns the coordinator installed for this session. | |
| 285 | 302 | func (s *session) renderCoordinator() *renderCoordinator { return s.coordinator.Load() } | |
| 286 | 303 | ||
| @@ -322,6 +339,8 @@ func (c *renderCoordinator) fire(gen uint64, watchdog bool) { | |||
| 322 | 339 | return | |
| 323 | 340 | } | |
| 324 | 341 | w := renderWake{reset: c.pendingReset, urgent: c.pendingUrgent, coalesced: c.coalesced, watchdog: watchdog} | |
| 342 | + | c.metrics.wakes.Add(1) | |
| 343 | + | c.metrics.coalesced.Add(uint64(w.coalesced)) | |
| 325 | 344 | c.pending, c.pendingReset, c.pendingUrgent, c.coalesced, c.armed = false, false, false, 0, false | |
| 326 | 345 | wake, preview := c.opts.wake, c.previewWake | |
| 327 | 346 | c.mu.Unlock() | |
| @@ -3,6 +3,7 @@ package daemon | |||||
| 3 | import ( | 3 | import ( | ||
| 4 | "runtime" | 4 | "runtime" | ||
| 5 | "sync" | 5 | "sync" | ||
| 6 | + | "sync/atomic" | |||
| 6 | "time" | 7 | "time" | ||
| 7 | 8 | ||||
| 8 | "github.com/bnema/vev/internal/domain" | 9 | "github.com/bnema/vev/internal/domain" | ||
| @@ -82,6 +83,15 @@ type renderCoordinatorOptions struct { | |||||
| 82 | } | 83 | } | ||
| 83 | 84 | ||||
| 84 | // renderCoordinator fans in producer invalidations for one attached session. | 85 | // renderCoordinator fans in producer invalidations for one attached session. | ||
| 86 | + | // renderCoordinatorBurstMetrics is deliberately observational. It permits | |||
| 87 | + | // benchmark/pprof callers to measure producer bursts without influencing | |||
| 88 | + | // scheduling or transport policy. | |||
| 89 | + | type renderCoordinatorBurstMetrics struct { | |||
| 90 | + | invalidations atomic.Uint64 | |||
| 91 | + | wakes atomic.Uint64 | |||
| 92 | + | coalesced atomic.Uint64 | |||
| 93 | + | } | |||
| 94 | + | ||||
| 85 | type renderCoordinator struct { | 95 | type renderCoordinator struct { | ||
| 86 | mu sync.Mutex | 96 | mu sync.Mutex | ||
| 87 | opts renderCoordinatorOptions | 97 | opts renderCoordinatorOptions | ||
| @@ -102,7 +112,8 @@ type renderCoordinator struct { | |||||
| 102 | attachment *attachedClient | 112 | attachment *attachedClient | ||
| 103 | torndown bool | 113 | torndown bool | ||
| 104 | 114 | ||||
| 105 | - | resize resizeRequestMetadata | 115 | + | resize resizeRequestMetadata |
| 116 | + | metrics renderCoordinatorBurstMetrics | |||
| 106 | // generation invalidates callbacks from superseded deadline/watchdog timers. | 117 | // generation invalidates callbacks from superseded deadline/watchdog timers. | ||
| 107 | generation uint64 | 118 | generation uint64 | ||
| 108 | armed bool | 119 | armed bool | ||
| @@ -127,6 +138,7 @@ func (c *renderCoordinator) invalidate(inv renderInvalidation) { | |||||
| 127 | if c.opts.onInvalidate != nil { | 138 | if c.opts.onInvalidate != nil { | ||
| 128 | c.opts.onInvalidate(inv) | 139 | c.opts.onInvalidate(inv) | ||
| 129 | } | 140 | } | ||
| 141 | + | c.metrics.invalidations.Add(1) | |||
| 130 | wasPending, wasUrgent := c.pending, c.pendingUrgent | 142 | wasPending, wasUrgent := c.pending, c.pendingUrgent | ||
| 131 | c.pending = true | 143 | c.pending = true | ||
| 132 | c.pendingReset = c.pendingReset || inv.reset | 144 | c.pendingReset = c.pendingReset || inv.reset | ||
| @@ -281,6 +293,11 @@ func (c *renderCoordinator) resizeSnapshot() resizeRequestMetadata { | |||||
| 281 | return c.resize | 293 | return c.resize | ||
| 282 | } | 294 | } | ||
| 283 | 295 | ||||
| 296 | + | // burstMetrics returns an observational snapshot for benchmark reporting. | |||
| 297 | + | func (c *renderCoordinator) burstMetrics() (invalidations, wakes, coalesced uint64) { | |||
| 298 | + | return c.metrics.invalidations.Load(), c.metrics.wakes.Load(), c.metrics.coalesced.Load() | |||
| 299 | + | } | |||
| 300 | + | ||||
| 284 | // renderCoordinator returns the coordinator installed for this session. | 301 | // renderCoordinator returns the coordinator installed for this session. | ||
| 285 | func (s *session) renderCoordinator() *renderCoordinator { return s.coordinator.Load() } | 302 | func (s *session) renderCoordinator() *renderCoordinator { return s.coordinator.Load() } | ||
| 286 | 303 | ||||
| @@ -322,6 +339,8 @@ func (c *renderCoordinator) fire(gen uint64, watchdog bool) { | |||||
| 322 | return | 339 | return | ||
| 323 | } | 340 | } | ||
| 324 | w := renderWake{reset: c.pendingReset, urgent: c.pendingUrgent, coalesced: c.coalesced, watchdog: watchdog} | 341 | w := renderWake{reset: c.pendingReset, urgent: c.pendingUrgent, coalesced: c.coalesced, watchdog: watchdog} | ||
| 342 | + | c.metrics.wakes.Add(1) | |||
| 343 | + | c.metrics.coalesced.Add(uint64(w.coalesced)) | |||
| 325 | c.pending, c.pendingReset, c.pendingUrgent, c.coalesced, c.armed = false, false, false, 0, false | 344 | c.pending, c.pendingReset, c.pendingUrgent, c.coalesced, c.armed = false, false, false, 0, false | ||
| 326 | wake, preview := c.opts.wake, c.previewWake | 345 | wake, preview := c.opts.wake, c.previewWake | ||
| 327 | c.mu.Unlock() | 346 | c.mu.Unlock() | ||
M internal/usecase/daemon/render_coordinator_test.go +2 -2
| @@ -67,7 +67,7 @@ func (h *coordinatorHarness) armedTimers(t *testing.T) []*signalTimer { | |||
| 67 | 67 | // deterministic contract failure, not a slow behavior to poll for. | |
| 68 | 68 | func awaitWake(t *testing.T, ch chan renderWake) renderWake { | |
| 69 | 69 | t.Helper() | |
| 70 | - | for range 64 { | |
| 70 | + | for range 4096 { | |
| 71 | 71 | select { | |
| 72 | 72 | case w := <-ch: | |
| 73 | 73 | return w | |
| @@ -90,7 +90,7 @@ func requireNoWake(t *testing.T, ch chan renderWake) { | |||
| 90 | 90 | ||
| 91 | 91 | func awaitInvalidation(t *testing.T, ch chan renderInvalidation) renderInvalidation { | |
| 92 | 92 | t.Helper() | |
| 93 | - | for range 64 { | |
| 93 | + | for range 4096 { | |
| 94 | 94 | select { | |
| 95 | 95 | case inv := <-ch: | |
| 96 | 96 | return inv | |
| @@ -67,7 +67,7 @@ func (h *coordinatorHarness) armedTimers(t *testing.T) []*signalTimer { | |||||
| 67 | // deterministic contract failure, not a slow behavior to poll for. | 67 | // deterministic contract failure, not a slow behavior to poll for. | ||
| 68 | func awaitWake(t *testing.T, ch chan renderWake) renderWake { | 68 | func awaitWake(t *testing.T, ch chan renderWake) renderWake { | ||
| 69 | t.Helper() | 69 | t.Helper() | ||
| 70 | - | for range 64 { | 70 | + | for range 4096 { |
| 71 | select { | 71 | select { | ||
| 72 | case w := <-ch: | 72 | case w := <-ch: | ||
| 73 | return w | 73 | return w | ||
| @@ -90,7 +90,7 @@ func requireNoWake(t *testing.T, ch chan renderWake) { | |||||
| 90 | 90 | ||||
| 91 | func awaitInvalidation(t *testing.T, ch chan renderInvalidation) renderInvalidation { | 91 | func awaitInvalidation(t *testing.T, ch chan renderInvalidation) renderInvalidation { | ||
| 92 | t.Helper() | 92 | t.Helper() | ||
| 93 | - | for range 64 { | 93 | + | for range 4096 { |
| 94 | select { | 94 | select { | ||
| 95 | case inv := <-ch: | 95 | case inv := <-ch: | ||
| 96 | return inv | 96 | return inv | ||