cassandra-driver 1.0.0.beta.3 → 1.0.0.rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +13 -5
- data/README.md +8 -6
- data/lib/cassandra.rb +99 -13
- data/lib/cassandra/address_resolution.rb +36 -0
- data/lib/cassandra/address_resolution/policies.rb +2 -0
- data/lib/cassandra/address_resolution/policies/ec2_multi_region.rb +56 -0
- data/lib/cassandra/address_resolution/policies/none.rb +35 -0
- data/lib/cassandra/auth.rb +1 -1
- data/lib/cassandra/auth/providers/password.rb +1 -1
- data/lib/cassandra/client.rb +2 -2
- data/lib/cassandra/client/batch.rb +3 -3
- data/lib/cassandra/client/client.rb +12 -12
- data/lib/cassandra/client/connection_manager.rb +2 -2
- data/lib/cassandra/client/connector.rb +6 -10
- data/lib/cassandra/client/prepared_statement.rb +4 -4
- data/lib/cassandra/client/request_runner.rb +1 -2
- data/lib/cassandra/cluster.rb +15 -5
- data/lib/cassandra/cluster/client.rb +158 -96
- data/lib/cassandra/cluster/connector.rb +42 -27
- data/lib/cassandra/cluster/control_connection.rb +384 -132
- data/lib/cassandra/cluster/options.rb +5 -2
- data/lib/cassandra/cluster/registry.rb +19 -9
- data/lib/cassandra/compression.rb +1 -1
- data/lib/cassandra/compression/compressors/lz4.rb +1 -1
- data/lib/cassandra/compression/compressors/snappy.rb +1 -1
- data/lib/cassandra/driver.rb +28 -20
- data/lib/cassandra/errors.rb +325 -35
- data/lib/cassandra/future.rb +3 -3
- data/lib/cassandra/load_balancing/policies/dc_aware_round_robin.rb +7 -3
- data/lib/cassandra/load_balancing/policies/token_aware.rb +1 -1
- data/lib/cassandra/protocol.rb +0 -16
- data/lib/cassandra/protocol/cql_byte_buffer.rb +18 -18
- data/lib/cassandra/protocol/cql_protocol_handler.rb +74 -8
- data/lib/cassandra/protocol/frame_decoder.rb +2 -2
- data/lib/cassandra/protocol/frame_encoder.rb +1 -1
- data/lib/cassandra/protocol/response.rb +1 -1
- data/lib/cassandra/protocol/responses/detailed_error_response.rb +16 -1
- data/lib/cassandra/protocol/responses/error_response.rb +17 -0
- data/lib/cassandra/protocol/responses/event_response.rb +1 -1
- data/lib/cassandra/protocol/responses/raw_rows_result_response.rb +1 -1
- data/lib/cassandra/protocol/responses/result_response.rb +1 -1
- data/lib/cassandra/protocol/responses/rows_result_response.rb +1 -1
- data/lib/cassandra/protocol/type_converter.rb +4 -3
- data/lib/cassandra/reconnection.rb +1 -1
- data/lib/cassandra/retry.rb +3 -5
- data/lib/cassandra/session.rb +11 -5
- data/lib/cassandra/table.rb +1 -1
- data/lib/cassandra/time_uuid.rb +21 -83
- data/lib/cassandra/util.rb +1 -1
- data/lib/cassandra/uuid.rb +6 -4
- data/lib/cassandra/uuid/generator.rb +207 -0
- data/lib/cassandra/version.rb +1 -1
- metadata +24 -19
@@ -22,7 +22,7 @@ module Cassandra
|
|
22
22
|
def self.decode(protocol_version, buffer, length, trace_id=nil)
|
23
23
|
type = buffer.read_string
|
24
24
|
impl = EVENT_TYPES[type]
|
25
|
-
raise
|
25
|
+
raise Errors::DecodingError, %(Unsupported event type: "#{type}") unless impl
|
26
26
|
new_length = length - 4 - type.bytesize
|
27
27
|
impl.decode(protocol_version, buffer, new_length, trace_id)
|
28
28
|
end
|
@@ -28,7 +28,7 @@ module Cassandra
|
|
28
28
|
def self.decode(protocol_version, buffer, length, trace_id=nil)
|
29
29
|
kind = buffer.read_int
|
30
30
|
impl = RESULT_TYPES[kind]
|
31
|
-
raise
|
31
|
+
raise Errors::DecodingError, %(Unsupported result kind: #{kind}) unless impl
|
32
32
|
impl.decode(protocol_version, buffer, length - 4, trace_id)
|
33
33
|
end
|
34
34
|
|
@@ -46,7 +46,7 @@ module Cassandra
|
|
46
46
|
case type
|
47
47
|
when Array
|
48
48
|
unless value.nil? || value.is_a?(Enumerable)
|
49
|
-
raise
|
49
|
+
raise EncodingError, 'Value for collection must be enumerable'
|
50
50
|
end
|
51
51
|
case type.first
|
52
52
|
when :list, :set
|
@@ -75,12 +75,12 @@ module Cassandra
|
|
75
75
|
nil_to_bytes(buffer, size_bytes)
|
76
76
|
end
|
77
77
|
else
|
78
|
-
raise
|
78
|
+
raise EncodingError, %(Unsupported column collection type: #{type.first})
|
79
79
|
end
|
80
80
|
else
|
81
81
|
converter = @to_bytes_converters[type]
|
82
82
|
unless converter
|
83
|
-
raise
|
83
|
+
raise EncodingError, %(Unsupported column type: #{type})
|
84
84
|
end
|
85
85
|
converter.call(buffer, value, size_bytes)
|
86
86
|
end
|
@@ -140,6 +140,7 @@ module Cassandra
|
|
140
140
|
size = buffer.read_signed_int
|
141
141
|
return nil if size & 0x80000000 == 0x80000000
|
142
142
|
end
|
143
|
+
return nil if size.zero?
|
143
144
|
size
|
144
145
|
end
|
145
146
|
|
@@ -30,7 +30,7 @@ module Cassandra
|
|
30
30
|
|
31
31
|
# A reconnection policy
|
32
32
|
# @abstract Actual reconnection policies supplied as `:reconnection_policy`
|
33
|
-
# option to {Cassandra.
|
33
|
+
# option to {Cassandra.cluster} don't need to inherit this class, only
|
34
34
|
# implement its methods. This class exists for documentation purposes
|
35
35
|
# only.
|
36
36
|
class Policy
|
data/lib/cassandra/retry.rb
CHANGED
@@ -19,7 +19,7 @@
|
|
19
19
|
module Cassandra
|
20
20
|
module Retry
|
21
21
|
# @abstract Actual retry policies supplied as `:retry_policy` option to
|
22
|
-
# {Cassandra.
|
22
|
+
# {Cassandra.cluster} don't need to inherit this class, only implement
|
23
23
|
# its methods. This class exists for documentation purposes only.
|
24
24
|
module Policy
|
25
25
|
# Decides wether to retry a read and at what consistency level.
|
@@ -51,8 +51,7 @@ module Cassandra
|
|
51
51
|
# @param statement [Cassandra::Statement] the original statement that timed out
|
52
52
|
# @param consistency [Symbol] the original consistency level for the
|
53
53
|
# request, one of {Cassandra::CONSISTENCIES}
|
54
|
-
# @param type [Symbol] One of
|
55
|
-
# `:counter` or `:batch_log`
|
54
|
+
# @param type [Symbol] One of {Cassandra::WRITE_TYPES}
|
56
55
|
# @param required [Integer] the number of acks required to achieve
|
57
56
|
# requested consistency level
|
58
57
|
# @param received [Integer] the number of acks received by the time the
|
@@ -75,8 +74,7 @@ module Cassandra
|
|
75
74
|
# request, one of {Cassandra::CONSISTENCIES}
|
76
75
|
# @param required [Integer] the number of replicas required to achieve
|
77
76
|
# requested consistency level
|
78
|
-
# @param alive [Integer] the number of replicas
|
79
|
-
# query timed out
|
77
|
+
# @param alive [Integer] the number of replicas available for the request
|
80
78
|
# @param retries [Integer] the number of retries already performed
|
81
79
|
#
|
82
80
|
# @return [Cassandra::Policies::Retry::Decision] a retry decision
|
data/lib/cassandra/session.rb
CHANGED
@@ -54,14 +54,17 @@ module Cassandra
|
|
54
54
|
# relevant for conditional updates and specifies a serial consistency to
|
55
55
|
# be used, one of {Cassandra::SERIAL_CONSISTENCIES}
|
56
56
|
#
|
57
|
-
# @see Cassandra.
|
58
|
-
#
|
57
|
+
# @see Cassandra.cluster Options that can be specified on the cluster-level
|
58
|
+
# and their default values.
|
59
59
|
#
|
60
60
|
# @note Last argument will be treated as `options` if it is a {Hash}.
|
61
61
|
# Therefore, make sure to pass empty `options` when executing a statement
|
62
62
|
# with the last parameter required to be a map datatype.
|
63
63
|
#
|
64
64
|
# @return [Cassandra::Future<Cassandra::Result>]
|
65
|
+
#
|
66
|
+
# @see Cassandra::Session#execute A list of errors this future can be
|
67
|
+
# resolved with
|
65
68
|
def execute_async(statement, *args)
|
66
69
|
if args.last.is_a?(::Hash)
|
67
70
|
options = @options.override(args.pop)
|
@@ -91,8 +94,11 @@ module Cassandra
|
|
91
94
|
# @see Cassandra::Future#get
|
92
95
|
#
|
93
96
|
# @return [Cassandra::Result] query result
|
94
|
-
# @raise [Cassandra::Errors::NoHostsAvailable] if
|
95
|
-
# @raise [Cassandra::Errors::
|
97
|
+
# @raise [Cassandra::Errors::NoHostsAvailable] if all hosts fail
|
98
|
+
# @raise [Cassandra::Errors::ExecutionError] if Cassandra fails to execute
|
99
|
+
# @raise [Cassandra::Errors::ValidationError] if Cassandra fails to validate
|
100
|
+
# @raise [Cassandra::Errors::TimeoutError] if request has not completed
|
101
|
+
# within the `:timeout` given
|
96
102
|
def execute(*args)
|
97
103
|
execute_async(*args).get
|
98
104
|
end
|
@@ -131,7 +137,7 @@ module Cassandra
|
|
131
137
|
#
|
132
138
|
# @return [Cassandra::Statements::Prepared] prepared statement
|
133
139
|
# @raise [Cassandra::Errors::NoHostsAvailable] if none of the hosts can be reached
|
134
|
-
# @raise [Cassandra::Errors::
|
140
|
+
# @raise [Cassandra::Errors::ExecutionError] if Cassandra returns an error response
|
135
141
|
def prepare(*args)
|
136
142
|
prepare_async(*args).get
|
137
143
|
end
|
data/lib/cassandra/table.rb
CHANGED
@@ -247,7 +247,7 @@ module Cassandra
|
|
247
247
|
# @private
|
248
248
|
def create_partition_key(values)
|
249
249
|
partition_key = @partition_key
|
250
|
-
return nil
|
250
|
+
return nil if partition_key.size > values.size
|
251
251
|
|
252
252
|
if partition_key.one?
|
253
253
|
column = partition_key.first
|
data/lib/cassandra/time_uuid.rb
CHANGED
@@ -19,6 +19,7 @@
|
|
19
19
|
module Cassandra
|
20
20
|
# A variant of UUID which can extract its time component.
|
21
21
|
#
|
22
|
+
# You can use {Cassandra::Uuid::Generator} to generate {Cassandra::TimeUuid}s
|
22
23
|
class TimeUuid < Uuid
|
23
24
|
include Comparable
|
24
25
|
|
@@ -29,18 +30,37 @@ module Cassandra
|
|
29
30
|
t = time_bits - GREGORIAN_OFFSET
|
30
31
|
seconds = t/10_000_000
|
31
32
|
microseconds = (t - seconds * 10_000_000)/10.0
|
33
|
+
|
32
34
|
Time.at(seconds, microseconds).utc
|
33
35
|
end
|
34
36
|
|
37
|
+
# Returns the date component from this UUID as Date.
|
38
|
+
#
|
39
|
+
# This just sugar around {#to_time}
|
40
|
+
#
|
41
|
+
# @return [Date]
|
42
|
+
def to_date
|
43
|
+
to_time.to_date
|
44
|
+
end
|
45
|
+
|
46
|
+
# Compares this timeuuid with another timeuuid
|
47
|
+
#
|
48
|
+
# @param other [Cassandra::TimeUuid] another timeuuid to compare
|
49
|
+
# @see Comparable
|
50
|
+
#
|
51
|
+
# @return [nil] when other is not a {Cassandra::Uuid}
|
52
|
+
# @return [Integer] `-1` when less than `other`, `0` when equal to `other`
|
53
|
+
# and `1` when greater than `other`
|
35
54
|
def <=>(other)
|
36
55
|
return nil unless other.kind_of?(Cassandra::Uuid)
|
37
56
|
c = self.value <=> other.value
|
38
|
-
return c if c == 0
|
57
|
+
return c if c == 0 || !other.is_a?(Cassandra::TimeUuid)
|
39
58
|
self.time_bits <=> other.time_bits
|
40
59
|
end
|
41
60
|
|
42
61
|
protected
|
43
62
|
|
63
|
+
# @private
|
44
64
|
def time_bits
|
45
65
|
n = (value >> 64)
|
46
66
|
t = 0
|
@@ -54,88 +74,6 @@ module Cassandra
|
|
54
74
|
|
55
75
|
# @private
|
56
76
|
LOWER_HALF_MASK = 0xffffffff_ffffffff
|
57
|
-
|
58
|
-
public
|
59
|
-
|
60
|
-
# A UUID version 1, variant 1 generator. It can generate a sequence of UUIDs
|
61
|
-
# with reasonable uniqueness guarantees:
|
62
|
-
#
|
63
|
-
# * The clock ID and node ID components are set to random numbers when the
|
64
|
-
# generator is created.
|
65
|
-
# * If two calls to {#next} happen within the time afforded by the system
|
66
|
-
# clock resolution a counter is incremented and added to the time
|
67
|
-
# component.
|
68
|
-
# * If the clock moves backwards the clock ID is reset to a new random
|
69
|
-
# number.
|
70
|
-
#
|
71
|
-
# Instances of this class are absolutely not threadsafe. You should
|
72
|
-
# never share instances between threads.
|
73
|
-
#
|
74
|
-
class Generator
|
75
|
-
# Create a new UUID generator.
|
76
|
-
#
|
77
|
-
# @param [Integer] node_id an alternate node ID (defaults to a random number)
|
78
|
-
# @param [Integer] clock_id an alternate clock ID (defaults to a random number)
|
79
|
-
#
|
80
|
-
def initialize(node_id=nil, clock_id=nil, clock=Time)
|
81
|
-
@node_id = node_id || (rand(2**47) | 0x010000000000)
|
82
|
-
@clock_id = clock_id || rand(2**16)
|
83
|
-
@clock = clock
|
84
|
-
end
|
85
|
-
|
86
|
-
# Returns a new UUID with a time component that is the current time.
|
87
|
-
#
|
88
|
-
# @return [Cassandra::TimeUuid] a new UUID
|
89
|
-
#
|
90
|
-
def next
|
91
|
-
now = @clock.now
|
92
|
-
usecs = now.to_i * 1_000_000 + now.usec
|
93
|
-
if @last_usecs && @last_usecs - @sequence <= usecs && usecs <= @last_usecs
|
94
|
-
@sequence += 1
|
95
|
-
elsif @last_usecs && @last_usecs > usecs
|
96
|
-
@sequence = 0
|
97
|
-
@clock_id = rand(2**16)
|
98
|
-
else
|
99
|
-
@sequence = 0
|
100
|
-
end
|
101
|
-
@last_usecs = usecs + @sequence
|
102
|
-
from_usecs(@last_usecs)
|
103
|
-
end
|
104
|
-
|
105
|
-
# Returns a new UUID with a time component based on the specified Time.
|
106
|
-
# A piece of jitter is added to ensure that multiple calls with the same
|
107
|
-
# time do not generate the same UUID (if you want determinism you can set
|
108
|
-
# the second parameter to zero).
|
109
|
-
#
|
110
|
-
# @param [Time] time the time to encode into the UUID
|
111
|
-
# @param [Integer] jitter a number of microseconds to add to the time to make it unique
|
112
|
-
# @return [Cassandra::TimeUuid] a new UUID
|
113
|
-
#
|
114
|
-
def from_time(time, jitter=rand(2**16))
|
115
|
-
usecs = time.to_i * 1_000_000 + time.usec + jitter
|
116
|
-
from_usecs(usecs)
|
117
|
-
end
|
118
|
-
|
119
|
-
# @private
|
120
|
-
def from_usecs(usecs)
|
121
|
-
t = GREGORIAN_OFFSET + usecs * 10
|
122
|
-
time_hi = t & 0x0fff000000000000
|
123
|
-
time_mid = t & 0x0000ffff00000000
|
124
|
-
time_low = t & 0x00000000ffffffff
|
125
|
-
version = 1
|
126
|
-
clock_id = @clock_id & 0x3fff
|
127
|
-
node_id = @node_id & 0xffffffffffff
|
128
|
-
variant = 0x8000
|
129
|
-
n = (time_low << 96) | (time_mid << 48) | (time_hi << 16)
|
130
|
-
n |= version << 76
|
131
|
-
n |= (clock_id | variant) << 48
|
132
|
-
n |= node_id
|
133
|
-
TimeUuid.new(n)
|
134
|
-
end
|
135
|
-
end
|
136
|
-
|
137
|
-
private
|
138
|
-
|
139
77
|
# @private
|
140
78
|
GREGORIAN_OFFSET = 122192928000000000
|
141
79
|
end
|
data/lib/cassandra/util.rb
CHANGED
data/lib/cassandra/uuid.rb
CHANGED
@@ -22,7 +22,7 @@ module Cassandra
|
|
22
22
|
# This is a very basic implementation of UUIDs and exists more or less just
|
23
23
|
# to encode and decode UUIDs from and to Cassandra.
|
24
24
|
#
|
25
|
-
# If you want to generate UUIDs see {Cassandra::
|
25
|
+
# If you want to generate UUIDs see {Cassandra::Uuid::Generator}.
|
26
26
|
#
|
27
27
|
class Uuid
|
28
28
|
# Creates a new UUID either from a string (expected to be on the standard 8-4-4-4-12 form, or just 32 characters without hyphens), or from a 128 bit number.
|
@@ -88,17 +88,19 @@ module Cassandra
|
|
88
88
|
# @private
|
89
89
|
def from_s(str)
|
90
90
|
str = str.gsub(HYPHEN, EMPTY_STRING)
|
91
|
-
raise ArgumentError, "Expected 32 hexadecimal digits but got #{str.length}" unless str.length == 32
|
92
|
-
raise ArgumentError, "invalid value for Integer(): \"#{str}\"" unless str =~ HEX_RE
|
91
|
+
raise ::ArgumentError, "Expected 32 hexadecimal digits but got #{str.length}" unless str.length == 32
|
92
|
+
raise ::ArgumentError, "invalid value for Integer(): \"#{str}\"" unless str =~ HEX_RE
|
93
93
|
Integer(str, 16)
|
94
94
|
end
|
95
95
|
else
|
96
96
|
# @private
|
97
97
|
def from_s(str)
|
98
98
|
str = str.gsub(HYPHEN, EMPTY_STRING)
|
99
|
-
raise ArgumentError, "Expected 32 hexadecimal digits but got #{str.length}" unless str.length == 32
|
99
|
+
raise ::ArgumentError, "Expected 32 hexadecimal digits but got #{str.length}" unless str.length == 32
|
100
100
|
Integer(str, 16)
|
101
101
|
end
|
102
102
|
end
|
103
103
|
end
|
104
104
|
end
|
105
|
+
|
106
|
+
require 'cassandra/uuid/generator'
|
@@ -0,0 +1,207 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
#--
|
4
|
+
# Copyright 2013-2014 DataStax, Inc.
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#++
|
18
|
+
|
19
|
+
module Cassandra
|
20
|
+
class Uuid
|
21
|
+
# A UUID generator.
|
22
|
+
#
|
23
|
+
# This class can be used to genereate Apache Cassandra timeuuid and uuid
|
24
|
+
# values.
|
25
|
+
#
|
26
|
+
# @see Cassandra::Uuid::Generator#now Generating a sequence time UUIDs
|
27
|
+
# with reasonable uniqueness guarantees.
|
28
|
+
# @see Cassandra::Uuid::Generator#at Generating a time UUID for a given
|
29
|
+
# time object or unix timestamp.
|
30
|
+
# @see Cassandra::Uuid::Generator#uuid Generating completely random v4
|
31
|
+
# UUIDs.
|
32
|
+
#
|
33
|
+
# @note Instances of this class are absolutely not threadsafe. You should
|
34
|
+
# never share instances between threads.
|
35
|
+
#
|
36
|
+
class Generator
|
37
|
+
# Create a new UUID generator.
|
38
|
+
#
|
39
|
+
# The clock ID and node ID components are set to random numbers when the
|
40
|
+
# generator is created. These are used for generation of time UUIDs only.
|
41
|
+
#
|
42
|
+
# @param [Integer] node_id an alternate node ID
|
43
|
+
# @param [Integer] clock_id an alternate clock ID
|
44
|
+
# @param [Object<#now>] clock used to generate timeuuid from current time
|
45
|
+
#
|
46
|
+
# @raise [ArgumentError] if clock doesn't respond to `now`
|
47
|
+
def initialize(node_id = (SecureRandom.random_number(2**47) | 0x010000000000), clock_id = SecureRandom.random_number(65536), clock = Time)
|
48
|
+
raise ::ArgumentError, "invalid clock" unless clock.respond_to?(:now)
|
49
|
+
|
50
|
+
@node_id = Integer(node_id)
|
51
|
+
@clock_id = Integer(clock_id)
|
52
|
+
@clock = clock
|
53
|
+
end
|
54
|
+
|
55
|
+
# Returns a new UUID with a time component that is the current time.
|
56
|
+
#
|
57
|
+
# If two calls to {#now} happen within the time afforded by the system
|
58
|
+
# clock resolution a counter is incremented and added to the time
|
59
|
+
# component.
|
60
|
+
#
|
61
|
+
# If the clock moves backwards the clock ID is reset to a new random
|
62
|
+
# number.
|
63
|
+
#
|
64
|
+
# @example Creating a sequential TimeUuids for the current time
|
65
|
+
# generator = Cassandra::Uuid::Generator.new
|
66
|
+
# timeuuids = 5.times.map { generator.now }
|
67
|
+
#
|
68
|
+
# puts timeuuids.zip(timeuuids.map(&:to_time)).map(&:inspect)
|
69
|
+
#
|
70
|
+
# # Outputs:
|
71
|
+
# # [8614b7d0-5646-11e4-8e54-6761d3995ef3, 2014-10-17 21:42:42 UTC]
|
72
|
+
# # [8614b91a-5646-11e4-8e54-6761d3995ef3, 2014-10-17 21:42:42 UTC]
|
73
|
+
# # [8614b960-5646-11e4-8e54-6761d3995ef3, 2014-10-17 21:42:42 UTC]
|
74
|
+
# # [8614b99c-5646-11e4-8e54-6761d3995ef3, 2014-10-17 21:42:42 UTC]
|
75
|
+
# # [8614b9ce-5646-11e4-8e54-6761d3995ef3, 2014-10-17 21:42:42 UTC]
|
76
|
+
#
|
77
|
+
# @see Time.now
|
78
|
+
#
|
79
|
+
# @return [Cassandra::TimeUuid] a new UUID
|
80
|
+
def now
|
81
|
+
now = @clock.now
|
82
|
+
usecs = now.to_i * 1_000_000 + now.usec
|
83
|
+
if @last_usecs && @last_usecs - @sequence <= usecs && usecs <= @last_usecs
|
84
|
+
@sequence += 1
|
85
|
+
elsif @last_usecs && @last_usecs > usecs
|
86
|
+
@sequence = 0
|
87
|
+
@clock_id = SecureRandom.random_number(65536)
|
88
|
+
else
|
89
|
+
@sequence = 0
|
90
|
+
end
|
91
|
+
@last_usecs = usecs + @sequence
|
92
|
+
from_usecs(@last_usecs)
|
93
|
+
end
|
94
|
+
|
95
|
+
# Returns a new UUID with a time component based on the specified Time.
|
96
|
+
# A piece of jitter is added to ensure that multiple calls with the same
|
97
|
+
# time do not generate the same UUID (if you want determinism you can set
|
98
|
+
# the second parameter to zero).
|
99
|
+
#
|
100
|
+
# @overload at(time, jitter = SecureRandom.random_number(65536))
|
101
|
+
# @param [Time] time a Time instance
|
102
|
+
# @param [Integer] jitter a number of microseconds to add to the time
|
103
|
+
# @return [Cassandra::TimeUuid] a new UUID
|
104
|
+
# @overload at(seconds_with_frac, jitter = SecureRandom.random_number(65536))
|
105
|
+
# @param [Numeric] seconds_with_frac can be {Integer}, {Float},
|
106
|
+
# {Rational}, or other {Numeric}
|
107
|
+
# @param [Integer] jitter a number of microseconds to add to the time
|
108
|
+
# @return [Cassandra::TimeUuid] a new UUID
|
109
|
+
# @overload at(seconds, microseconds_with_frac, jitter = SecureRandom.random_number(65536))
|
110
|
+
# @param [Integer] seconds
|
111
|
+
# @param [Numeric] microseconds_with_frac can be {Integer}, {Float},
|
112
|
+
# {Rational}, or other {Numeric}
|
113
|
+
# @param [Integer] jitter a number of microseconds to add to the time
|
114
|
+
# @return [Cassandra::TimeUuid] a new UUID
|
115
|
+
#
|
116
|
+
# @note the `jitter` argument accepted by all variants of this method is
|
117
|
+
# required to add randomness to generated {Cassandra::TimeUuid} and
|
118
|
+
# might affect the order of generated timestamps. You should set
|
119
|
+
# `jitter` to 0 when the source time(stamp)s are unique.
|
120
|
+
#
|
121
|
+
# @example Creating a TimeUuid from a Time instance
|
122
|
+
# generator = Cassandra::Uuid::Generator.new
|
123
|
+
# timeuuid = generator.at(Time.at(1413582460))
|
124
|
+
#
|
125
|
+
# puts timeuuid.to_time
|
126
|
+
#
|
127
|
+
# # Outputs:
|
128
|
+
# # 2014-10-17 21:47:40 UTC
|
129
|
+
#
|
130
|
+
# @example Creating a TimeUuid from a timestamp
|
131
|
+
# generator = Cassandra::Uuid::Generator.new
|
132
|
+
# timeuuid = generator.at(1413582423)
|
133
|
+
#
|
134
|
+
# puts timeuuid.to_time
|
135
|
+
#
|
136
|
+
# # Outputs:
|
137
|
+
# # 2014-10-17 21:47:03 UTC
|
138
|
+
#
|
139
|
+
# @example Avoid jitter in generated TimeUuid
|
140
|
+
# timestamp = 1413582418
|
141
|
+
# generator = Cassandra::Uuid::Generator.new
|
142
|
+
# timeuuid = generator.at(timestamp, 0)
|
143
|
+
#
|
144
|
+
# puts timeuuid.to_time.to_i
|
145
|
+
#
|
146
|
+
# # Outputs:
|
147
|
+
# # 1413582418
|
148
|
+
#
|
149
|
+
# @raise [ArgumentError] when given no arguments or more than 3 arguments
|
150
|
+
#
|
151
|
+
# @see Time.at
|
152
|
+
def at(*args)
|
153
|
+
raise ::ArgumentError, "not enough arguments" if args.empty?
|
154
|
+
raise ::ArgumentError, "too many arguments" if args.size > 3
|
155
|
+
|
156
|
+
if args.first.is_a?(Time)
|
157
|
+
time = args.shift
|
158
|
+
jitter = args.empty? ? SecureRandom.random_number(65536) : Integer(args.shift)
|
159
|
+
else
|
160
|
+
jitter = args.size > 2 ? Integer(args.pop) : SecureRandom.random_number(65536)
|
161
|
+
time = Time.at(*args)
|
162
|
+
end
|
163
|
+
|
164
|
+
from_usecs(time.to_i * 1_000_000 + time.usec + jitter)
|
165
|
+
end
|
166
|
+
|
167
|
+
# Returns a completely random version 4 UUID.
|
168
|
+
#
|
169
|
+
# @example Generating a random Uuid
|
170
|
+
# generator = Cassandra::Uuid::Generator.new
|
171
|
+
# uuid = generator.uuid
|
172
|
+
#
|
173
|
+
# puts uuid
|
174
|
+
#
|
175
|
+
# # Outputs:
|
176
|
+
# # 664dedae-e162-4bc0-9066-b9f1968252aa
|
177
|
+
#
|
178
|
+
# @see SecureRandom.uuid
|
179
|
+
#
|
180
|
+
# @return [Cassandra::Uuid] a new UUID
|
181
|
+
def uuid
|
182
|
+
Uuid.new(SecureRandom.uuid)
|
183
|
+
end
|
184
|
+
|
185
|
+
private
|
186
|
+
|
187
|
+
# @private
|
188
|
+
def from_usecs(usecs)
|
189
|
+
t = TimeUuid::GREGORIAN_OFFSET + usecs * 10
|
190
|
+
time_hi = t & 0x0fff000000000000
|
191
|
+
time_mid = t & 0x0000ffff00000000
|
192
|
+
time_low = t & 0x00000000ffffffff
|
193
|
+
version = 1
|
194
|
+
clock_id = @clock_id & 0x3fff
|
195
|
+
node_id = @node_id & 0xffffffffffff
|
196
|
+
variant = 0x8000
|
197
|
+
|
198
|
+
n = (time_low << 96) | (time_mid << 48) | (time_hi << 16)
|
199
|
+
n |= version << 76
|
200
|
+
n |= (clock_id | variant) << 48
|
201
|
+
n |= node_id
|
202
|
+
|
203
|
+
TimeUuid.new(n)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|