Skip to content

Support/rdkemw 21295#106

Open
brendanobra wants to merge 2 commits into
developfrom
support/RDKEMW-21295
Open

Support/rdkemw 21295#106
brendanobra wants to merge 2 commits into
developfrom
support/RDKEMW-21295

Conversation

@brendanobra

Copy link
Copy Markdown
Contributor

Fix: Increase default waitTime_ms to 5000ms and add FIREBOLT_WAIT_TIME_MS env override

Measured issue from prod logs

The previous default waitTime_ms=3000ms could cause false timeouts when a backend operation legitimately takes close to (but under) the threshold. Measured real-world latency for certain Firebolt API calls in production reached p99=2493ms, leaving only a 507ms margin before the checkPromises() watchdog rejected the in-flight request as Error::Timedout.

When checkPromises() fires before the server response arrives, the caller receives a false timeout even though the server completed the operation successfully. The caller may then disconnect, reconnect, and retry — repeating multiple times and blocking the calling thread unnecessarily.

The same false-timeout cycle can cause duplicate API submissions when the caller retries an operation the server already processed.

Changes

include/firebolt/config.h.in

  • Default waitTime_ms increased from 3000ms → 5000ms
  • Added explanatory comment

src/gateway.cpp

  • Added FIREBOLT_WAIT_TIME_MS environment variable override read at connect() time
  • Accepts values 1–120000ms; invalid/zero/out-of-range values are rejected with a warning log and the config value is preserved
  • Allows runtime tuning without a rebuild for deployments where backend latency characteristics are known

test/unit/gatewayTest.cpp — 7 new tests (136 total, all passing):

  • RequestFalseTimeoutWhenServerRespondsLatenegative/bug: server responds after the timeout fires; caller receives Error::Timedout despite server success. Directly models the defect.
  • EnvWaitTimeMsFixesFalseTimeoutpositive/fix: same conditions with FIREBOLT_WAIT_TIME_MS=5000; response arrives in time and call succeeds.
  • EnvWaitTimeMsOverridesConfig — valid env var replaces config value
  • EnvWaitTimeMsInvalidValuesIgnored — non-numeric input ignored
  • EnvWaitTimeMsZeroIgnored — zero rejected
  • EnvWaitTimeMsAboveCapIgnored — value > 120000 rejected
  • NoEnvWaitTimeMsUsesConfigValue — absent env var leaves config unchanged (regression guard)

Why 5000ms

Value
Observed p99 backend latency 2493 ms
watchdogCycle_ms 500 ms
Minimum safe threshold 2993 ms
New default 5000 ms
Headroom above threshold 2007 ms (~2×)

Side effects

  • Normal operation (calls returning < 3000ms): No change. waitTime_ms is a deadline, not a delay.
  • True backend outage (no response): Per-attempt failure detection is 2s slower (5500ms vs 3500ms). However, retry amplification is eliminated — worst-case total blocking time improves (1×5.5s vs N×3.5s).
  • Connection establishment: Unaffected — governed by connect_attempt_timeout_ms (separate field, unchanged at 10000ms).
  • All other Firebolt APIs: Unaffected — fast calls resolve well under 100ms regardless of this value.

Testing

./test.sh      # 136/136 pass
./fmt.sh       # 0 lint errors

Copilot AI review requested due to automatic review settings July 19, 2026 18:00

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adjusts the Firebolt C++ transport’s JSON-RPC request timeout behavior to reduce false client-side timeouts observed in production by increasing the default response deadline and adding a runtime environment override.

Changes:

  • Increase default Config::waitTime_ms from 3000ms to 5000ms (with updated rationale in the public config header template).
  • Add FIREBOLT_WAIT_TIME_MS environment-variable override handling in GatewayImpl::connect() (validated range, warning on invalid values).
  • Add unit tests modeling the false-timeout scenario and validating the env override behavior.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 15 comments.

Show a summary per file
File Description
include/firebolt/config.h.in Raises the default RPC response timeout to 5000ms and documents the motivation/override behavior.
src/gateway.cpp Implements FIREBOLT_WAIT_TIME_MS parsing and applies it to the runtime wait-time used by the watchdog timeout logic.
test/unit/gatewayTest.cpp Adds tests reproducing the false-timeout scenario and verifying env-var override behavior and guardrails.
src/utils.cpp Modifies buildGatewayUrl() query-param handling (and currently adds debug stderr prints).
src/transport.cpp Adds an additional connect-time URL log line (currently logs the full, unredacted URL).

