Skip to content

Commit ebb2d2f

Browse files
authored
fix(auth): fix emulator log spam and add exponential backoff on retry (fixes #1629) (#1894)
1 parent 8715933 commit ebb2d2f

5 files changed

Lines changed: 77 additions & 37 deletions

File tree

auth/src/desktop/auth_desktop.cc

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
#include "auth/src/desktop/auth_desktop.h"
1616

17+
#include <algorithm>
1718
#include <cassert>
1819
#include <cstdint>
1920
#include <cstring>
@@ -92,6 +93,19 @@ void* CreatePlatformAuth(App* const app) {
9293
AuthImpl* const auth = new AuthImpl();
9394
auth->api_key = app->options().api_key();
9495
auth->app_name = app->name();
96+
97+
// Check environment variables for emulator if set.
98+
if (std::getenv("USE_AUTH_EMULATOR") != nullptr) {
99+
auth->assigned_emulator_url.append(kEmulatorLocalHost);
100+
auth->assigned_emulator_url.append(":");
101+
if (std::getenv("AUTH_EMULATOR_PORT") == nullptr) {
102+
auth->assigned_emulator_url.append(kEmulatorPort);
103+
} else {
104+
auth->assigned_emulator_url.append(std::getenv("AUTH_EMULATOR_PORT"));
105+
}
106+
LogInfo("Using Auth Emulator: %s", auth->assigned_emulator_url.c_str());
107+
}
108+
95109
return auth;
96110
}
97111

@@ -576,6 +590,7 @@ void Auth::UseEmulator(std::string host, uint32_t port) {
576590
auth_impl->assigned_emulator_url.append(host);
577591
auth_impl->assigned_emulator_url.append(":");
578592
auth_impl->assigned_emulator_url.append(std::to_string(port));
593+
LogInfo("Using Auth Emulator: %s", auth_impl->assigned_emulator_url.c_str());
579594
}
580595

581596
void InitializeTokenRefresher(AuthData* auth_data) {
@@ -667,6 +682,8 @@ void IdTokenRefreshThread::Initialize(AuthData* auth_data) {
667682
thread_ = firebase::Thread(
668683
[](IdTokenRefreshThread* refresh_thread) {
669684
Auth* auth = refresh_thread->auth;
685+
ExponentialBackoff backoff;
686+
670687
while (!refresh_thread->is_shutting_down()) {
671688
// Note: Make sure to always make future_impl.mutex the innermost
672689
// lock, to prevent deadlocks!
@@ -700,8 +717,19 @@ void IdTokenRefreshThread::Initialize(AuthData* auth_data) {
700717
// is completed.
701718
future_sem.Wait();
702719

703-
// (We don't actually care about the results of the token request.
704-
// The token listener will handle that.)
720+
if (future.error() != 0) {
721+
// Token refresh failed (e.g. network outage). Wait with
722+
// exponential backoff to prevent tight retry loops and avoid
723+
// overloading backend servers upon reconnect. Matches Android
724+
// DefaultTokenRefresher (30s initial, doubling up to 16m max).
725+
if (!refresh_thread->is_shutting_down()) {
726+
refresh_thread->wakeup_sem_.TimedWait(backoff.NextDelayMs());
727+
}
728+
continue;
729+
} else {
730+
// Refresh succeeded! Reset backoff to minimum interval.
731+
backoff.Reset();
732+
}
705733
} else {
706734
auth->auth_data_->future_impl.mutex().Release();
707735
refresh_thread->ref_count_mutex_.Release();

auth/src/desktop/auth_desktop.h

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#ifndef FIREBASE_AUTH_SRC_DESKTOP_AUTH_DESKTOP_H_
1616
#define FIREBASE_AUTH_SRC_DESKTOP_AUTH_DESKTOP_H_
1717

18+
#include <algorithm>
1819
#include <cstdint>
1920
#include <memory>
2021
#include <string>
@@ -179,6 +180,49 @@ struct AuthImpl {
179180
const int kMinutesPerTokenRefresh = 58;
180181
const int kMsPerTokenRefresh =
181182
kMinutesPerTokenRefresh * internal::kMillisecondsPerMinute;
183+
// Exponential backoff parameters when token refresh fails (e.g. network
184+
// outage), matching Android DefaultTokenRefresher.
185+
const int kMinRetryBackoffMs = 30 * 1000; // 30 seconds base delay
186+
const int kMaxRetryBackoffMs = 16 * 60 * 1000; // 16 minutes max delay
187+
188+
// Encapsulates deterministic exponential backoff parameters and calculation
189+
// when token refresh fails (e.g. network outage), matching Android
190+
// DefaultTokenRefresher (30s initial, doubling up to 16m max).
191+
class ExponentialBackoff {
192+
public:
193+
explicit ExponentialBackoff(int min_delay_ms = kMinRetryBackoffMs,
194+
int max_delay_ms = kMaxRetryBackoffMs,
195+
double multiplier = 2.0)
196+
: min_delay_ms_(std::max(0, min_delay_ms)),
197+
max_delay_ms_(std::max(min_delay_ms_, max_delay_ms)),
198+
multiplier_(std::max(1.0, multiplier)),
199+
current_delay_ms_(min_delay_ms_) {}
200+
201+
// Resets the backoff delay to the initial minimum delay.
202+
void Reset() { current_delay_ms_ = min_delay_ms_; }
203+
204+
// Returns the delay to wait for the current retry attempt, and advances
205+
// the internal delay for subsequent attempts up to max_delay_ms.
206+
int NextDelayMs() {
207+
int delay = current_delay_ms_;
208+
if (static_cast<double>(current_delay_ms_) * multiplier_ >=
209+
static_cast<double>(max_delay_ms_)) {
210+
current_delay_ms_ = max_delay_ms_;
211+
} else {
212+
current_delay_ms_ = static_cast<int>(current_delay_ms_ * multiplier_);
213+
}
214+
return delay;
215+
}
216+
217+
// Returns the current delay without advancing it.
218+
int current_delay_ms() const { return current_delay_ms_; }
219+
220+
private:
221+
int min_delay_ms_;
222+
int max_delay_ms_;
223+
double multiplier_;
224+
int current_delay_ms_;
225+
};
182226

183227
void InitializeUserDataPersist(AuthData* auth_data);
184228
void DestroyUserDataPersist(AuthData* auth_data);

auth/src/desktop/rpcs/auth_request.cc

Lines changed: 2 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,6 @@ const char* kHeaderFirebaseLocale = "X-Firebase-Locale";
4242
AuthRequest::AuthRequest(::firebase::App& app, const char* schema,
4343
bool deliver_heartbeat)
4444
: RequestJson(schema), app(app) {
45-
CheckEnvEmulator();
46-
4745
if (deliver_heartbeat) {
4846
std::shared_ptr<heartbeat::HeartbeatController> heartbeat_controller =
4947
app.GetHeartbeatController();
@@ -76,52 +74,23 @@ AuthRequest::AuthRequest(::firebase::App& app, const char* schema,
7674
}
7775

7876
std::string AuthRequest::GetUrl() {
79-
std::string emulator_url;
8077
Auth* auth_ptr = Auth::GetAuth(&app);
8178
std::string assigned_emulator_url =
8279
static_cast<AuthImpl*>(auth_ptr->auth_data_->auth_impl)
8380
->assigned_emulator_url;
84-
if (assigned_emulator_url.empty()) {
85-
emulator_url = env_emulator_url;
86-
} else {
87-
emulator_url = assigned_emulator_url;
88-
}
8981

90-
if (emulator_url.empty()) {
82+
if (assigned_emulator_url.empty()) {
9183
std::string url(kHttps);
9284
url += kServerURL;
9385
return url;
9486
} else {
9587
std::string url(kHttp);
96-
url += emulator_url;
88+
url += assigned_emulator_url;
9789
url += "/";
9890
url += kServerURL;
9991
return url;
10092
}
10193
}
10294

103-
void AuthRequest::CheckEnvEmulator() {
104-
if (!env_emulator_url.empty()) {
105-
LogInfo("Environment Emulator Url already set: %s",
106-
env_emulator_url.c_str());
107-
return;
108-
}
109-
110-
// Use emulator as long as this env variable is set, regardless its value.
111-
if (std::getenv("USE_AUTH_EMULATOR") == nullptr) {
112-
LogInfo("USE_AUTH_EMULATOR not set.");
113-
return;
114-
}
115-
env_emulator_url.append(kEmulatorLocalHost);
116-
env_emulator_url.append(":");
117-
// Use AUTH_EMULATOR_PORT if it is set to non empty string,
118-
// otherwise use the default port.
119-
if (std::getenv("AUTH_EMULATOR_PORT") == nullptr) {
120-
env_emulator_url.append(kEmulatorPort);
121-
} else {
122-
env_emulator_url.append(std::getenv("AUTH_EMULATOR_PORT"));
123-
}
124-
}
125-
12695
} // namespace auth
12796
} // namespace firebase

auth/src/desktop/rpcs/auth_request.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,6 @@ class AuthRequest
5555
std::string GetUrl();
5656

5757
private:
58-
void CheckEnvEmulator();
59-
std::string env_emulator_url;
6058
::firebase::App& app;
6159
};
6260

release_build_files/readme.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -615,6 +615,7 @@ code.
615615
## Release Notes
616616
### Upcoming
617617
- Changes
618+
- Auth (Desktop): Fixed log spam and high CPU utilization when offline by moving `USE_AUTH_EMULATOR` environment variable check to initialization and eliminating per-request logging (#1629).
618619
- Messaging: Added new Registration methods using Installation Ids.
619620
Deprecated old Token based methods.
620621

0 commit comments

Comments
 (0)