Skip to content

Commit a7f0b7e

Browse files
rustyconoverclaude
andcommitted
fix: telemetry no longer blocks extension load
std::async returns a future whose destructor blocks until the task completes, so binding it to a local made the telemetry POST effectively synchronous: extension loading waited for the HTTP request. With the previous defaults (30s timeout, 3 retries, 4x backoff) the worst case was roughly two minutes of blocked LOAD. Use a detached std::thread instead, and bound the request to a 10s timeout with retries disabled, since telemetry is best-effort. Bump version to 2026072501. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent c9ef842 commit a7f0b7e

2 files changed

Lines changed: 11 additions & 6 deletions

File tree

src/airport_extension.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
#include "airport_logging.hpp"
1818
#include "query_farm_telemetry.hpp"
1919

20-
#define AIRPORT_EXTENSION_VERSION "2026042701"
20+
#define AIRPORT_EXTENSION_VERSION "2026072501"
2121

2222
namespace duckdb
2323
{

src/query_farm_telemetry.cpp

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
#include "duckdb/main/extension_helper.hpp"
77
#include "duckdb/main/config.hpp"
88
#include <cstdlib>
9-
#include <future>
109
using namespace duckdb_yyjson; // NOLINT
1110

1211
namespace duckdb
@@ -25,6 +24,10 @@ namespace duckdb
2524

2625
auto &http_util = HTTPUtil::Get(*db);
2726
unique_ptr<HTTPParams> params = http_util.InitializeParameters(*db, TARGET_URL);
27+
// Telemetry is best-effort: bound the request so a stalled connection cannot
28+
// keep the detached thread alive. No retries -- a dropped ping is not worth one.
29+
params->timeout = 10;
30+
params->retries = 0;
2831

2932
PostRequestInfo post_request(TARGET_URL, headers, *params, reinterpret_cast<const_data_ptr_t>(json_body),
3033
json_body_size);
@@ -94,10 +97,12 @@ namespace duckdb
9497
yyjson_mut_doc_free(doc);
9598

9699
#ifndef __EMSCRIPTEN__
97-
[[maybe_unused]] auto _ = std::async(
98-
std::launch::async, [db_ptr = loader.GetDatabaseInstance().shared_from_this(), json = telemetry_data,
99-
len = telemetry_len]() mutable
100-
{ sendHTTPRequest(std::move(db_ptr), json, len); });
100+
// Fire-and-forget. Deliberately not std::async: the future it returns has a
101+
// blocking destructor, which would make the caller wait on this HTTP request.
102+
std::thread([db_ptr = loader.GetDatabaseInstance().shared_from_this(), json = telemetry_data,
103+
len = telemetry_len]() mutable
104+
{ sendHTTPRequest(std::move(db_ptr), json, len); })
105+
.detach();
101106
#else
102107
sendHTTPRequest(loader.GetDatabaseInstance().shared_from_this(), telemetry_data, telemetry_len);
103108
#endif

0 commit comments

Comments
 (0)