Comment thread src/utils.cpp
Comment on lines +29 to +31
// DEBUG: Log input
fprintf(stderr, "[buildGatewayUrl] input='%s' legacyRPCv1=%d\n", url.c_str(), legacyRPCv1);

Comment thread src/utils.cpp
Comment on lines +47 to 67
// Only add RPCv2=true if it's not already present
size_t rpcv2Pos = url.find("RPCv2=true");
fprintf(stderr, "[buildGatewayUrl] checking RPCv2: pos=%zu (npos=%zu)\n", rpcv2Pos, std::string::npos);

if (rpcv2Pos == std::string::npos)
{
url += "?";
fprintf(stderr, "[buildGatewayUrl] RPCv2 not found, adding...\n");
if (url.find('?', hostStart) == std::string::npos)
{
url += "?";
}
else
{
url += "&";
}
url += "RPCv2=true";
}
else
{
url += "&";
fprintf(stderr, "[buildGatewayUrl] RPCv2 already present, skipping\n");
}
Comment thread src/utils.cpp
Comment on lines +70 to +71
// DEBUG: Log output
fprintf(stderr, "[buildGatewayUrl] output='%s'\n", url.c_str());
Comment thread src/transport.cpp
FIREBOLT_LOG_DEBUG("Transport", "[connect] websocket logging masks include=0x%x exclude=0x%x",
static_cast<unsigned>(include), static_cast<unsigned>(exclude));

FIREBOLT_LOG_NOTICE("Transport", "[connect] Full WebSocket URL: %s", url.c_str());
Comment thread test/unit/gatewayTest.cpp
Comment on lines +392 to +409
std::thread(
[this, hdl, msg]()
{
std::this_thread::sleep_for(std::chrono::milliseconds(1600)); // arrives AFTER 1500ms timeout
try
{
auto req = nlohmann::json::parse(msg->get_payload());
nlohmann::json resp;
resp["jsonrpc"] = "2.0";
resp["id"] = req["id"];
resp["result"] = true; // server would have returned success
m_server.send(hdl, resp.dump(), msg->get_opcode());
}
catch (...)
{
}
})
.detach();
Comment thread test/unit/gatewayTest.cpp
Comment on lines +1698 to +1702
::setenv("FIREBOLT_WAIT_TIME_MS", "not_a_number", 1);
struct Cleanup
{
~Cleanup() { ::unsetenv("FIREBOLT_WAIT_TIME_MS"); }
} cleanup;
Comment thread test/unit/gatewayTest.cpp
Comment on lines +1725 to +1729
::setenv("FIREBOLT_WAIT_TIME_MS", "0", 1);
struct Cleanup
{
~Cleanup() { ::unsetenv("FIREBOLT_WAIT_TIME_MS"); }
} cleanup;
Comment thread test/unit/gatewayTest.cpp
Comment on lines +1749 to +1753
::setenv("FIREBOLT_WAIT_TIME_MS", "200000", 1);
struct Cleanup
{
~Cleanup() { ::unsetenv("FIREBOLT_WAIT_TIME_MS"); }
} cleanup;
Comment thread test/unit/gatewayTest.cpp
// ---------------------------------------------------------------------------
TEST_F(GatewayUTest, NoEnvWaitTimeMsUsesConfigValue)
{
::unsetenv("FIREBOLT_WAIT_TIME_MS"); // ensure absent
Comment thread src/gateway.cpp
Comment on lines +553 to +556
if (const char* env = std::getenv("FIREBOLT_WAIT_TIME_MS"))
{
char* end = nullptr;
unsigned long override_val = std::strtoul(env, &end, 10);
Comment thread test/unit/gatewayTest.cpp
m_messageHandler = [this](connection_hdl hdl, server::message_ptr msg)
{
std::thread(
[this, hdl, msg]()
Comment thread test/unit/gatewayTest.cpp
m_messageHandler = [this](connection_hdl hdl, server::message_ptr msg)
{
std::thread(
[this, hdl, msg]()
Comment thread test/unit/gatewayTest.cpp
m_messageHandler = [this](connection_hdl hdl, server::message_ptr msg)
{
std::thread(
[this, hdl, msg]()
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.

3 participants