Dynamically Adjusting Server Configuration

iFun Engine provides RESTful API to dynamically change some of the server configuration. Mutable configuration includes iFun Engine flags as shown below and your own flags. (Since iFun Engine relies on Google Gflags for its command-line flag processing, you can easily add your own flags. Please refer to the Google Gflags documents for details.)

  • minloglevel: This changes the minimum log level.
  • v: This changes the verbosity.
  • alsologtostderr: This turns on/off log messages to the standard error in addition to the log file.
  • session_message_logging_level: This changes log levels for session messages between the client and the server. 0 for no session message logging, 1 for message type and length only, and 2 for entire message dump. Please refer to Networking parameters for details.
  • rpc_message_logging_level: This changes log levels for RPC messages between servers. 0 for no message logging, 1 for message type and length only, and 2 for entire message dump. Please see Distribution parameters for details.
  • enable_event_profiler: This turns on/off the iFun Engine’s event profiling feature. Please refer to Event profiling: details for details.
  • event_threads_size: This changes the number of main threads. Please refer to Event parameters.
  • client_current_version: This changes the required client version to connect the server. This can prevent clients with invalid versions from connecting to the server. Please refer to Client version control.

Important

Please be aware that the change is tentative and does not mean overwriting the MANIFEST.json file.

Configuring MANIFEST.json

To enable dynamic configuration changes, a component named RuntimeConfiguration should be listed in the MANIFEST.json file.

Note

If you have created a project with iFun Engine version 1.0.0-1401 or later, the component is already in the generated MANIFEST.json and you do not have to include it manually.

Important

RuntimeConfiguration relies on the ApiService component. So, please make sure you enabled the ApiService component referring to ApiService parameters.

"RuntimeConfiguration": {
  "enable_runtime_configuration": true,
  "additional_configurations": []
}
  • enable_runtime_configuration: This should be set to true to enable the feature.

  • additional_configurations: You can specify a list of custom gflags to allow dynamic re-configuration. (E.g., [“myflag1”, “myflag2”])

    Tip

    You cannot add iFun Engine’s flags in the list. But it is allowed to include flags related to Glog or Gflag.

RESTful APIs

To retrieve all the current flag values

GET /v1/configurations/

Request example:

GET /v1/configurations/ HTTP/1.1

Response example:

HTTP/1.1 200 OK
Content-Type: application/json

{
  "minloglevel": {
    "type": "int32",
    "desc": "Messages logged at a lower level than this don't actually get logged anywhere",
    "value": "0"
  },
  ...
}

To read a specific flag

GET /v1/configurations/(flag_name)
Parameters:
  • flag_name – the name of a flag in question.

Example - in case that the flag name indeed exists:

Request

GET /v1/configurations/minloglevel HTTP/1.1

Response

HTTP/1.1 200 OK
Content-Type: application/json

{
  "minloglevel": {
    "type": "int32",
    "desc": "Messages logged at a lower level than this don't actually get logged anywhere",
    "value": "0"
  }
}

Example - in case that the flag name does not exist.:

Request

GET /v1/configurations/no_flag HTTP/1.1

Response

HTTP/1.1 400 Bad Request
Content-Type: application/json

{
    "no_flag": {
        "message": "This is not available",
        "available_configurations": {
            "minloglevel": {
                "type": "int32",
                "desc": "Messages logged at a lower level than this don't actually get logged anywhere"
            },
            ...
        }
    }
}

To change a specific flag

PUT /v1/configurations/(flag_name)/(value)
Parameters:
  • flag_name – the name of a flag to update
  • value – new value

Example - in case the the value is legitimate:

Request

PUT /v1/configurations/minloglevel/1 HTTP/1.1

Reply

HTTP/1.1 200 OK
Content-Type: application/json

{
  "minloglevel": {
    "result": "minloglevel set to 1\n"
  }
}

Example - in case the value is not legitimate:

Request

PUT /v1/configurations/minloglevel/one HTTP/1.1

Reply

HTTP/1.1 400 Bad Request
Content-Type: application/json

{
  "minloglevel": {
    "result": "The 'one' is not a valid value"
  }
}

To read and update the information on compatible client versions

You can retrieve the compatible client version information specified by the client_compatible_versions of the AppInfo component in MANIFEST.json in this way:

GET /v1/configurations/compatible_client_versions

Example:

Request

GET /v1/configurations/compatible_client_versions HTTP/1.1

Reply:

HTTP/1.1 200 OK
Content-Type: application/json

{
  "current_compatible_versions": ["0.0.1","0.0.2"]
}

