Repository navigation
feat(api): reuse connections, add timeouts, and stamp emissions with measurement time - #1340
davidberenstein1957 wants to merge 5 commits into
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #1340 +/- ##
==========================================
+ Coverage 93.66% 93.74% +0.07%
==========================================
Files 50 50
Lines 5591 5613 +22
==========================================
+ Hits 5237 5262 +25
+ Misses 354 351 -3 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
a53dcb9 to
863b9c3
Compare
863b9c3 to
76e81b7
Compare
0658642 to
697e1e8
Compare
76e81b7 to
ec517ea
Compare
e9cecd4 to
7b566a8
Compare
Verdict: ✅ Approve with nits (please merge #1339 first)Session reuse (with Issues:
Nit:
|
|
Made the changes in 72c53b7: 60 s cooldown after a failed run creation. On the 2 x 13 s concern: Not done: rewriting the description (my edit was blocked; the retry/config/backoff sections are stale, the diff is Session + timeouts + measurement timestamp + cooldown). Skipped the DST nit: making the tracker timestamp offset-aware changes the CSV timestamp format, which needs a maintainer call. |
|
Deferring the DST timestamp nit: making the timestamp timezone-aware at the source changes the CSV timestamp format, so it will ship in the next minor release together with #1334 rather than in this PR. |
…measurement time `ApiClient` called the module-level `requests` functions, so every call opened a new TCP connection and TLS handshake. A tracker sending one measurement per tick opened 50 connections for 50 uploads; with a `Session` it opens 1. The flat 2s timeout is replaced with `(3.05, 10)`: the old value timed out against a healthy but loaded API, while a hung endpoint still cannot block the scheduler thread for long. `ApiClient.add_emission` also discarded `carbon_emission["timestamp"]` and called `get_datetime_with_timezone()` instead, so every row stored the moment the payload was built rather than the moment it was measured. Harmless while the two are milliseconds apart, wrong by the full latency as soon as a send is slow, retried or queued. The measurement timestamp is now normalised to offset-aware, falling back to now when the payload carries none or an unparseable one (the method is public and takes a plain dict). CSV output is untouched. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A down API now costs one blocking run-creation call per minute instead of one per measurement tick. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…a stop() retry loop out() only runs on an explicit flush() or stop(); the periodic ticks use live_out(). So CodeCarbonAPIOutput.out() now always bypasses the cooldown, and _emit() skips the emission at debug level when run creation is in its cooldown. This removes the second out(final=True) pass in stop(), its monotonic-timestamp bookkeeping and the http import in emissions_tracker, and the `final` parameter of ApiClient.add_emission. Stop still makes a single run-creation attempt when the API is down. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JfRKgUd281URY6fMyNAQmh
The 422 retry in add_emission built its own requests.post calls, duplicating the timeout and headers from _request. Extract the raw call into _send, used by both, so request settings (and #1340's session) live in one place. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JfRKgUd281URY6fMyNAQmh
38be0d9 to
0c7231b
Compare
|
Rebased onto Rebase
Fix added: The extra send of the last emission on
Review feedback
Tests and coverage
Docs: no change needed. Left for the maintainer
Generated by Claude Code |
The 422 retry in add_emission built its own requests.post calls, duplicating the timeout and headers from _request. Extract the raw call into _send, used by both, so request settings (and #1340's session) live in one place. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JfRKgUd281URY6fMyNAQmh
Description
ApiClient._requestcalled barerequests.get/post/patchwith a hardcodedtimeout=2and noSession, so every emission POST opened a new TCP connection. This PR:requests.SessionperApiClient, so the socket and TLS handshake are reused.ApiClient.close()closes it, andCodeCarbonAPIOutput.exit()calls it on tracker stop.timeout=2with a(3.05, 10)connect/read timeout.EmissionsData.timestamp) instead of the time it was sent. A missing or unparseable timestamp falls back to now._create_runre-raises every failure, so_emitstops after the first attempt, and with the cooldown a down API costs one call per minute instead of one per measurement tick.out(), which runs onflush()/stop(), bypasses this cooldown and tries run creation once, so the last emission is not dropped; periodiclive_out()ticks respect it and log the skip at DEBUG.duration < 1check still drops that row. fix: send and store real sub-second emission durations #1374 removes that check, so merge fix: send and store real sub-second emission durations #1374 before or with this PR.An earlier version of this description mentioned a
Retryadapter andapi_timeout/api_retriesconfig options; neither is in the current diff. The measurement-timestamp change from #1341 (now closed) is included here.Related Issue
Part of #1338
Motivation and Context
Without connection reuse, every emission POST paid a TCP (and TLS) handshake. When the API is down, each tick used to retry run creation and wait for the timeout. Emissions sent late were stamped with the send time, which shifted them on the dashboard.
How Has This Been Tested?
tests/test_api_client_session.py(new): runs against a stdlib loopback server rather thanrequests_mock, which bypasses the transport layer. It covers sequential calls reusing one connection,close()being idempotent, and requests getting the connect/read timeout.tests/test_api_call.py:test_add_emission_keeps_measurement_timestamp(naive, missing and unparseable timestamps) andtest_failed_create_run_is_not_retried_during_cooldown(no retry inside the cooldown, retry after it).CI is green on this branch.
Screenshots (if appropriate):
N/A
Types of changes
AI Usage Disclosure
Checklist:
Deferred: making the tracker's own timestamp timezone-aware (to fix the DST edge case). That changes the CSV timestamp format, so it will ship in the next minor release together with #1334. Not included: moving the direct
requests.getcalls inelectricitymaps_api/geography.pyonto the shared session.