[
https://issues.apache.org/jira/browse/CASSANDRA-15064?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=16805069#comment-16805069
]
Jon Meredith commented on CASSANDRA-15064:
------------------------------------------
Thanks for the additional information. I'm not familiar with the Go client so
thanks for the link. The Go client takes a different approach to UUID
generation than the server-side now() function, which as I mentioned above,
generates the clockseq once at startup time - which limits now() to generating
a single UUID per 100ns (which is 10 million / second).
I agree with you you'll get a wrapping problem with your UUID solution. It
just takes the lower 14 bits from the 32-bit clockseq counter which obviously
wraps.
What do you think about these two possibilities
# Insert using CQL now() - probably easiest
# Write an alternate UUIDFromTime
*Insert Using CQL now()*
{noformat}
INSERT INTO timeline(user_id, image_timestamp, image_id) VALUES(?, now(), ?)
{noformat}
That will generate timestamps that always sort as you expect, however you'll
need an average insertion rate less than 10,000,000 / second / coordinator and
move the cost of UUID generation from client to server which should be small.
*Write an alternate UUIDFromTime to match server-side now()*
It should be straightforward to provide an alternative UUIDFromTime with the
same semantics as CQL now().
Initialize clockseq once at startup
Keep an atomic 64-bit, lastNanos, to store the 60-bit UUID timestamp
Atomically update to be monontonically increasing.
Something like this (apologies for the pigeon Go, I don't normally write it and
have not tested this fragment)
{noformat}
var currentNanos = atomic.LoadInt64(&lastNanos)
// compute nextNanos the same way as
https://github.com/gocql/gocql/blame/master/uuid.go#L126
var nextNanos = int64(utcTime.Unix()-timeBase)*10000000 +
int64(utcTime.Nanosecond()/100)
if (thisNanos <= currentNanos) {
thisNanos = currentNanos + 1; // If the clock is drifting backwards, just
pick the monontonic next timestamp
}
if(!atomic.CompareAndSwapUInt64(&lastNanos, currentNanos, nextNanos)) {
// If unable to swap, another thread has generated a new UUID, so the
time should be close
// to when this thread would have, just take the next timestamp rather
than loop forever
// in case of high contention.
thisNanos = atomic.AddInt64(&lastNanos, 1)
}
{noformat}
What do you think?
As for this JIRA, what I'd propose is that I'll submit documentation
improvements so that users don't rely on TimeUUID clockseq sort order, and file
an issue under the GoCql client driver in case they missed this JIRA.
> Wrong ordering for timeuuid fields
> ----------------------------------
>
> Key: CASSANDRA-15064
> URL: https://issues.apache.org/jira/browse/CASSANDRA-15064
> Project: Cassandra
> Issue Type: Bug
> Components: Cluster/Schema
> Reporter: Andreas Andersen
> Assignee: Jon Meredith
> Priority: Normal
> Attachments: example.cql
>
>
> Hi!
> We're seeing some strange behavior for the ordering of timeuuid fields. They
> seem to be sorted in the wrong order when the clock_seq_low field in a
> timeuuid goes from 7f to 80. Consider the following example:
> {noformat}
> cqlsh:test> show version;
> [cqlsh 5.0.1 | Cassandra 3.11.4 | CQL spec 3.4.4 | Native protocol v4]
> cqlsh:test> CREATE TABLE t (
> ... partition int,
> ... t timeuuid,
> ... i int,
> ...
> ... PRIMARY KEY(partition, t)
> ... )
> ... WITH CLUSTERING ORDER BY(t ASC);
> cqlsh:test> INSERT INTO t(partition, t, i) VALUES(1,
> 84e2c963-4ef9-11e9-b57e-f0def1d0755e, 1);
> cqlsh:test> INSERT INTO t(partition, t, i) VALUES(1,
> 84e2c963-4ef9-11e9-b57f-f0def1d0755e, 2);
> cqlsh:test> INSERT INTO t(partition, t, i) VALUES(1,
> 84e2c963-4ef9-11e9-b580-f0def1d0755e, 3);
> cqlsh:test> INSERT INTO t(partition, t, i) VALUES(1,
> 84e2c963-4ef9-11e9-b581-f0def1d0755e, 4);
> cqlsh:test> INSERT INTO t(partition, t, i) VALUES(1,
> 84e2c963-4ef9-11e9-b582-f0def1d0755e, 5);
> cqlsh:test> SELECT * FROM t WHERE partition = 1 ORDER BY t ASC;
>
> partition | t | i
> -----------+--------------------------------------+---
> 1 | 84e2c963-4ef9-11e9-b580-f0def1d0755e | 3
> 1 | 84e2c963-4ef9-11e9-b581-f0def1d0755e | 4
> 1 | 84e2c963-4ef9-11e9-b582-f0def1d0755e | 5
> 1 | 84e2c963-4ef9-11e9-b57e-f0def1d0755e | 1
> 1 | 84e2c963-4ef9-11e9-b57f-f0def1d0755e | 2
>
> (5 rows)
> cqlsh:test>
> {noformat}
> The expected behavior is that the rows are returned in the same order as they
> were inserted (we inserted them with their clustering key in an ascending
> order). Instead, the order "wraps" in the middle.
> This issue only arises when the 9th octet (clock_seq_low) in the uuid goes
> from 7f to 80. A guess would be that the comparison is implemented as a
> signed integer instead of an unsigned integer, as 0x7f = 127 and 0x80 = -128.
> According to the RFC, the field should be treated as an unsigned integer:
> [https://tools.ietf.org/html/rfc4122#section-4.1.2]
> Changing the field from a timeuuid to a uuid gives the expected correct
> behavior:
> {noformat}
> cqlsh:test> CREATE TABLE t (
> ... partition int,
> ... t uuid,
> ... i int,
> ...
> ... PRIMARY KEY(partition, t)
> ... )
> ... WITH CLUSTERING ORDER BY(t ASC);
> cqlsh:test> INSERT INTO t(partition, t, i) VALUES(1,
> 84e2c963-4ef9-11e9-b57e-f0def1d0755e, 1);
> cqlsh:test> INSERT INTO t(partition, t, i) VALUES(1,
> 84e2c963-4ef9-11e9-b57f-f0def1d0755e, 2);
> cqlsh:test> INSERT INTO t(partition, t, i) VALUES(1,
> 84e2c963-4ef9-11e9-b580-f0def1d0755e, 3);
> cqlsh:test> INSERT INTO t(partition, t, i) VALUES(1,
> 84e2c963-4ef9-11e9-b581-f0def1d0755e, 4);
> cqlsh:test> INSERT INTO t(partition, t, i) VALUES(1,
> 84e2c963-4ef9-11e9-b582-f0def1d0755e, 5);
> cqlsh:test> SELECT * FROM t WHERE partition = 1 ORDER BY t ASC;
>
> partition | t | i
> -----------+--------------------------------------+---
> 1 | 84e2c963-4ef9-11e9-b57e-f0def1d0755e | 1
> 1 | 84e2c963-4ef9-11e9-b57f-f0def1d0755e | 2
> 1 | 84e2c963-4ef9-11e9-b580-f0def1d0755e | 3
> 1 | 84e2c963-4ef9-11e9-b581-f0def1d0755e | 4
> 1 | 84e2c963-4ef9-11e9-b582-f0def1d0755e | 5
>
> (5 rows)
> cqlsh:test>{noformat}
>
>
--
This message was sent by Atlassian JIRA
(v7.6.3#76005)
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]