Also, you can override the compatible client version information like this:

POST /v1/configurations/compatible_client_versions

Example

Request:

POST /v1/configurations/compatible_client_versions HTTP/1.1

["0.0.3", "0.0.4"]

Reply:

HTTP/1.1 200 OK
Content-Type: application/json

{
  "current_compatible_versions": ["0.0.3", "0.0.4"]
}

Important

Again, the change is tentative and will be reset once the server reboots. This means the update query does not overwrite the MANIFEST.json file.

Make custom flags dynamically configurable

Say, we defined a command-line flag using the Google Gflags.

DEFINE_bool(enable_gold_event,   // names the flag.
            false,               // gives the default value for the flag.
            "gold event flag");  // gives a long description about the flag.

If we want to make the enable_gold_event flag dynamically mutable, we need to add it to the additional_configuration field of the RuntimeConfiguration component.

"RuntimeConfiguration": {
  "enable_runtime_configuration": true,
  "additional_configurations": ["enable_gold_event"]
}

OK. The flag is ready to be updated now. We then need to figure out how to update.

Pitfall: reflecting flag value changes to real changes

Say, we want to update the enable_gold_event flag explained in Make custom flags dynamically configurable.

Let’s make the scenario more realistic. Say, the flag defaults to false and is used in a code like this way:

// user.cc

DEFINE_bool(enable_gold_event, false, "gold event flag");

void OnLogin() {
  Ptr<User> user = User::Fetch(...);

  // If enable_gold_event flag is turned on, the player gets extra 100 gold on log in.
  if (FLAGS_enable_gold_event) {
    user->SetGold(user->GetGold() + 100);
  }
}

We may want to turn on the flag for a while to attract more players.

Request

PUT /v1/configurations/enable_gold_event/true

Tip

Any non-zero values are treated as true.

Reply

HTTP/1.1 200 OK
Content-Type: application/json

{
  "enable_gold_event": {
    "result": "enable_gold_event set to true\n"
  }
}

You might write a code snippet that copies the Google flag value to a variable, rather than directly reading the flag. But, please keep it mind that this approach will not work, since changes to the flag do not imply changes to the variable. For example, say we have a class named EventManager which keeps track of in-game campaigns and the class has a method named EventManager::IsGoldEventEnabled that returns a internal copy of the Google flag. Code would look like this:

// server.cc

DEFINE_bool(enable_gold_event, false, "gold event flag");

// Assume below method is invoked when the server starts.
void Start() {
  // the enable_gold_event flag is set to false, by default.
  // We are passing the flag value when initialization our EventManager class.
  EventManager::Initialize(FLAGS_enable_gold_event);
}


// event_manager.cc

bool the_is_gold_event_enabled = false;

void EventManager::Initialize(bool is_gold_event_enabled) {
  // The initialization method makes a local copy of the flag.
  the_is_gold_event_enabled = is_gold_event_enabled;
}

bool EventManager::IsGoldEventEnabled() {
  // This getter method simply returns the local copy.
  return the_is_gold_event_enabled;
}


// user.cc

void OnLogin() {
  Ptr<User> user = User::Fetch(...);

  // We will reward the user some gold if the gold campaign is in progress.
  // But this code has a flaw, since EventManager::IsGoldEventEnabled() cached the flag value.
  if (EventManager::IsGoldEventEnabled()) {
    user->SetGold(user->GetGold() + 100);
  }
}

As we outlined above, EventManager::Initialize() returns a local copy of the enable_gold_event Google flag. So, changes to the flag are not relevant to the local variable. And this is obviously not what we want. So, please do not forget to directly read the flag if the flag is set to mutable via the REST API. Correct code should look like this:

// server.cc

DEFINE_bool(enable_gold_event, false, "gold event flag");

// Assume below method is invoked when the server starts.
void Start() {
  // Please note that we are not passing the flag value anymore.
  EventManager::Initialize();
}


// event_manager.cc

// To read the flag, we need to use Google flags DECLARE_*(...) macros.
// In this example, we will use DECLARE_bool(...) as the flag is boolean.
DECLARE_bool(enable_gold_event);

void EventManager::Initialize() {
  ...
}

bool EventManager::IsGoldEventEnabled() {
  // This function now reads the flag directly, which is correct.
  return FLAGS_enable_gold_event;
}


// user.cc

void OnLogin() {
  Ptr<User> user = User::Fetch(...);

  // Now, we can turn on/off the event via the REST API.
  if (EventManager::IsGoldEventEnabled()) {
    user->SetGold(user->GetGold() + 100);
  }
}