Tracker cannot be restarted: start() after stop() is a no-op and the next row's duration is wrong
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 1.9k
- Forks
- 323
- Avg merge
- 1d 12h
- Merged PRs (30d)
- 12
Description
Problem
A tracker cannot be restarted. After stop(), a subsequent start() is a no-op ("Already started tracking"), and the next stop() still writes a row — but that row's duration covers everything since the original start(), including the idle gap, and the phase was never sampled. This is the natural pattern for benchmarking loops ("measure phase A, stop, measure phase B") and for notebooks where a cell is re-run.
Reproduction
tracker = OfflineEmissionsTracker(country_iso_code="FRA", measure_power_secs=1)
tracker.start(); time.sleep(3); tracker.stop() # row 1: duration ~3
time.sleep(3)
tracker.start() # logs "Already started tracking", does nothing
time.sleep(3); tracker.stop() # row 2: duration ~9, unsampled
assert tracker._scheduler is None # no periodic measurement at all in phase B
Root cause
stop() tears down both schedulers but never clears the start time (codecarbon/emissions_tracker.py:909-914):
if self._scheduler:
self._scheduler.stop()
self._scheduler = None
if self._scheduler_monitor_power:
self._scheduler_monitor_power.stop()
self._scheduler_monitor_power = None
start() then bails out because _start_time is still set (emissions_tracker.py:708-710), and even if that guard is bypassed, self._scheduler.start() at emissions_tracker.py:725 raises AttributeError on None, which the @suppress(Exception) decorator on start() turns into a warning. _initialize_scheduler_state() (emissions_tracker.py:344) is only ever called from __init__ (emissions_tracker.py:604), so the schedulers are never rebuilt.
Lifecycle state is split across _start_time, _scheduler and _scheduler_monitor_power with no single reset path.
Expected vs actual
Expected: start() after stop() resumes tracking; the periodic measurement runs again and the next row's duration reflects active tracking time.
Actual: the second start() is silently a no-op, phase B gets a single catch-all measurement from _measure_power_and_energy_if_stale() at stop(), and the row's duration includes the paused interval — so emissions_rate for that row is wrong too.
Measured on current master (measure_power_secs=1, 3 s per phase): row 1 duration=3.088958, row 2 duration=6.104781.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in codecarbon/emissions_tracker.py, especially start() at lines 708-725, stop() at lines 909-914, and _initialize_scheduler_state() at line 344. Trace how _start_time and both scheduler fields change across a start-stop-start cycle. Done means a second start resumes periodic measurement and the next row reports active tracking duration without the idle gap.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- backend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 68/100