The challenge in implementing a TrueTime API lies in the guarantees you must provide. Namely, the absolute time must never be outside the TrueTime interval on any server in the system. If this can happen, then absolute ordering of events is lost, as are most of the guarantees of Spanner.
The Spanner paper achieves this by a combination of means (section 3):
- Multiple time servers, with disparate sources (GPS, atomic clocks), including time servers from other datacenters.
- Marzullo’s algorithm to detect liars and multiplex the various trusted time sources into an update of the local machine clock.
- An assumed clock drift of 200us/s at spanservers, applied between clock synchronizations.
- Kicking machines from the system that exhibit measured local clock drift > threshold (threshold << 200us/s by necessity).
Now, you can achieve this with simpler means - NTP and an assumed error interval of 10 minutes would trivially do. But as noted in the question, there are performance implications to this. Read-write transactions (4.2.1) must wait on commit, with an expected wait time of 2*errorAverage - 20 minutes in this example. Similarly, read-only transactions (4.2.2) at time "now" - rather than a time in the past - must wait for safetime to advance far enough; at least 10 minutes in this example. So to have a high performance system, you need to minimize the error intervals as far as possible, without losing your guarantees, which is where the complexity arises.
I'm not sure how ntp_adjtime is being called in your system - it's possible it's already being set using multiple untrusted and uncorrelated time sources, in which case you're most of the way there already. If you can also ensure that the maxerror value is guaranteed to be advancing faster than the possible clock drift of your system, you should be good to go. Most of the performance of Spanner, without your own personal atomic clock :).