feat(api): reuse connections, add timeouts, and stamp emissions with measurement time - #1340
davidberenstein1957 wants to merge 4 commits into
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #1340 +/- ##
==========================================
+ Coverage 91.43% 91.77% +0.34%
==========================================
Files 49 49
Lines 5057 5181 +124
==========================================
+ Hits 4624 4755 +131
+ Misses 433 426 -7 ☔ 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
…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>
e9cecd4 to
7b566a8
Compare
Verdict: ✅ Approve with nits (please merge #1339 first)Session reuse (with Issues:
Nit:
|
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>
|
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. |
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. The final flush ontracker.stop()/exit bypasses this cooldown and retries run creation once, so the last emission is not silently dropped just because it landed inside the cooldown window. During the cooldown, each skipped tick logs at DEBUG instead of ERROR.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.