Added correct ordering for TimeBasedUUID offset (#28387)
This commit is contained in:
parent
aa2493c475
commit
da0a906b08
3 changed files with 105 additions and 1 deletions
66
akka-actor/src/main/scala/akka/util/UUIDComparator.scala
Normal file
66
akka-actor/src/main/scala/akka/util/UUIDComparator.scala
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Copyright (C) 2016-2019 Lightbend Inc. <https://www.lightbend.com>
|
||||
*/
|
||||
|
||||
package akka.util
|
||||
|
||||
import java.util.Comparator
|
||||
import java.util.UUID
|
||||
|
||||
/**
|
||||
* Scala implementation of UUIDComparator in
|
||||
* https://github.com/cowtowncoder/java-uuid-generator
|
||||
* Apache License 2.0.
|
||||
*/
|
||||
class UUIDComparator extends Comparator[UUID] {
|
||||
|
||||
def compare(u1: UUID, u2: UUID): Int = {
|
||||
// First: major sorting by types
|
||||
val version = u1.version()
|
||||
val diff = version - u2.version()
|
||||
if (diff != 0) {
|
||||
diff
|
||||
} else {
|
||||
// Second: for time-based variant, order by time stamp:
|
||||
if (version == 1) {
|
||||
val diff2 = compareULongs(u1.timestamp(), u2.timestamp())
|
||||
if (diff2 == 0) {
|
||||
// or if that won't work, by other bits lexically
|
||||
compareULongs(u1.getLeastSignificantBits(), u2.getLeastSignificantBits())
|
||||
} else
|
||||
diff2
|
||||
} else {
|
||||
// note: java.util.Uuids compares with sign extension, IMO that's wrong, so:
|
||||
val diff2 = compareULongs(u1.getMostSignificantBits(), u2.getMostSignificantBits())
|
||||
if (diff2 == 0) {
|
||||
compareULongs(u1.getLeastSignificantBits(), u2.getLeastSignificantBits())
|
||||
} else
|
||||
diff2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def compareULongs(l1: Long, l2: Long): Int = {
|
||||
val diff = compareUInts((l1 >> 32).toInt, (l2 >> 32).toInt)
|
||||
if (diff == 0)
|
||||
compareUInts(l1.toInt, l2.toInt)
|
||||
else
|
||||
diff
|
||||
}
|
||||
|
||||
private def compareUInts(i1: Int, i2: Int): Int =
|
||||
/* bit messier due to java's insistence on signed values: if both
|
||||
* have same sign, normal comparison (by subtraction) works fine;
|
||||
* but if signs don't agree need to resolve differently
|
||||
*/
|
||||
if (i1 < 0) {
|
||||
if (i2 < 0) (i1 - i2) else 1
|
||||
} else {
|
||||
if (i2 < 0) -1 else (i1 - i2)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
object UUIDComparator {
|
||||
val comparator = new UUIDComparator
|
||||
}
|
||||
|
|
@ -6,6 +6,8 @@ package akka.persistence.query
|
|||
|
||||
import java.util.UUID
|
||||
|
||||
import akka.util.UUIDComparator
|
||||
|
||||
object Offset {
|
||||
|
||||
// factories to aid discoverability
|
||||
|
|
@ -44,7 +46,7 @@ final case class TimeBasedUUID(value: UUID) extends Offset with Ordered[TimeBase
|
|||
throw new IllegalArgumentException("UUID " + value + " is not a time-based UUID")
|
||||
}
|
||||
|
||||
override def compare(other: TimeBasedUUID): Int = value.compareTo(other.value)
|
||||
override def compare(other: TimeBasedUUID): Int = UUIDComparator.comparator.compare(value, other.value)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright (C) 2019 Lightbend Inc. <https://www.lightbend.com>
|
||||
*/
|
||||
|
||||
package akka.persistence.query
|
||||
|
||||
import java.util.UUID
|
||||
|
||||
import org.scalatest.{ Matchers, WordSpecLike }
|
||||
|
||||
import scala.util.Random
|
||||
|
||||
class OffsetSpec extends WordSpecLike with Matchers {
|
||||
|
||||
"TimeBasedUUID offset" must {
|
||||
|
||||
"be ordered correctly" in {
|
||||
val uuid1 = TimeBasedUUID(UUID.fromString("49225740-2019-11ea-a752-ffae2393b6e4")) //2019-12-16T15:32:36.148Z[UTC]
|
||||
val uuid2 = TimeBasedUUID(UUID.fromString("91be23d0-2019-11ea-a752-ffae2393b6e4")) //2019-12-16T15:34:37.965Z[UTC]
|
||||
val uuid3 = TimeBasedUUID(UUID.fromString("91f95810-2019-11ea-a752-ffae2393b6e4")) //2019-12-16T15:34:38.353Z[UTC]
|
||||
uuid1.value.timestamp() should be < uuid2.value.timestamp()
|
||||
uuid2.value.timestamp() should be < uuid3.value.timestamp()
|
||||
List(uuid2, uuid1, uuid3).sorted shouldEqual List(uuid1, uuid2, uuid3)
|
||||
List(uuid3, uuid2, uuid1).sorted shouldEqual List(uuid1, uuid2, uuid3)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
"Sequence offset" must {
|
||||
|
||||
"be ordered correctly" in {
|
||||
val sequenceBasedList = List(1L, 2L, 3L).map(Sequence)
|
||||
Random.shuffle(sequenceBasedList).sorted shouldEqual sequenceBasedList
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue