kudu-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From a...@apache.org
Subject [1/2] kudu git commit: clock: refactor interaction with NTP into a new interface
Date Thu, 03 Aug 2017 21:09:53 GMT
Repository: kudu
Updated Branches:
  refs/heads/master 7ff27dcf1 -> e5202d30a


clock: refactor interaction with NTP into a new interface

This refactors HybridClock to interact with the system NTP service (or
its mock) via a new TimeService interface. This encapsulates the
NTP-specific code in such a way that we can easily swap it out with a
built-in NTP client implementation in a future patch.

This also cleans up the 'mock' and OSX (unsynchronized) clock mode to
simply be new implementations of the above interface.

Change-Id: If0653ec32016c588fb63c74f7bc962593aa6c4a0
Reviewed-on: http://gerrit.cloudera.org:8080/7520
Tested-by: Kudu Jenkins
Reviewed-by: Adar Dembo <adar@cloudera.com>


Project: http://git-wip-us.apache.org/repos/asf/kudu/repo
Commit: http://git-wip-us.apache.org/repos/asf/kudu/commit/de8b92eb
Tree: http://git-wip-us.apache.org/repos/asf/kudu/tree/de8b92eb
Diff: http://git-wip-us.apache.org/repos/asf/kudu/diff/de8b92eb

Branch: refs/heads/master
Commit: de8b92eb6469f843d43c432678e52732a861014b
Parents: 7ff27dc
Author: Todd Lipcon <todd@apache.org>
Authored: Thu Jul 20 17:53:20 2017 -0700
Committer: Adar Dembo <adar@cloudera.com>
Committed: Thu Aug 3 21:09:05 2017 +0000

----------------------------------------------------------------------
 src/kudu/clock/CMakeLists.txt                   |  13 +-
 src/kudu/clock/hybrid_clock-test.cc             |  19 ++-
 src/kudu/clock/hybrid_clock.cc                  | 149 ++++---------------
 src/kudu/clock/hybrid_clock.h                   |  40 +----
 src/kudu/clock/mock_ntp.cc                      |  48 ++++++
 src/kudu/clock/mock_ntp.h                       |  70 +++++++++
 src/kudu/clock/system_ntp.cc                    | 115 ++++++++++++++
 src/kudu/clock/system_ntp.h                     |  65 ++++++++
 src/kudu/clock/system_unsync_time.cc            |  39 +++++
 src/kudu/clock/system_unsync_time.h             |  48 ++++++
 src/kudu/clock/time_service.h                   |  55 +++++++
 src/kudu/integration-tests/consistency-itest.cc |   8 +-
 .../tablet_history_gc-itest.cc                  |  13 +-
 src/kudu/server/generic_service.cc              |  14 +-
 src/kudu/server/server_base.proto               |   2 +-
 src/kudu/tablet/tablet_history_gc-test.cc       |  15 +-
 16 files changed, 534 insertions(+), 179 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/kudu/blob/de8b92eb/src/kudu/clock/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/src/kudu/clock/CMakeLists.txt b/src/kudu/clock/CMakeLists.txt
index 786d3ef..0e94f4a 100644
--- a/src/kudu/clock/CMakeLists.txt
+++ b/src/kudu/clock/CMakeLists.txt
@@ -15,9 +15,18 @@
 # specific language governing permissions and limitations
 # under the License.
 
-add_library(clock
+set(CLOCK_SRCS
   hybrid_clock.cc
-  logical_clock.cc)
+  logical_clock.cc
+  mock_ntp.cc
+  system_unsync_time.cc)
+
+if (NOT APPLE)
+  set(CLOCK_SRCS ${CLOCK_SRCS} system_ntp.cc)
+endif()
+
+add_library(clock ${CLOCK_SRCS})
+
 target_link_libraries(clock
   kudu_util)
 

http://git-wip-us.apache.org/repos/asf/kudu/blob/de8b92eb/src/kudu/clock/hybrid_clock-test.cc
----------------------------------------------------------------------
diff --git a/src/kudu/clock/hybrid_clock-test.cc b/src/kudu/clock/hybrid_clock-test.cc
index 4b73e57..caea024 100644
--- a/src/kudu/clock/hybrid_clock-test.cc
+++ b/src/kudu/clock/hybrid_clock-test.cc
@@ -20,12 +20,13 @@
 #include <gtest/gtest.h>
 
 #include "kudu/clock/hybrid_clock.h"
+#include "kudu/clock/mock_ntp.h"
 #include "kudu/util/monotime.h"
 #include "kudu/util/random.h"
 #include "kudu/util/random_util.h"
 #include "kudu/util/test_util.h"
 
