Skip to content

Share a single vcgencmd runner between temperature and throttled collectors - #96

Merged
LarsLaskowski merged 2 commits into
mainfrom
claude/issue-66-vq18k1
Aug 21, 2026
Merged

Share a single vcgencmd runner between temperature and throttled collectors#96
LarsLaskowski merged 2 commits into
mainfrom
claude/issue-66-vq18k1

Conversation

@LarsLaskowski

@LarsLaskowski LarsLaskowski commented Aug 21, 2026

Copy link
Copy Markdown
Owner

📖 Description

TemperatureCollector (measure_temp) and ThrottledCollector (get_throttled) each implemented their own copy of the lazy vcgencmd detection/re-detection logic (exec.LookPath("vcgencmd"), throttled retry via detectRetryInterval) — duplicated verbatim between the two files.

This extracts a shared vcgencmdRunner type (internal/collector/vcgencmd.go) that owns detection/re-detection and a run(ctx, subcommand) helper, and has both collectors delegate to a single shared instance (constructed once in collector.New). The duplicated logic now lives in one place, and vcgencmd is looked up once total instead of once per collector.

Scope note on the issue's "halves the exec rate" suggestion: true single-exec batching of measure_temp and get_throttled isn't possible — vcgencmd only accepts one subcommand per invocation, so both collectors still shell out separately on the fast-tick hot path (this is unchanged from before). This PR targets the actionable, achievable part of the finding: eliminating the duplicated detection logic and the redundant exec.LookPath calls.

🎫 Issues

Closes #66

👩‍💻 Reviewer Notes

  • internal/collector/vcgencmd.go is the new shared type; temperature.go and throttled.go were trimmed down to delegate to it via a vcg *vcgencmdRunner field.
  • ThrottledCollector and TemperatureCollector both treat a nil vcg (or an unresolved vcgencmd) the same way as before: no GPU/throttled reading, no error — off-Pi/dev-machine behavior is unchanged.
  • Not verifiable on real Pi hardware in this environment; the behavior change is confined to internal wiring (shared detection state) and is covered by unit tests using fixture-style fakes, consistent with the rest of the collector package (no real vcgencmd//proc//sys access in tests, per docs/TESTS.md).

📑 Test Plan

  • Added internal/collector/vcgencmd_test.go covering the new runner: unavailable-without-exec, throttled re-detection, and re-detection after the throttle window elapses.
  • Updated temperature_test.go / throttled_test.go for the new field shape (vcg *vcgencmdRunner instead of per-collector vcgencmd fields); added TestThrottledCollector_Collect_NilRunner.
  • go build ./..., go vet ./..., go test ./... -race -cover, and golangci-lint run all pass locally.

✅ Checklist

General

  • I have added/updated tests for my changes (go test ./... -race -cover passes locally).
  • go vet ./... and golangci-lint run are clean.
  • I have tested my changes.
  • I have read the CONTRIBUTING documentation and followed the project's code style guidelines.
  • I have updated ARCHITECTURE.md if this changes a documented design decision. — Not needed: the architecture doc's description of the collector/vcgencmd relationship (one Collect() method per metric family, optionally shelling out to vcgencmd) is unchanged; this is an internal implementation detail.

REST API / configuration / packaging

Not applicable — no REST API, configuration, or packaging changes.

⏭ Next Steps

None.

claude added 2 commits August 21, 2026 16:01
…collectors

TemperatureCollector and ThrottledCollector each implemented their own
lazy vcgencmd detection/re-detection logic (exec.LookPath, throttled
retry) verbatim. Extract that into a shared vcgencmdRunner type used by
both, so the duplicated code collapses to one place and vcgencmd is
looked up once (shared across both collectors) instead of once per
collector.

True single-exec batching of measure_temp and get_throttled isn't
possible: vcgencmd only accepts one subcommand per invocation, so both
collectors still shell out separately on the hot path. This change
targets the actionable part of issue #66 - the duplicated
detection/re-detection logic - rather than a batching scheme vcgencmd
doesn't support.
The previous commit's tests never actually ran a subprocess through
vcgencmdRunner.run(): every test kept vcg nil or pre-set "unavailable",
so the real exec.CommandContext success/failure paths, and
exec.LookPath's success path, were untested new code and tripped
SonarCloud's coverage gate.

Add a writeFakeVcgencmd test helper (mirroring the existing aptPath:
"true" stub-command pattern in updates_test.go) that writes a small
executable shell script standing in for vcgencmd, and use it to
exercise: vcgencmdRunner.run() executing successfully and failing,
redetectLocked() finding a binary via PATH, and the resulting
success/failure/malformed-output behavior through
TemperatureCollector.Collect and ThrottledCollector.Collect end to end.
@sonarqubecloud

Copy link
Copy Markdown

@LarsLaskowski
LarsLaskowski merged commit a691ba7 into main Aug 21, 2026
5 checks passed
@LarsLaskowski
LarsLaskowski deleted the claude/issue-66-vq18k1 branch August 21, 2026 16:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Performance: vcgencmd is spawned twice per fast tick (measure_temp + get_throttled)

2 participants