[Bug] Unable to Fetch Updated Data from Firebase Remote Config without Restart #1630
Description
[REQUIRED] Please fill in the following fields:
- Pre-built SDK from the open-source from this repo: https://github.com/firebase/firebase-cpp-sdk/releases/tag/v12.0.0
- Firebase C++ SDK version: 12.0.0
- Problematic Firebase Component: Remote Config (Auth, Database, etc.)
- Other Firebase Components in use: No (Auth, Database, etc.)
- Platform you are using the C++ SDK on: Mac (Mac, Windows, or Linux)
- Platform you are targeting: desktop (iOS, Android, and/or desktop)
[REQUIRED] Please describe the issue here:
We are experiencing an issue where our application is unable to fetch updated data from Firebase Remote Config. Despite setting the .minimum_fetch_interval_in_milliseconds to 0 and calling Fetch(0), the application continues to retrieve outdated data until it is restarted.
According to the source code, these steps should be sufficient to fetch the new data immediately. However, changes made to the Firebase Remote Config are not reflected in the app until a restart is performed.
In the attached example (a modified example from https://github.com/firebase/quickstart-cpp/tree/main/remote_config/testapp), I have added 5 attempts to fetch new data with a delay. During the timeout, I change values in the remote config. Below is the output log from the example
Steps to reproduce:
- Set up Firebase Remote Config from https://github.com/firebase/quickstart-cpp/tree/main/remote_config/testapp.
- Change common_main.cc to provided below or from attached archive.
- Set .minimum_fetch_interval_in_milliseconds = 0.
- Call Fetch(0) to fetch new data.
- Change values in the Firebase Remote Config console.
- Attempt to fetch the updated data multiple times with a delay between each attempt.
- Observe that the fetched data remains the same (old data) until the application is restarted.
Have you been able to reproduce this issue with just the Firebase C++ quickstarts ?
Yes
What's the issue repro rate? (e g 100%, 1/5 etc)
100%
What happened? How can we make the problem occur?
Initialize the Firebase Remote Config library
Created the Firebase app f3405e80
Try to initialize Firebase RemoteConfig
Initialized the Firebase Remote Config API
Changed ConfigSettings minimum_fetch_interval_in_milliseconds to 0
Fetch...
Fetch Complete
Activate succeeded
Info last_fetch_time_ms=-468513974 (year=2024.60) fetch_status=0 failure_reason=0 throttled_end_time=0
Get test_key "test changed" Remote
GetKeys:
test_key
Fetch...
Fetch Complete
Activate succeeded
Info last_fetch_time_ms=-468493069 (year=2024.60) fetch_status=0 failure_reason=0 throttled_end_time=0
Get test_key "test changed" Remote
GetKeys:
test_key
Fetch...
Fetch Complete
Activate succeeded
Info last_fetch_time_ms=-468472180 (year=2024.60) fetch_status=0 failure_reason=0 throttled_end_time=0
Get test_key "test changed" Remote
GetKeys:
test_key
Relevant Code:
// Copyright 2016 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <assert.h>
#include "firebase/app.h"
#include "firebase/remote_config.h"
#include "firebase/util.h"
// Thin OS abstraction layer.
#include "main.h" // NOLINT
using firebase::remote_config::RemoteConfig;
// Convert remote_config::ValueSource to a string.
const char *ValueSourceToString(firebase::remote_config::ValueSource source)
{
static const char *kSourceToString[] = {
"Static", // kValueSourceStaticValue
"Remote", // kValueSourceRemoteValue
"Default", // kValueSourceDefaultValue
};
return kSourceToString[source];
}
RemoteConfig *rc_ = nullptr;
// Execute all methods of the C++ Remote Config API.
extern "C" int common_main(int argc, const char *argv[])
{
namespace remote_config = ::firebase::remote_config;
::firebase::App *app;
// Initialization
firebase::AppOptions options;
// Fill options
// options.set_api_key("");
// options.set_app_id("");
// options.set_project_id("");
// options.set_storage_bucket("");
// options.set_messaging_sender_id("");
LogMessage("Initialize the Firebase Remote Config library");
#if defined(__ANDROID__)
app = ::firebase::App::Create(GetJniEnv(), GetActivity());
#else
app = ::firebase::App::Create(options);
#endif // defined(__ANDROID__)
LogMessage("Created the Firebase app %x",
static_cast<int>(reinterpret_cast<intptr_t>(app)));
::firebase::ModuleInitializer initializer;
void *ptr = nullptr;
ptr = &rc_;
initializer.Initialize(app, ptr, [](::firebase::App *app, void *target)
{
LogMessage("Try to initialize Firebase RemoteConfig");
RemoteConfig **rc_ptr = reinterpret_cast<RemoteConfig **>(target);
*rc_ptr = RemoteConfig::GetInstance(app);
return firebase::kInitResultSuccess; });
while (initializer.InitializeLastResult().status() !=
firebase::kFutureStatusComplete)
{
if (ProcessEvents(100))
return 1; // exit if requested
}
if (initializer.InitializeLastResult().error() != 0)
{
LogMessage("Failed to initialize Firebase Remote Config: %s",
initializer.InitializeLastResult().error_message());
ProcessEvents(2000);
return 1;
}
LogMessage("Initialized the Firebase Remote Config API");
auto config_settings_result = rc_->SetConfigSettings(
{.fetch_timeout_in_milliseconds =
firebase::remote_config::kDefaultTimeoutInMilliseconds,
.minimum_fetch_interval_in_milliseconds = 0});
while (config_settings_result.status() == firebase::kFutureStatusPending)
{
if (ProcessEvents(1000))
{
break;
}
}
LogMessage("Changed ConfigSettings minimum_fetch_interval_in_milliseconds to 0");
auto tryWithDelay = [&]()
{
// Test Fetch...
LogMessage("Fetch...");
auto future_result = rc_->Fetch(0);
while (future_result.status() == firebase::kFutureStatusPending)
{
if (ProcessEvents(1000))
{
break;
}
}
if (future_result.status() == firebase::kFutureStatusComplete)
{
LogMessage("Fetch Complete");
auto activate_future_result = rc_->Activate();
while (future_result.status() == firebase::kFutureStatusPending)
{
if (ProcessEvents(1000))
{
break;
}
}
bool activate_result = activate_future_result.result();
LogMessage("Activate %s", activate_result ? "succeeded" : "failed");
const remote_config::ConfigInfo &info = rc_->GetInfo();
LogMessage("Info last_fetch_time_ms=%d (year=%.2f) fetch_status=%d "
"failure_reason=%d throttled_end_time=%d",
static_cast<int>(info.fetch_time),
1970.0f + static_cast<float>(info.fetch_time) /
(1000.0f * 60.0f * 60.0f * 24.0f * 365.0f),
info.last_fetch_status, info.last_fetch_failure_reason,
info.throttled_end_time);
remote_config::ValueInfo value_info;
std::string result = rc_->GetString("test_key", &value_info);
LogMessage("Get test_key \"%s\" %s", result.c_str(),
ValueSourceToString(value_info.source));
{
// Print out the keys that are now tied to data
std::vector<std::string> keys = rc_->GetKeys();
LogMessage("GetKeys:");
for (auto s = keys.begin(); s != keys.end(); ++s)
{
LogMessage(" %s", s->c_str());
}
}
}
else
{
LogMessage("Fetch Incomplete");
}
// Release a handle to the future so we can shutdown the Remote Config API
// when exiting the app. Alternatively we could have placed future_result
// in a scope different to our shutdown code below.
future_result.Release();
};
for (int i = 5; i > 0; --i)
{
tryWithDelay();
ProcessEvents(20000);
}
// Wait until the user wants to quit the app.
while (!ProcessEvents(1000))
{
}
delete rc_;
rc_ = nullptr;
delete app;
return 0;
}
Activity