-DECLARE_bool(use_mock_wall_clock);
+DECLARE_string(time_source);
 
 namespace kudu {
 namespace clock {
@@ -45,9 +46,13 @@ class HybridClockTest : public KuduTest {
   scoped_refptr<HybridClock> clock_;
 };
 
+clock::MockNtp* mock_ntp(const scoped_refptr<HybridClock>& clock) {
+  return down_cast<clock::MockNtp*>(clock->time_service());
+}
+
 TEST(MockHybridClockTest, TestMockedSystemClock) {
   google::FlagSaver saver;
-  FLAGS_use_mock_wall_clock = true;
+  FLAGS_time_source = "mock";
   scoped_refptr<HybridClock> clock(new HybridClock());
   clock->Init();
   Timestamp timestamp;
@@ -61,8 +66,8 @@ TEST(MockHybridClockTest, TestMockedSystemClock) {
   // Now set an arbitrary time and check that is the time returned by the clock.
   uint64_t time = 1234;
   uint64_t error = 100 * 1000;
-  clock->SetMockClockWallTimeForTests(time);
-  clock->SetMockMaxClockErrorForTests(error);
+  mock_ntp(clock)->SetMockClockWallTimeForTests(time);
+  mock_ntp(clock)->SetMockMaxClockErrorForTests(error);
   clock->NowWithError(&timestamp, &max_error_usec);
   ASSERT_EQ(timestamp.ToUint64(),
             HybridClock::TimestampFromMicrosecondsAndLogicalValue(time, 0).ToUint64());
@@ -81,10 +86,10 @@ TEST(MockHybridClockTest, TestMockedSystemClock) {
 // This is a regression test for KUDU-1345.
 TEST(MockHybridClockTest, TestClockDealsWithWrapping) {
   google::FlagSaver saver;
-  FLAGS_use_mock_wall_clock = true;
+  FLAGS_time_source = "mock";
   scoped_refptr<HybridClock> clock(new HybridClock());
-  clock->SetMockClockWallTimeForTests(1000);
   clock->Init();
+  mock_ntp(clock)->SetMockClockWallTimeForTests(1000);
 
   Timestamp prev = clock->Now();
 
@@ -103,7 +108,7 @@ TEST(MockHybridClockTest, TestClockDealsWithWrapping) {
   // Advance the time microsecond by microsecond, and ensure the clock never
   // goes backwards.
   for (int time = 1001; time < 1020; time++) {
-    clock->SetMockClockWallTimeForTests(time);
+    mock_ntp(clock)->SetMockClockWallTimeForTests(time);
     Timestamp now = clock->Now();
 
     // Clock should run strictly forwards.

http://git-wip-us.apache.org/repos/asf/kudu/blob/de8b92eb/src/kudu/clock/hybrid_clock.cc
----------------------------------------------------------------------
diff --git a/src/kudu/clock/hybrid_clock.cc b/src/kudu/clock/hybrid_clock.cc
index dfe668a..f62b3f5 100644
--- a/src/kudu/clock/hybrid_clock.cc
+++ b/src/kudu/clock/hybrid_clock.cc
@@ -18,9 +18,14 @@
 #include "kudu/clock/hybrid_clock.h"
 
 #include <algorithm>
+#include <boost/algorithm/string/predicate.hpp>
 #include <glog/logging.h>
 #include <mutex>
+#include <string>
 
+#include "kudu/clock/mock_ntp.h"
+#include "kudu/clock/system_ntp.h"
+#include "kudu/clock/system_unsync_time.h"
 #include "kudu/gutil/bind.h"
 #include "kudu/gutil/strings/substitute.h"
 #include "kudu/gutil/walltime.h"
@@ -32,10 +37,6 @@
 #include "kudu/util/metrics.h"
 #include "kudu/util/status.h"
 
-#if !defined(__APPLE__)
-#include <sys/timex.h>
-#endif // !defined(__APPLE__)
-
 DEFINE_int32(max_clock_sync_error_usec, 10 * 1000 * 1000, // 10 secs
              "Maximum allowed clock synchronization error as reported by NTP "
              "before the server will abort.");
@@ -47,10 +48,19 @@ DEFINE_bool(use_hybrid_clock, true,
             " implementation. This should be disabled for testing purposes only.");
 TAG_FLAG(use_hybrid_clock, hidden);
 
-DEFINE_bool(use_mock_wall_clock, false,
-            "Whether HybridClock should use a mock wall clock which is updated manually"
-            "instead of reading time from the system clock, for tests.");
-TAG_FLAG(use_mock_wall_clock, hidden);
+DEFINE_string(time_source, "system",
+              "The clock source that HybridClock should use. Must be one of "
+              "'system' or 'mock' (for tests only)");
+TAG_FLAG(time_source, experimental);
+DEFINE_validator(time_source, [](const char* /* flag_name */, const string& value) {
+    if (boost::iequals(value, "system") ||
+        boost::iequals(value, "mock")) {
+      return true;
+    }
+    LOG(ERROR) << "unknown value for 'time_source': '" << value << "'"
+               << " (expected one of 'system' or 'mock')";
+    return false;
+  });
 
 METRIC_DEFINE_gauge_uint64(server, hybrid_clock_timestamp,
                            "Hybrid Clock Timestamp",
@@ -69,43 +79,6 @@ namespace clock {
 
 namespace {
 
-#if !defined(__APPLE__)
-// Returns the clock modes and checks if the clock is synchronized.
-Status GetClockModes(timex* timex) {
-  // this makes ntp_adjtime a read-only call
-  timex->modes = 0;
-  int rc = ntp_adjtime(timex);
-  if (PREDICT_FALSE(rc == TIME_ERROR)) {
-    return Status::ServiceUnavailable(
-        Substitute("Error reading clock. Clock considered unsynchronized. Return code: $0",
rc));
-  }
-  // TODO what to do about leap seconds? see KUDU-146
-  if (PREDICT_FALSE(rc != TIME_OK)) {
-    LOG(ERROR) << Substitute("TODO Server undergoing leap second. Return code: $0",
rc);
-  }
-  return Status::OK();
-}
-
-// Returns the current time/max error and checks if the clock is synchronized.
-kudu::Status GetClockTime(ntptimeval* timeval) {
-  int rc = ntp_gettime(timeval);
-  switch (rc) {
-    case TIME_OK:
-      return Status::OK();
-    case -1: // generic error
-      return Status::ServiceUnavailable("Error reading clock. ntp_gettime() failed",
-                                        ErrnoToString(errno));
-    case TIME_ERROR:
-      return Status::ServiceUnavailable("Error reading clock. Clock considered unsynchronized");
-    default:
-      // TODO what to do about leap seconds? see KUDU-146
-      KLOG_FIRST_N(ERROR, 1) << "Server undergoing leap second. This may cause consistency
issues "
-        << "(rc=" << rc << ")";
-      return Status::OK();
-  }
-}
-#endif // !defined(__APPLE__)
-
 Status CheckDeadlineNotWithinMicros(const MonoTime& deadline, int64_t wait_for_usec)
{
   if (!deadline.Initialized()) {
     // No deadline.
@@ -128,56 +101,25 @@ const int HybridClock::kBitsToShift = 12;
 // This mask gives us back the logical bits.
 const uint64_t HybridClock::kLogicalBitMask = (1 << kBitsToShift) - 1;
 
-const uint64_t HybridClock::kNanosPerSec = 1000000;
-
-const double HybridClock::kAdjtimexScalingFactor = 65536;
 
 HybridClock::HybridClock()
-    : mock_clock_time_usec_(0),
-      mock_clock_max_error_usec_(0),
-#if !defined(__APPLE__)
-      divisor_(1),
-#endif
-      tolerance_adjustment_(1),
-      next_timestamp_(0),
+    : next_timestamp_(0),
       state_(kNotInitialized) {
 }
 
 Status HybridClock::Init() {
-  if (PREDICT_FALSE(FLAGS_use_mock_wall_clock)) {
-    LOG(WARNING) << "HybridClock set to mock the wall clock.";
-    state_ = kInitialized;
-    return Status::OK();
-  }
-#if defined(__APPLE__)
-  LOG(WARNING) << "HybridClock initialized in local mode (OS X only). "
-               << "Not suitable for distributed clusters.";
+  if (boost::iequals(FLAGS_time_source, "mock")) {
+    time_service_.reset(new clock::MockNtp());
+  } else if (boost::iequals(FLAGS_time_source, "system")) {
+#ifndef __APPLE__
+    time_service_.reset(new clock::SystemNtp());
 #else
-  // Read the current time. This will return an error if the clock is not synchronized.
-  uint64_t now_usec;
-  uint64_t error_usec;
-  RETURN_NOT_OK(WalltimeWithError(&now_usec, &error_usec));
-
-  timex timex;
-  RETURN_NOT_OK(GetClockModes(&timex));
-  // read whether the STA_NANO bit is set to know whether we'll get back nanos
-  // or micros in timeval.time.tv_usec. See:
-  // http://stackoverflow.com/questions/16063408/does-ntp-gettime-actually-return-nanosecond-precision
-  // set the timeval.time.tv_usec divisor so that we always get micros
-  if (timex.status & STA_NANO) {
-    divisor_ = 1000;
+    time_service_.reset(new clock::SystemUnsyncTime());
+#endif
   } else {
-    divisor_ = 1;
+    return Status::InvalidArgument("invalid NTP source", FLAGS_time_source);
   }
-
-  // Calculate the sleep skew adjustment according to the max tolerance of the clock.
-  // Tolerance comes in parts per million but needs to be applied a scaling factor.
-  tolerance_adjustment_ = (1 + ((timex.tolerance / kAdjtimexScalingFactor) / 1000000.0));
-
-  LOG(INFO) << "HybridClock initialized. Resolution in nanos?: " << (divisor_
== 1000)
-            << " Wait times tolerance adjustment: " << tolerance_adjustment_
-            << " Current error: " << error_usec;
-#endif // defined(__APPLE__)
+  RETURN_NOT_OK(time_service_->Init());
 
   state_ = kInitialized;
 
@@ -338,7 +280,7 @@ Status HybridClock::WaitUntilAfter(const Timestamp& then,
 
   // Additionally adjust the sleep time with the max tolerance adjustment
   // to account for the worst case clock skew while we're sleeping.
-  wait_for_usec *= tolerance_adjustment_;
+  wait_for_usec *= (1 + (time_service_->skew_ppm() / 1000000.0));
 
   // Check that sleeping wouldn't sleep longer than our deadline.
   RETURN_NOT_OK(CheckDeadlineNotWithinMicros(deadline, wait_for_usec));
@@ -388,47 +330,16 @@ bool HybridClock::IsAfter(Timestamp t) {
 }
 
 kudu::Status HybridClock::WalltimeWithError(uint64_t* now_usec, uint64_t* error_usec) {
-  if (PREDICT_FALSE(FLAGS_use_mock_wall_clock)) {
-    VLOG(1) << "Current clock time: " << mock_clock_time_usec_ << " error:
"
-            << mock_clock_max_error_usec_ << ". Updating to time: " <<
now_usec
-            << " and error: " << error_usec;
-    *now_usec = mock_clock_time_usec_;
-    *error_usec = mock_clock_max_error_usec_;
-  } else {
-#if defined(__APPLE__)
-    *now_usec = GetCurrentTimeMicros();
-    *error_usec = 0;
-  }
-#else
-    // Read the time. This will return an error if the clock is not synchronized.
-    ntptimeval timeval;
-    RETURN_NOT_OK(GetClockTime(&timeval));
-    *now_usec = timeval.time.tv_sec * kNanosPerSec + timeval.time.tv_usec / divisor_;
-    *error_usec = timeval.maxerror;
-  }
-
+  RETURN_NOT_OK(time_service_->WalltimeWithError(now_usec, error_usec));
   // If the clock is synchronized but has max_error beyond max_clock_sync_error_usec
   // we also return a non-ok status.
   if (*error_usec > FLAGS_max_clock_sync_error_usec) {
     return Status::ServiceUnavailable(Substitute("Error: Clock synchronized but error was"
         "too high ($0 us).", *error_usec));
   }
-#endif // defined(__APPLE__)
   return kudu::Status::OK();
 }
 
-void HybridClock::SetMockClockWallTimeForTests(uint64_t now_usec) {
-  CHECK(FLAGS_use_mock_wall_clock);
-  std::lock_guard<simple_spinlock> lock(lock_);
-  CHECK_GE(now_usec, mock_clock_time_usec_);
-  mock_clock_time_usec_ = now_usec;
-}
-
-void HybridClock::SetMockMaxClockErrorForTests(uint64_t max_error_usec) {
-  CHECK(FLAGS_use_mock_wall_clock);
-  std::lock_guard<simple_spinlock> lock(lock_);
-  mock_clock_max_error_usec_ = max_error_usec;
-}
 
 // Used to get the timestamp for metrics.
 uint64_t HybridClock::NowForMetrics() {

http://git-wip-us.apache.org/repos/asf/kudu/blob/de8b92eb/src/kudu/clock/hybrid_clock.h
----------------------------------------------------------------------
diff --git a/src/kudu/clock/hybrid_clock.h b/src/kudu/clock/hybrid_clock.h
index d6c50b6..c0516e6 100644
--- a/src/kudu/clock/hybrid_clock.h
+++ b/src/kudu/clock/hybrid_clock.h
@@ -26,6 +26,7 @@
 
 namespace kudu {
 namespace clock {
+class TimeService;
 
 // The HybridTime clock.
 //
@@ -146,21 +147,12 @@ class HybridClock : public Clock {
   // separated.
   static std::string StringifyTimestamp(const Timestamp& timestamp);
 
-  // Sets the time to be returned by a mock call to the system clock, for tests.
-  // Requires that 'FLAGS_use_mock_wall_clock' is set to true and that 'now_usec' is higher
-  // than the previously set time.
-  // NOTE: This refers to the time returned by the system clock, not the time returned
-  // by HybridClock, i.e. 'now_usec' is not a HybridTime timestmap and shouldn't have
-  // a logical component.
-  void SetMockClockWallTimeForTests(uint64_t now_usec);
-
-  // Sets the max. error to be returned by a mock call to the system clock, for tests.
-  // Requires that 'FLAGS_use_mock_wall_clock' is set to true.
-  // This can be used to make HybridClock report the wall clock as unsynchronized, by
-  // setting error to be more than the configured tolerance.
-  void SetMockMaxClockErrorForTests(uint64_t max_error_usec);
+  clock::TimeService* time_service() {
+    return time_service_.get();
+  }
 
  private:
+  friend class TestNtp;
 
   // Obtains the current wallclock time and maximum error in microseconds,
   // and checks if the clock is synchronized.
@@ -174,19 +166,9 @@ class HybridClock : public Clock {
   // Used to get the current error, for metrics.
   uint64_t ErrorForMetrics();
 
-  // Set by calls to SetMockClockWallTimeForTests().
-  // For testing purposes only.
-  uint64_t mock_clock_time_usec_;
-
-  // Set by calls to SetMockClockErrorForTests().
-  // For testing purposes only.
-  uint64_t mock_clock_max_error_usec_;
-
-#if !defined(__APPLE__)
-  uint64_t divisor_;
-#endif
-
-  double tolerance_adjustment_;
+  // Used to fetch the current time and error bound from the system or NTP
+  // service.
+  std::unique_ptr<clock::TimeService> time_service_;
 
   mutable simple_spinlock lock_;
 
@@ -201,12 +183,6 @@ class HybridClock : public Clock {
   // Mask to extract the pure logical bits.
   static const uint64_t kLogicalBitMask;
 
-  static const uint64_t kNanosPerSec;
-
-  // The scaling factor used to obtain ppms. From the adjtimex source:
-  // "scale factor used by adjtimex freq param.  1 ppm = 65536"
-  static const double kAdjtimexScalingFactor;
-
   enum State {
     kNotInitialized,
     kInitialized

http://git-wip-us.apache.org/repos/asf/kudu/blob/de8b92eb/src/kudu/clock/mock_ntp.cc
----------------------------------------------------------------------
diff --git a/src/kudu/clock/mock_ntp.cc b/src/kudu/clock/mock_ntp.cc
new file mode 100644
index 0000000..991fa2b
--- /dev/null
+++ b/src/kudu/clock/mock_ntp.cc
@@ -0,0 +1,48 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you 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 "kudu/clock/mock_ntp.h"
+
+#include "kudu/util/locks.h"
+#include "kudu/util/status.h"
+
+namespace kudu {
+namespace clock {
+
+Status MockNtp::WalltimeWithError(uint64_t* now_usec, uint64_t* error_usec) {
+  std::lock_guard<simple_spinlock> lock(lock_);
+  VLOG(1) << "Current clock time: " << mock_clock_time_usec_ << " error:
"
+          << mock_clock_max_error_usec_ << ". Updating to time: " << now_usec
+          << " and error: " << error_usec;
+  *now_usec = mock_clock_time_usec_;
+  *error_usec = mock_clock_max_error_usec_;
+  return Status::OK();
+}
+
+void MockNtp::SetMockClockWallTimeForTests(uint64_t now_usec) {
+  std::lock_guard<simple_spinlock> lock(lock_);
+  CHECK_GE(now_usec, mock_clock_time_usec_);
+  mock_clock_time_usec_ = now_usec;
+}
+
+void MockNtp::SetMockMaxClockErrorForTests(uint64_t max_error_usec) {
+  std::lock_guard<simple_spinlock> lock(lock_);
+  mock_clock_max_error_usec_ = max_error_usec;
+}
+
+} // namespace clock
+} // namespace kudu

http://git-wip-us.apache.org/repos/asf/kudu/blob/de8b92eb/src/kudu/clock/mock_ntp.h
----------------------------------------------------------------------
diff --git a/src/kudu/clock/mock_ntp.h b/src/kudu/clock/mock_ntp.h
new file mode 100644
index 0000000..9056efe
--- /dev/null
+++ b/src/kudu/clock/mock_ntp.h
@@ -0,0 +1,70 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you 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.
+#pragma once
+
+#include "kudu/clock/time_service.h"
+#include "kudu/gutil/macros.h"
+#include "kudu/util/locks.h"
+#include "kudu/util/status.h"
+
+namespace kudu {
+namespace clock {
+
+// Mock implementation of TimeService.
+//
+// This is used for various tests which rely on manually advancing time.
+class MockNtp : public TimeService {
+ public:
+  MockNtp() = default;
+  virtual ~MockNtp() = default;
+
+  virtual Status Init() override {
+    return Status::OK();
+  }
+
+  virtual Status WalltimeWithError(uint64_t* now_usec, uint64_t* error_usec) override;
+
+  virtual int64_t skew_ppm() const override {
+    // Just return the same constant as the default configuration for NTP:
+    // the local clock frequency may accumulate error at a max rate of
+    // 500us per second.
+    return 500;
+  }
+
+  // Sets the time to be returned by a mock call to the system clock, for tests.
+  // Requires that 'now_usec' is higher than the previously set time.
+  // NOTE: This refers to the time returned by the system clock, not the time returned
+  // by HybridClock, i.e. 'now_usec' is not a HybridTime timestamp and shouldn't have
+  // a logical component.
+  void SetMockClockWallTimeForTests(uint64_t now_usec);
+
+  // Sets the max. error to be returned by a mock call to the system clock, for tests.
+  // This can be used to make HybridClock report the wall clock as unsynchronized, by
+  // setting error to be more than the configured tolerance.
+  void SetMockMaxClockErrorForTests(uint64_t max_error_usec);
+
+ private:
+  simple_spinlock lock_;
+
+  uint64_t mock_clock_time_usec_ = 0;
+  uint64_t mock_clock_max_error_usec_ = 0;
+
+  DISALLOW_COPY_AND_ASSIGN(MockNtp);
+};
+
+} // namespace clock
+} // namespace kudu

http://git-wip-us.apache.org/repos/asf/kudu/blob/de8b92eb/src/kudu/clock/system_ntp.cc
----------------------------------------------------------------------
diff --git a/src/kudu/clock/system_ntp.cc b/src/kudu/clock/system_ntp.cc
new file mode 100644
index 0000000..b687c6a
--- /dev/null
+++ b/src/kudu/clock/system_ntp.cc
@@ -0,0 +1,115 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you 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 "kudu/clock/system_ntp.h"
+
+#include <sys/timex.h>
+
+#include "kudu/gutil/strings/substitute.h"
+#include "kudu/util/errno.h"
+#include "kudu/util/logging.h"
+#include "kudu/util/status.h"
+
+namespace kudu {
+namespace clock {
+
+using strings::Substitute;
+
+const double SystemNtp::kAdjtimexScalingFactor = 65536;
+const uint64_t SystemNtp::kNanosPerSec = 1000000;
+
+namespace {
+
+// Returns the clock modes and checks if the clock is synchronized.
+Status GetClockModes(timex* timex) {
+  // this makes ntp_adjtime a read-only call
+  timex->modes = 0;
+  int rc = ntp_adjtime(timex);
+  if (PREDICT_FALSE(rc == TIME_ERROR)) {
+    return Status::ServiceUnavailable(
+        Substitute("Error reading clock. Clock considered unsynchronized. Return code: $0",
rc));
+  }
+  // TODO what to do about leap seconds? see KUDU-146
+  if (PREDICT_FALSE(rc != TIME_OK)) {
+    LOG(ERROR) << Substitute("TODO Server undergoing leap second. Return code: $0",
rc);
+  }
+  return Status::OK();
+}
+
+// Returns the current time/max error and checks if the clock is synchronized.
+Status GetClockTime(ntptimeval* timeval) {
+  int rc = ntp_gettime(timeval);
+  switch (rc) {
+    case TIME_OK:
+      return Status::OK();
+    case -1: // generic error
+      return Status::ServiceUnavailable("Error reading clock. ntp_gettime() failed",
+                                        ErrnoToString(errno));
+    case TIME_ERROR:
+      return Status::ServiceUnavailable("Error reading clock. Clock considered unsynchronized");
+    default:
+      // TODO what to do about leap seconds? see KUDU-146
+      KLOG_FIRST_N(ERROR, 1) << "Server undergoing leap second. This may cause consistency
issues "
+        << "(rc=" << rc << ")";
+      return Status::OK();
+  }
+}
+
+} // anonymous namespace
+
+Status SystemNtp::Init() {
+  // Read the current time. This will return an error if the clock is not synchronized.
+  uint64_t now_usec;
+  uint64_t error_usec;
+  RETURN_NOT_OK(WalltimeWithError(&now_usec, &error_usec));
+
+  timex timex;
+  RETURN_NOT_OK(GetClockModes(&timex));
+  // read whether the STA_NANO bit is set to know whether we'll get back nanos
+  // or micros in timeval.time.tv_usec. See:
+  // http://stackoverflow.com/questions/16063408/does-ntp-gettime-actually-return-nanosecond-precision
+  // set the timeval.time.tv_usec divisor so that we always get micros
+  if (timex.status & STA_NANO) {
+    divisor_ = 1000;
+  } else {
+    divisor_ = 1;
+  }
+
+  // Calculate the sleep skew adjustment according to the max tolerance of the clock.
+  // Tolerance comes in parts per million but needs to be applied a scaling factor.
+  skew_ppm_ = timex.tolerance / kAdjtimexScalingFactor;
+
+  LOG(INFO) << "NTP initialized. Resolution in nanos?: " << (divisor_ == 1000)
+            << " Skew PPM: " << skew_ppm_
+            << " Current error: " << error_usec;
+
+  return Status::OK();
+}
+
+
+Status SystemNtp::WalltimeWithError(uint64_t *now_usec,
+                                    uint64_t *error_usec) {
+  // Read the time. This will return an error if the clock is not synchronized.
+  ntptimeval timeval;
+  RETURN_NOT_OK(GetClockTime(&timeval));
+  *now_usec = timeval.time.tv_sec * kNanosPerSec + timeval.time.tv_usec / divisor_;
+  *error_usec = timeval.maxerror;
+  return Status::OK();
+}
+
+} // namespace clock
+} // namespace kudu

http://git-wip-us.apache.org/repos/asf/kudu/blob/de8b92eb/src/kudu/clock/system_ntp.h
----------------------------------------------------------------------
diff --git a/src/kudu/clock/system_ntp.h b/src/kudu/clock/system_ntp.h
new file mode 100644
index 0000000..e62c03e
--- /dev/null
+++ b/src/kudu/clock/system_ntp.h
@@ -0,0 +1,65 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you 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.
+#pragma once
+
+#include "kudu/clock/time_service.h"
+#include "kudu/gutil/macros.h"
+#include "kudu/util/status.h"
+
+namespace kudu {
+namespace clock {
+
+// TimeService implementation which uses the 'ntp_adjtime' call (corresponding to the
+// 'adjtimex' syscall) to consult the Linux kernel for the current time
+// and error bound.
+//
+// This implementation relies on the ntpd service running on the local host
+// to keep the kernel's timekeeping up to date and in sync.
+class SystemNtp : public TimeService {
+ public:
+  SystemNtp() = default;
+
+  // Ensure that the kernel's timekeeping status indicates that it is currently
+  // in sync, and initialize various internal parameters.
+  virtual Status Init() override;
+
+  virtual Status WalltimeWithError(uint64_t* now_usec, uint64_t* error_usec) override;
+
+  virtual int64_t skew_ppm() const override {
+    return skew_ppm_;
+  }
+
+ private:
+  // The scaling factor used to obtain ppms. From the adjtimex source:
+  // "scale factor used by adjtimex freq param.  1 ppm = 65536"
+  static const double kAdjtimexScalingFactor;
+
+  static const uint64_t kNanosPerSec;
+
+  // The divisor for the 'timeval.time.tv_usec' returned from the kernel.
+  // Despite the name 'tv_usec', in fact in some cases it will return a nanosecond
+  // value, in which case we may need to divide.
+  uint64_t divisor_ = 1;
+
+  // The skew rate in PPM reported by the kernel.
+  uint64_t skew_ppm_ = 0;
+
+  DISALLOW_COPY_AND_ASSIGN(SystemNtp);
+};
+
+} // namespace clock
+} // namespace kudu

http://git-wip-us.apache.org/repos/asf/kudu/blob/de8b92eb/src/kudu/clock/system_unsync_time.cc
----------------------------------------------------------------------
diff --git a/src/kudu/clock/system_unsync_time.cc b/src/kudu/clock/system_unsync_time.cc
new file mode 100644
index 0000000..5c2e39f
--- /dev/null
+++ b/src/kudu/clock/system_unsync_time.cc
@@ -0,0 +1,39 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you 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 "kudu/clock/system_unsync_time.h"
+
+#include "kudu/gutil/walltime.h"
+
+namespace kudu {
+namespace clock {
+
+Status SystemUnsyncTime::Init() {
+  LOG(WARNING) << "NTP support is disabled. Clock error bounds will not "
+               << "be accurate. This configuration is not suitable for "
+               << "distributed clusters.";
+  return Status::OK();
+}
+
+Status SystemUnsyncTime::WalltimeWithError(uint64_t* now_usec, uint64_t* error_usec) {
+  *now_usec = GetCurrentTimeMicros();
+  *error_usec = 0;
+  return Status::OK();
+}
+
+} // namespace clock
+} // namespace kudu

http://git-wip-us.apache.org/repos/asf/kudu/blob/de8b92eb/src/kudu/clock/system_unsync_time.h
----------------------------------------------------------------------
diff --git a/src/kudu/clock/system_unsync_time.h b/src/kudu/clock/system_unsync_time.h
new file mode 100644
index 0000000..a9b8838
--- /dev/null
+++ b/src/kudu/clock/system_unsync_time.h
@@ -0,0 +1,48 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you 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.
+#pragma once
+
+#include "kudu/clock/time_service.h"
+#include "kudu/gutil/macros.h"
+#include "kudu/util/status.h"
+
+namespace kudu {
+namespace clock {
+
+// TimeService implementation which uses the system clock without requiring
+// that NTP is running or synchronized.
+//
+// This is useful on OSX which doesn't support the required adjtimex() syscall
+// to query the NTP synchronization status.
+class SystemUnsyncTime : public TimeService {
+ public:
+  SystemUnsyncTime() = default;
+
+  virtual Status Init() override;
+
+  virtual Status WalltimeWithError(uint64_t* now_usec, uint64_t* error_usec) override;
+
+  virtual int64_t skew_ppm() const override {
+    return 500; // Reasonable default value.
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SystemUnsyncTime);
+};
+
+} // namespace clock
+} // namespace kudu

http://git-wip-us.apache.org/repos/asf/kudu/blob/de8b92eb/src/kudu/clock/time_service.h
----------------------------------------------------------------------
diff --git a/src/kudu/clock/time_service.h b/src/kudu/clock/time_service.h
new file mode 100644
index 0000000..82cfce8
--- /dev/null
+++ b/src/kudu/clock/time_service.h
@@ -0,0 +1,55 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you 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.
+#pragma once
+
+#include "kudu/gutil/macros.h"
+
+namespace kudu {
+class Status;
+
+namespace clock {
+
+// Interface encapsulating interaction with a time service (eg NTP).
+class TimeService {
+ public:
+  TimeService() = default;
+  virtual ~TimeService() = default;
+
+  // Initialize the NTP source, validating that it is available and
+  // properly synchronized.
+  virtual Status Init() = 0;
+
+  // Return the current wall time in microseconds since the Unix epoch in '*now_usec'.
+  // The current maximum error bound in microseconds is returned in '*error_usec'.
+  //
+  // May return a bad Status if the NTP service has become unsynchronized or otherwise
+  // unavailable.
+  virtual Status WalltimeWithError(uint64_t* now_usec, uint64_t* error_usec) = 0;
+
+  // Return the estimated max amount of clock skew as configured by this NTP service.
+  // This is expressed in PPM (parts-per-million) and indicates the maximum number
+  // of microseconds by which the local clock can be expected to drift for each
+  // elapsed second of actual time.
+  //
+  // For example, if the local monotonic clock indicates that 1 second has elapsed,
+  // and skew_ppm() returns 500, then the actual time may have actually changed by
+  // 1sec +/- 500us.
+  virtual int64_t skew_ppm() const = 0;
+};
+
+} // namespace clock
+} // namespace kudu

http://git-wip-us.apache.org/repos/asf/kudu/blob/de8b92eb/src/kudu/integration-tests/consistency-itest.cc
----------------------------------------------------------------------
diff --git a/src/kudu/integration-tests/consistency-itest.cc b/src/kudu/integration-tests/consistency-itest.cc
index 6aa2f23..7d8dec1 100644
--- a/src/kudu/integration-tests/consistency-itest.cc
+++ b/src/kudu/integration-tests/consistency-itest.cc
@@ -29,6 +29,7 @@
 #include "kudu/client/scanner-internal.h"
 #include "kudu/client/shared_ptr.h"
 #include "kudu/clock/hybrid_clock.h"
+#include "kudu/clock/mock_ntp.h"
 #include "kudu/common/partial_row.h"
 #include "kudu/gutil/stringprintf.h"
 #include "kudu/gutil/strings/substitute.h"
@@ -51,7 +52,7 @@
 DECLARE_int32(heartbeat_interval_ms);
 DECLARE_int32(max_clock_sync_error_usec);
 DECLARE_int32(scanner_gc_check_interval_us);
-DECLARE_bool(use_mock_wall_clock);
+DECLARE_string(time_source);
 
 using kudu::client::ScanConfiguration;
 using kudu::client::sp::shared_ptr;
@@ -80,7 +81,7 @@ class ConsistencyITest : public MiniClusterITestBase {
         key_split_value_(8) {
 
     // Using the mock clock: need to advance the clock for tablet servers.
-    FLAGS_use_mock_wall_clock = true;
+    FLAGS_time_source = "mock";
 
     // Reduce the TS<->Master heartbeat interval: this speeds up testing,
     // saving about 700ms per test.
@@ -104,7 +105,8 @@ class ConsistencyITest : public MiniClusterITestBase {
   static void UpdateClock(HybridClock* clock, MonoDelta delta) {
     const uint64_t new_time(HybridClock::GetPhysicalValueMicros(clock->Now()) +
                             delta.ToMicroseconds());
-    clock->SetMockClockWallTimeForTests(new_time);
+    auto* ntp = down_cast<clock::MockNtp*>(clock->time_service());
+    ntp->SetMockClockWallTimeForTests(new_time);
   }
 
   // Creates a table with the specified name and replication factor.

http://git-wip-us.apache.org/repos/asf/kudu/blob/de8b92eb/src/kudu/integration-tests/tablet_history_gc-itest.cc
----------------------------------------------------------------------
diff --git a/src/kudu/integration-tests/tablet_history_gc-itest.cc b/src/kudu/integration-tests/tablet_history_gc-itest.cc
index 009df58..a0c4a7c 100644
--- a/src/kudu/integration-tests/tablet_history_gc-itest.cc
+++ b/src/kudu/integration-tests/tablet_history_gc-itest.cc
@@ -22,6 +22,7 @@
 
 #include "kudu/client/client-test-util.h"
 #include "kudu/clock/hybrid_clock.h"
+#include "kudu/clock/mock_ntp.h"
 #include "kudu/common/wire_protocol-test-util.h"
 #include "kudu/gutil/map-util.h"
 #include "kudu/gutil/ref_counted.h"
@@ -52,7 +53,7 @@ using std::vector;
 using strings::Substitute;
 
 DECLARE_bool(enable_maintenance_manager);
-DECLARE_bool(use_mock_wall_clock);
+DECLARE_string(time_source);
 DECLARE_double(missed_heartbeats_before_rejecting_snapshot_scans);
 DECLARE_int32(flush_threshold_secs);
 DECLARE_int32(maintenance_manager_num_threads);
@@ -73,7 +74,8 @@ class TabletHistoryGcITest : public MiniClusterITestBase {
   void AddTimeToHybridClock(HybridClock* clock, MonoDelta delta) {
     uint64_t now = HybridClock::GetPhysicalValueMicros(clock->Now());
     uint64_t new_time = now + delta.ToMicroseconds();
-    clock->SetMockClockWallTimeForTests(new_time);
+    auto* ntp = down_cast<clock::MockNtp*>(clock->time_service());
+    ntp->SetMockClockWallTimeForTests(new_time);
   }
 };
 
@@ -103,7 +105,7 @@ TEST_F(TabletHistoryGcITest, TestSnapshotScanBeforeAHM) {
 
 // Check that the maintenance manager op to delete undo deltas actually deletes them.
 TEST_F(TabletHistoryGcITest, TestUndoDeltaBlockGc) {
-  FLAGS_use_mock_wall_clock = true; // Allow moving the clock.
+  FLAGS_time_source = "mock"; // Allow moving the clock.
   FLAGS_tablet_history_max_age_sec = 1000;
   FLAGS_flush_threshold_secs = 0; // Flush as aggressively as possible.
   FLAGS_maintenance_manager_polling_interval_ms = 1; // Spin on MM for a quick test.
@@ -238,7 +240,7 @@ class RandomizedTabletHistoryGcITest : public TabletHistoryGcITest {
   RandomizedTabletHistoryGcITest() {
     // We need to fully control compactions, flushes, and the clock.
     FLAGS_enable_maintenance_manager = false;
-    FLAGS_use_mock_wall_clock = true;
+    FLAGS_time_source = "mock";
     FLAGS_tablet_history_max_age_sec = 100;
 
     // Set these really high since we're using the mock clock.
@@ -508,7 +510,8 @@ TEST_F(RandomizedTabletHistoryGcITest, TestRandomHistoryGCWorkload) {
   // Set initial clock time to 1000 seconds past 0, which is enough so that the
   // AHM will not be negative.
   const uint64_t kInitialMicroTime = 1L * 1000 * 1000 * 1000;
-  clock_->SetMockClockWallTimeForTests(kInitialMicroTime);
+  auto* ntp = down_cast<clock::MockNtp*>(clock_->time_service());
+  ntp->SetMockClockWallTimeForTests(kInitialMicroTime);
 
   LOG(INFO) << "Seeding random number generator";
   Random random(SeedRandom());

http://git-wip-us.apache.org/repos/asf/kudu/blob/de8b92eb/src/kudu/server/generic_service.cc
----------------------------------------------------------------------
diff --git a/src/kudu/server/generic_service.cc b/src/kudu/server/generic_service.cc
index 422865f..e7b8e9c 100644
--- a/src/kudu/server/generic_service.cc
+++ b/src/kudu/server/generic_service.cc
@@ -23,6 +23,7 @@
 
 #include "kudu/clock/clock.h"
 #include "kudu/clock/hybrid_clock.h"
+#include "kudu/clock/mock_ntp.h"
 #include "kudu/gutil/map-util.h"
 #include "kudu/rpc/remote_user.h"
 #include "kudu/rpc/rpc_context.h"
@@ -31,7 +32,7 @@
 #include "kudu/util/debug/leak_annotations.h"
 #include "kudu/util/flag_tags.h"
 
-DECLARE_bool(use_mock_wall_clock);
+DECLARE_string(time_source);
 DECLARE_bool(use_hybrid_clock);
 
 using std::string;
@@ -169,18 +170,19 @@ void GenericServiceImpl::ServerClock(const ServerClockRequestPB* req,
 void GenericServiceImpl::SetServerWallClockForTests(const SetServerWallClockForTestsRequestPB
*req,
                                                    SetServerWallClockForTestsResponsePB *resp,
                                                    rpc::RpcContext *context) {
-  if (!FLAGS_use_hybrid_clock || !FLAGS_use_mock_wall_clock) {
+  if (!FLAGS_use_hybrid_clock || FLAGS_time_source != "mock") {
     LOG(WARNING) << "Error setting wall clock for tests. Server is not using HybridClock"
-        "or was not started with '--use_mock_wall_clock= true'";
+        "or was not started with '--ntp-source=mock'";
     resp->set_success(false);
   }
 
-  clock::HybridClock* clock = down_cast<clock::HybridClock*>(server_->clock());
+  auto* clock = down_cast<clock::HybridClock*>(server_->clock());
+  auto* mock = down_cast<clock::MockNtp*>(clock->time_service());
   if (req->has_now_usec()) {
-    clock->SetMockClockWallTimeForTests(req->now_usec());
+    mock->SetMockClockWallTimeForTests(req->now_usec());
   }
   if (req->has_max_error_usec()) {
-    clock->SetMockMaxClockErrorForTests(req->max_error_usec());
+    mock->SetMockMaxClockErrorForTests(req->max_error_usec());
   }
   resp->set_success(true);
   context->RespondSuccess();

http://git-wip-us.apache.org/repos/asf/kudu/blob/de8b92eb/src/kudu/server/server_base.proto
----------------------------------------------------------------------
diff --git a/src/kudu/server/server_base.proto b/src/kudu/server/server_base.proto
index 022942a..58bdf1b 100644
--- a/src/kudu/server/server_base.proto
+++ b/src/kudu/server/server_base.proto
@@ -111,7 +111,7 @@ message GetStatusResponsePB {
 
 // Makes the HybridClock of the server use these values for wall clock time and error,
 // for testing purposes.
-// Requires that the server was started with '--use_mock_wall_clock=true'.
+// Requires that the server was started with '--time_source=mock'.
 message SetServerWallClockForTestsRequestPB {
   optional uint64 now_usec = 1;
   optional uint64 max_error_usec = 2;

http://git-wip-us.apache.org/repos/asf/kudu/blob/de8b92eb/src/kudu/tablet/tablet_history_gc-test.cc
----------------------------------------------------------------------
diff --git a/src/kudu/tablet/tablet_history_gc-test.cc b/src/kudu/tablet/tablet_history_gc-test.cc
index 7c75fa7..5aae7b5 100644
--- a/src/kudu/tablet/tablet_history_gc-test.cc
+++ b/src/kudu/tablet/tablet_history_gc-test.cc
@@ -19,6 +19,7 @@
 #include <gflags/gflags.h>
 
 #include "kudu/clock/hybrid_clock.h"
+#include "kudu/clock/mock_ntp.h"
 #include "kudu/tablet/compaction.h"
 #include "kudu/tablet/mvcc.h"
 #include "kudu/tablet/tablet-test-base.h"
@@ -27,7 +28,7 @@
 
 DECLARE_bool(enable_maintenance_manager);
 DECLARE_int32(tablet_history_max_age_sec);
-DECLARE_bool(use_mock_wall_clock);
+DECLARE_string(time_source);
 
 using kudu::clock::HybridClock;
 
@@ -51,13 +52,19 @@ class TabletHistoryGcTest : public TabletTestBase<IntKeyTestSetup<INT64>>
{
 
   TabletHistoryGcTest()
       : Superclass(TabletHarness::Options::HYBRID_CLOCK) {
-    FLAGS_use_mock_wall_clock = true;
+    FLAGS_time_source = "mock";
+  }
+
+  void SetMockTime(int64_t micros) {
+    auto* hybrid_clock = down_cast<HybridClock*>(clock());
+    auto* ntp = down_cast<clock::MockNtp*>(hybrid_clock->time_service());
+    ntp->SetMockClockWallTimeForTests(micros);
   }
 
   virtual void SetUp() OVERRIDE {
     NO_FATALS(TabletTestBase<IntKeyTestSetup<INT64>>::SetUp());
     // Mock clock defaults to 0 and this screws up the AHM calculation which ends up negative.
-    down_cast<HybridClock*>(clock())->SetMockClockWallTimeForTests(GetCurrentTimeMicros());
+    SetMockTime(GetCurrentTimeMicros());
   }
 
  protected:
@@ -71,7 +78,7 @@ class TabletHistoryGcTest : public TabletTestBase<IntKeyTestSetup<INT64>>
{
   void AddTimeToHybridClock(MonoDelta delta) {
     uint64_t now = HybridClock::GetPhysicalValueMicros(clock()->Now());
     uint64_t new_time = now + delta.ToMicroseconds();
-    down_cast<HybridClock*>(clock())->SetMockClockWallTimeForTests(new_time);
+    SetMockTime(new_time);
   }
 
   int64_t TotalNumRows() const { return num_rowsets_ * rows_per_rowset_; }


Mime
View raw message