vesoft-inc / nebula

A distributed, fast open-source graph database featuring horizontal scalability and high availability
https://nebula-graph.io
Apache License 2.0
10.89k stars 1.21k forks source link

the `time::WallClock::fastNowInSec()` is getting more and more ahead #5833

Closed UndefinedSy closed 8 months ago

UndefinedSy commented 8 months ago

Describe the bug (required) the deviation of the time::WallClock::fastNowInSec() is unpredictable.

I've noticed that on my different machines, the timepoint generated by fastNowInSec is unpredictable. some of the results show that the fastNowInSec is always consistent with the slowNowInSec, the others show that the differences between the two continue to widen. I'm not sure if this will lead to a buggy behavior on the scenario that requires two timepoints to cal the duration. (like TTL or something)

Your Environments (required)

How To Reproduce(required)

Steps to reproduce the behavior:

#include <chrono>
#include <ctime>
#include <iostream>
#include <sstream>

#include "common/time/WallClock.h"

int main(int argc, char *argv[]) {
  // test std::chrono::system_clock
  auto std_system_now1 = std::chrono::system_clock::now();
  // test std::chrono::steady_clock
  auto std_steady_now1 = std::chrono::steady_clock::now();
  // test nebula::time::WallClock::slowNowInSec
  auto nebula_slow_now1 = nebula::time::WallClock::slowNowInSec();
  // test nebula::time::WallClock::fastNowInSec
  auto nebula_fast_now1 = nebula::time::WallClock::fastNowInSec();
  // output current date with format "YYYY-MM-DD HH:MM:SS"
  auto system_wallclock_datetime = std::chrono::system_clock::to_time_t(std_system_now1);
  std::stringstream ss;
  ss << std::put_time(std::localtime(&system_wallclock_datetime), "%Y-%m-%d %X");
  std::cout << "Current DataTime(system wallclock): "
            << ss.str() << std::endl;
  std::cout << "\tstd::chrono::system_clock::now(): "
            << std::chrono::duration_cast<std::chrono::seconds>(std_system_now1.time_since_epoch()).count() << std::endl;
  std::cout << "\tstd::chrono::steady_clock::now(): "
            << std::chrono::duration_cast<std::chrono::seconds>(std_steady_now1.time_since_epoch()).count() << std::endl;
  std::cout << "\tnebula::time::WallClock::slowNowInSec(): "
            << nebula_slow_now1 << std::endl;
  std::cout << "\tnebula::time::WallClock::fastNowInSec(): "
            << nebula_fast_now1 << std::endl;

  while (1) {
    // wait for 10s and re-genereate the time
    std::cout << "Wait for 10s..." << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(10));

    // test std::chrono::system_clock
    auto std_system_now2 = std::chrono::system_clock::now();
    // test std::chrono::steady_clock
    auto std_steady_now2 = std::chrono::steady_clock::now();
    // test nebula::time::WallClock::slowNowInSec
    auto nebula_slow_now2 = nebula::time::WallClock::slowNowInSec();
    // test nebula::time::WallClock::fastNowInSec
    auto nebula_fast_now2 = nebula::time::WallClock::fastNowInSec();
    // output current date with format "YYYY-MM-DD HH:MM:SS"
    system_wallclock_datetime = std::chrono::system_clock::to_time_t(std_system_now2);
    ss.str("");
    ss << std::put_time(std::localtime(&system_wallclock_datetime), "%Y-%m-%d %X");
    std::cout << "Current DataTime(system wallclock): "
              << ss.str() << std::endl;
    std::cout << "\tstd::chrono::system_clock::now(): "
              << std::chrono::duration_cast<std::chrono::seconds>(std_system_now2.time_since_epoch()).count() << std::endl;
    std::cout << "\tstd::chrono::steady_clock::now(): "
              << std::chrono::duration_cast<std::chrono::seconds>(std_steady_now2.time_since_epoch()).count() << std::endl;
    std::cout << "\tnebula::time::WallClock::slowNowInSec(): "
              << nebula_slow_now2 << std::endl;
    std::cout << "\tnebula::time::WallClock::fastNowInSec(): "
              << nebula_fast_now2 << std::endl;
    std::cout << "--------------------------------------------------------------\n";
  }
}

running the above code for a period of time, got the following result.

Current DataTime(system wallclock): 2024-03-11 11:35:12
        std::chrono::system_clock::now(): 1710128112
        std::chrono::steady_clock::now(): 4478530
        nebula::time::WallClock::slowNowInSec(): 1710128112
        nebula::time::WallClock::fastNowInSec(): 1710128216

Expected behavior the fast one can be not so precise but the differences should be limited to a predictable range.

UndefinedSy commented 8 months ago

not sure if this is by-designed

critical27 commented 8 months ago

fast won't be accurate. IIRC, its difference is unpredictable. And yes, this may lead a problem when consider TTL if it use the fast function.

dutor commented 8 months ago

Years ago, I had noticed this buggy problem but didn't dive into it, and never have reproduced it ever since.

Yeah you are right. The so called fast version of WallClock is kind of not predictable because of how it works when converting the CPU ticks to realtime duration. It's subject to many aspects including the rdtsc instruction itself, accuracy of the system timer and even system load during caliberation.

So we discourage to use it in some critical situations.

If you are interesting in this issue, you could try out to utilize this kernel module to make the conversion mentioned above more accurate and eliminate the caliberation process.

UndefinedSy commented 8 months ago

thx for the prompt reply.