Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Logical vs Physical Clock Optimistic Locking

DZone's Guide to

Logical vs Physical Clock Optimistic Locking

· Java Zone
Free Resource

What every Java engineer should know about microservices: Reactive Microservices Architecture.  Brought to you in partnership with Lightbend.

Introduction

In my previous post I demonstrated why optimistic locking is the only viable solution for application-level transactions. Optimistic locking requires a version column that can be represented as:

  • a physical clock (a timestamp value taken from the system clock)
  • a logical clock (an incrementing numeric value)

This article will demonstrate why logical clocks are better suited for optimistic locking mechanisms.

System time

The system time is provided by the operating system internal clocking algorithm. The programmable interval timer periodically sends an interrupt signal (with a frequency of 1.193182 MHz). The CPU receives the time interruption and increments a tick counter.

Both Unix and Window record time as the number of ticks since a predefined absolute time reference (an epoch). The operating system clock resolution varies from 1ms (Android) to 100ns (Windows) and to 1ns (Unix).

Monotonic time

To order events, the version must advance monotonically. While incrementing a local counter is a monotonic function, system time might not always return monotonic timestamps.

Java has two ways of fetching the current system time. You can either use:

  1. System#currentTimeMillis(), that gives you the number of milliseconds elapsed since Unix epoch This method doesn’t give you monotonic time results because it returns the wall clock time which is prone to both forward and backward adjustments (if NTP is used for system time synchronization).For monotonic currentTimeMillis, you can check Peter Lawrey’s solutionor Bitronix Transaction Manager Monotonic Clock.
  2. System#nanoTime(), that returns the number of nanoseconds elapsed since an arbitrarily chosen time reference
  3. This method tries to use the current operating system monotonic clock implementation, but it falls back to wall clock time if no monotonic clock could be found.

Argument 1: System time is not always monotonically incremented.

Database timestamp precision

The SQL-92 standard defines the TIMESTAMP data type as YYYY-MM-DD hh:mm:ss. The fraction part is optional and each database implements a specific timestamp data type:

RDBMS TIMESTAMP RESOLUTION
Oracle TIMESTAMP(9) may use up to 9 fractional digits (nano second precision).
MSSQL DATETIME2 has a precision of 100ns.
MySQL MySQL 5.6.4 added microseconds precision support for TIME, DATETIME, and TIMESTAMP types (e.g. TIMESTAMP(6)).
Previous MySQL versions discard the fractional part of all temporal types.
PostgreSQL Both TIME and TIMESTAMP types have microsecond precision.
DB2 TIMESTAMP(12) may use up to 12 fractional digits (picosecond precision).

When it comes to persisting timestamps, most database servers offer at least 6 fractional digits. MySQL users have long been waiting for a more precise temporal type and the 5.6.4 version had finally added microsecond precision.

On a pre-5.6.4 MySQL database server, updates might be lost during the lifespan of any given second. That’s because all transactions updating the same database row will see the same version timestamp (which points to the beginning of the current running second).

Argument 2: Pre-5.6.4 MySQL versions only support second precision timestamps.

Handling time is not that easy

Incrementing a local version number is always safer because this operation doesn’t depends on any external factors. If the database row already contains a higher version number your data has become stale. It’s as simple as that.

On the other hand, time is one of the most complicated dimension to deal with. If you don’t believe me, check the for daylight saving time handling considerations.

It took 8 versions for Java to finally come up with a mature Date/Time API. Handling time across application layers (from JavaScript, to Java middle-ware to database date/time types) makes matters worse.

Argument 3: Handling system time is a challenging job. You have to hanlde leap secondsdaylight savingtime zones and various time standards

Lessons from distributed computing

Optimistic locking is all about event ordering, so naturally we’re only interested in the happened-before relationship.

In distributed computing, logical clocks are favored over physical ones (system clock), because networks time synchronization implies variable latencies.

Sequence number versioning is similar to Lamport timestamps algorithm, each event incrementing only one counter.

While Lamport timestamps was defined for multiple distributed nodes event synchronization, database optimistic locking is much simpler, because there is only on node (the database server) where all transactions are synchronized (coming from concurrent client connections).

Argument 4: Distributed computing favors logical clock over physical ones, because we are only interested in event ordering anyway.

If you enjoyed this article, I bet you are going to love my book as well.






Conclusion

Using physical time might seem convenient at first, but it turns out to be a naive solution. In a distributed environment, perfect system time synchronization is mostly unlikely. All in all, you should always prefer logical clocks when implementing an optimistic locking mechanism.

Microservices for Java, explained. Revitalize your legacy systems (and your career) with Reactive Microservices Architecture, a free O'Reilly book. Brought to you in partnership with Lightbend.

Topics:
java ,sql ,persistence

Published at DZone with permission of Vlad Mihalcea. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}