cql-rb 2.0.0.pre2 → 2.0.0.rc0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +14 -6
- data/lib/cql.rb +7 -1
- data/lib/cql/{client/authenticators.rb → auth.rb} +4 -43
- data/lib/cql/auth/plain_text_auth.rb +46 -0
- data/lib/cql/client.rb +0 -6
- data/lib/cql/client/batch.rb +11 -0
- data/lib/cql/client/client.rb +47 -24
- data/lib/cql/client/column_metadata.rb +1 -1
- data/lib/cql/client/keyspace_changer.rb +1 -1
- data/lib/cql/client/peer_discovery.rb +1 -0
- data/lib/cql/client/prepared_statement.rb +83 -4
- data/lib/cql/client/query_result.rb +39 -1
- data/lib/cql/client/result_metadata.rb +3 -2
- data/lib/cql/client/void_result.rb +20 -0
- data/lib/cql/version.rb +1 -1
- data/spec/cql/{client/authenticators_spec.rb → auth/plain_text_auth_spec.rb} +1 -1
- data/spec/cql/client/client_spec.rb +16 -2
- data/spec/cql/client/prepared_statement_spec.rb +0 -1
- data/spec/cql/client/query_result_spec.rb +39 -3
- data/spec/cql/client/void_result_spec.rb +12 -0
- data/spec/integration/client_spec.rb +15 -3
- metadata +6 -8
- data/lib/cql/client/query_trace.rb +0 -46
- data/spec/cql/client/query_trace_spec.rb +0 -138
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b5d95e28951fcb350531d0899c6a4b12938677c9
|
4
|
+
data.tar.gz: 271b515465def1838737f6d92983958f47541f09
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 89b8043dc895260cb8baee75a43652d7150574141863a876a663fb2427672b36ba970f3837813800edd4f106e5fc56d053d75dca2c26889ab0a25b110846a660
|
7
|
+
data.tar.gz: 2d910f7d38e3bb0851c6a43b1146d01f84b6c7b5c5d33682bd587dd2b0f4eb2127e5fc26798a491a4b03730fc8a16fa756bbe19445f2ddfb1b3910b8487dc51b
|
data/README.md
CHANGED
@@ -143,6 +143,8 @@ A prepared statement can be run many times, but the CQL parsing will only be don
|
|
143
143
|
|
144
144
|
Statements are prepared on all connections and each call to `#execute` selects a random connection to run the query on.
|
145
145
|
|
146
|
+
You should only create a prepared statement for a query once, and then reuse the prepared statement object. Preparing the same CQL over and over again is bad for performance since each preparation requires a roundtrip to _all_ connected Cassandra nodes.
|
147
|
+
|
146
148
|
## Batching
|
147
149
|
|
148
150
|
If you're using Cassandra 2.0 or later you can build batch requests, either from regular queries or from prepared statements. Batches can consist of `INSERT`, `UPDATE` and `DELETE` statements.
|
@@ -210,6 +212,16 @@ batch.execute(consistency: :quorum)
|
|
210
212
|
|
211
213
|
As you can see you can specify the options either when creating the batch or when sending it (when using the variant where you call `#execute` yourself). The options given to `#execute` take precedence. You can omit the batch type and specify the options as the only parameter when you want to use the the default batch type.
|
212
214
|
|
215
|
+
If you want to execute the same prepared statement multiple times in a batch there is a special variant of the batching feature available from `PreparedStatement`:
|
216
|
+
|
217
|
+
```ruby
|
218
|
+
# the same counter_statement as in the example above
|
219
|
+
counter_statement.batch do |batch|
|
220
|
+
batch.add(3, 'some_counter')
|
221
|
+
batch.add(2, 'another_counter')
|
222
|
+
end
|
223
|
+
```
|
224
|
+
|
213
225
|
Cassandra 1.2 also supported batching, but only as a CQL feature, you had to build the batch as a string, and it didn't really play well with prepared statements.
|
214
226
|
|
215
227
|
## Paging
|
@@ -219,15 +231,11 @@ If you're using Cassandra 2.0 or later you can page your query results by adding
|
|
219
231
|
```ruby
|
220
232
|
result_page = client.execute("SELECT * FROM large_table WHERE id = 'partition_with_lots_of_data'", page_size: 100)
|
221
233
|
|
222
|
-
|
234
|
+
while result_page
|
223
235
|
result_page.each do |row|
|
224
236
|
p row
|
225
237
|
end
|
226
|
-
|
227
|
-
break
|
228
|
-
else
|
229
|
-
result_page = result_page.next_page
|
230
|
-
end
|
238
|
+
result_page = result_page.next_page
|
231
239
|
end
|
232
240
|
```
|
233
241
|
|
data/lib/cql.rb
CHANGED
@@ -5,15 +5,21 @@ require 'ione'
|
|
5
5
|
|
6
6
|
module Cql
|
7
7
|
CqlError = Class.new(StandardError)
|
8
|
+
IoError = Ione::IoError
|
8
9
|
|
10
|
+
# @private
|
9
11
|
Promise = Ione::Promise
|
12
|
+
|
13
|
+
# @private
|
10
14
|
Future = Ione::Future
|
15
|
+
|
16
|
+
# @private
|
11
17
|
Io = Ione::Io
|
12
|
-
IoError = Ione::IoError
|
13
18
|
end
|
14
19
|
|
15
20
|
require 'cql/uuid'
|
16
21
|
require 'cql/time_uuid'
|
17
22
|
require 'cql/compression'
|
18
23
|
require 'cql/protocol'
|
24
|
+
require 'cql/auth'
|
19
25
|
require 'cql/client'
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
3
|
module Cql
|
4
|
-
module
|
4
|
+
module Auth
|
5
5
|
# An auth provider is a factory for {Cql::Client::Authenticator} instances
|
6
6
|
# (or objects matching that interface). Its {#create_authenticator} will be
|
7
7
|
# called once for each connection that requires authentication.
|
@@ -71,46 +71,7 @@ module Cql
|
|
71
71
|
# @param token [String] a token sent by the server
|
72
72
|
# @return [nil]
|
73
73
|
end
|
74
|
-
|
75
|
-
# Auth provider used for Cassandra's built in authentication.
|
76
|
-
#
|
77
|
-
# There is no need to create instances of this class to pass as `:auth_provider`
|
78
|
-
# to {Cql::Client.connect}, instead use the `:credentials` option and one
|
79
|
-
# will be created automatically for you.
|
80
|
-
class PlainTextAuthProvider
|
81
|
-
def initialize(username, password)
|
82
|
-
@username = username
|
83
|
-
@password = password
|
84
|
-
end
|
85
|
-
|
86
|
-
def create_authenticator(authentication_class)
|
87
|
-
if authentication_class == PASSWORD_AUTHENTICATOR_FQCN
|
88
|
-
PlainTextAuthenticator.new(@username, @password)
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
private
|
93
|
-
|
94
|
-
PASSWORD_AUTHENTICATOR_FQCN = 'org.apache.cassandra.auth.PasswordAuthenticator'.freeze
|
95
|
-
end
|
96
|
-
|
97
|
-
# Authenticator used for Cassandra's built in authentication,
|
98
|
-
# see {Cql::Client::PlainTextAuthProvider}
|
99
|
-
class PlainTextAuthenticator
|
100
|
-
def initialize(username, password)
|
101
|
-
@username = username
|
102
|
-
@password = password
|
103
|
-
end
|
104
|
-
|
105
|
-
def initial_response
|
106
|
-
"\x00#{@username}\x00#{@password}"
|
107
|
-
end
|
108
|
-
|
109
|
-
def challenge_response(token)
|
110
|
-
end
|
111
|
-
|
112
|
-
def authentication_successful(token)
|
113
|
-
end
|
114
|
-
end
|
115
74
|
end
|
116
|
-
end
|
75
|
+
end
|
76
|
+
|
77
|
+
require 'cql/auth/plain_text_auth'
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Cql
|
4
|
+
module Auth
|
5
|
+
# Auth provider used for Cassandra's built in authentication.
|
6
|
+
#
|
7
|
+
# There is no need to create instances of this class to pass as `:auth_provider`
|
8
|
+
# to {Cql::Client.connect}, instead use the `:credentials` option and one
|
9
|
+
# will be created automatically for you.
|
10
|
+
class PlainTextAuthProvider
|
11
|
+
def initialize(username, password)
|
12
|
+
@username = username
|
13
|
+
@password = password
|
14
|
+
end
|
15
|
+
|
16
|
+
def create_authenticator(authentication_class)
|
17
|
+
if authentication_class == PASSWORD_AUTHENTICATOR_FQCN
|
18
|
+
PlainTextAuthenticator.new(@username, @password)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
PASSWORD_AUTHENTICATOR_FQCN = 'org.apache.cassandra.auth.PasswordAuthenticator'.freeze
|
25
|
+
end
|
26
|
+
|
27
|
+
# Authenticator used for Cassandra's built in authentication,
|
28
|
+
# see {Cql::Auth::PlainTextAuthProvider}
|
29
|
+
class PlainTextAuthenticator
|
30
|
+
def initialize(username, password)
|
31
|
+
@username = username
|
32
|
+
@password = password
|
33
|
+
end
|
34
|
+
|
35
|
+
def initial_response
|
36
|
+
"\x00#{@username}\x00#{@password}"
|
37
|
+
end
|
38
|
+
|
39
|
+
def challenge_response(token)
|
40
|
+
end
|
41
|
+
|
42
|
+
def authentication_successful(token)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
data/lib/cql/client.rb
CHANGED
@@ -20,7 +20,6 @@ module Cql
|
|
20
20
|
TimeoutError = Class.new(CqlError)
|
21
21
|
ClientError = Class.new(CqlError)
|
22
22
|
AuthenticationError = Class.new(ClientError)
|
23
|
-
IncompleteTraceError = Class.new(ClientError)
|
24
23
|
UnsupportedProtocolVersionError = Class.new(ClientError)
|
25
24
|
NotPreparedError = Class.new(ClientError)
|
26
25
|
|
@@ -92,9 +91,6 @@ module Cql
|
|
92
91
|
# @param [Hash] options
|
93
92
|
# @option options [Array<String>] :hosts (['localhost']) One or more
|
94
93
|
# hostnames used as seed nodes when connecting. Duplicates will be removed.
|
95
|
-
# @option options [String] :host ('localhost') A comma separated list of
|
96
|
-
# hostnames to use as seed nodes. This is a backwards-compatible version
|
97
|
-
# of the :hosts option, and is deprecated.
|
98
94
|
# @option options [String] :port (9042) The port to connect to, this port
|
99
95
|
# will be used for all nodes. Because the `system.peers` table does not
|
100
96
|
# contain the port that the nodes are listening on, the port must be the
|
@@ -143,7 +139,6 @@ require 'cql/client/connector'
|
|
143
139
|
require 'cql/client/null_logger'
|
144
140
|
require 'cql/client/column_metadata'
|
145
141
|
require 'cql/client/result_metadata'
|
146
|
-
require 'cql/client/query_trace'
|
147
142
|
require 'cql/client/execute_options_decoder'
|
148
143
|
require 'cql/client/keyspace_changer'
|
149
144
|
require 'cql/client/client'
|
@@ -152,5 +147,4 @@ require 'cql/client/batch'
|
|
152
147
|
require 'cql/client/query_result'
|
153
148
|
require 'cql/client/void_result'
|
154
149
|
require 'cql/client/request_runner'
|
155
|
-
require 'cql/client/authenticators'
|
156
150
|
require 'cql/client/peer_discovery'
|
data/lib/cql/client/batch.rb
CHANGED
@@ -2,6 +2,13 @@
|
|
2
2
|
|
3
3
|
module Cql
|
4
4
|
module Client
|
5
|
+
# Batches let you send multiple queries (`INSERT`, `UPDATE` and `DELETE`) in
|
6
|
+
# one go. This can lead to better performance, and depending on the options
|
7
|
+
# you specify can also give you different consistency guarantees.
|
8
|
+
#
|
9
|
+
# Batches can contain a mix of different queries and prepared statements.
|
10
|
+
#
|
11
|
+
# @see Cql::Client::Client#batch
|
5
12
|
class Batch
|
6
13
|
# @!method add(cql_or_prepared_statement, *bound_values)
|
7
14
|
#
|
@@ -37,6 +44,10 @@ module Cql
|
|
37
44
|
# @return [Cql::Client::VoidResult] a batch always returns a void result
|
38
45
|
end
|
39
46
|
|
47
|
+
# A convenient wrapper that makes it easy to build batches of multiple
|
48
|
+
# executions of the same prepared statement.
|
49
|
+
#
|
50
|
+
# @see Cql::Client::PreparedStatement#batch
|
40
51
|
class PreparedStatementBatch
|
41
52
|
# @!method add(*bound_values)
|
42
53
|
#
|
data/lib/cql/client/client.rb
CHANGED
@@ -2,18 +2,29 @@
|
|
2
2
|
|
3
3
|
module Cql
|
4
4
|
module Client
|
5
|
+
# A CQL client manages connections to one or more Cassandra nodes and you use
|
6
|
+
# it run queries, insert and update data, prepare statements and switch
|
7
|
+
# keyspaces.
|
8
|
+
#
|
9
|
+
# To get a reference to a client you call {Cql::Client.connect}. When you
|
10
|
+
# don't need the client anymore you can call {#close} to close all connections.
|
11
|
+
#
|
12
|
+
# Internally the client runs an IO reactor in a background thread. The reactor
|
13
|
+
# handles all IO and manages the connections to the Cassandra nodes. This
|
14
|
+
# makes it possible for the client to handle highly concurrent applications
|
15
|
+
# very efficiently.
|
16
|
+
#
|
17
|
+
# Client instances are threadsafe and you only need a single instance for in
|
18
|
+
# an application. Using multiple instances is more likely to lead to worse
|
19
|
+
# performance than better.
|
20
|
+
#
|
21
|
+
# Because the client opens sockets, and runs threads it cannot be used by
|
22
|
+
# the child created when forking a process. If your application forks (for
|
23
|
+
# example applications running in the Unicorn application server or Resque
|
24
|
+
# task queue) you _must_ connect after forking.
|
25
|
+
#
|
26
|
+
# @see Cql::Client.connect
|
5
27
|
class Client
|
6
|
-
# @!method connect
|
7
|
-
#
|
8
|
-
# Connect to all nodes. See {Cql::Client.connect} for the full
|
9
|
-
# documentation.
|
10
|
-
#
|
11
|
-
# This method needs to be called before any other. Calling it again will
|
12
|
-
# have no effect.
|
13
|
-
#
|
14
|
-
# @see Cql::Client.connect
|
15
|
-
# @return [Cql::Client]
|
16
|
-
|
17
28
|
# @!method close
|
18
29
|
#
|
19
30
|
# Disconnect from all nodes.
|
@@ -43,7 +54,7 @@ module Cql
|
|
43
54
|
# @raise [Cql::NotConnectedError] raised when the client is not connected
|
44
55
|
# @return [nil]
|
45
56
|
|
46
|
-
# @!method execute(cql, *values,
|
57
|
+
# @!method execute(cql, *values, options={})
|
47
58
|
#
|
48
59
|
# Execute a CQL statement, optionally passing bound values.
|
49
60
|
#
|
@@ -55,7 +66,8 @@ module Cql
|
|
55
66
|
# bound values when you will issue the request multiple times, prepared
|
56
67
|
# statements are almost always a better choice.
|
57
68
|
#
|
58
|
-
#
|
69
|
+
# _Please note that on-the-fly bound values are only supported by Cassandra
|
70
|
+
# 2.0 and above._
|
59
71
|
#
|
60
72
|
# @example A simple CQL query
|
61
73
|
# result = client.execute("SELECT * FROM users WHERE user_name = 'sue'")
|
@@ -75,6 +87,15 @@ module Cql
|
|
75
87
|
# @example Specifying the consistency and other options
|
76
88
|
# client.execute("SELECT * FROM users", consistency: :all, timeout: 1.5)
|
77
89
|
#
|
90
|
+
# @example Loading a big result page by page
|
91
|
+
# result_page = client.execute("SELECT * FROM large_table WHERE id = 'partition_with_lots_of_data'", page_size: 100)
|
92
|
+
# while result_page
|
93
|
+
# result_page.each do |row|
|
94
|
+
# p row
|
95
|
+
# end
|
96
|
+
# result_page = result_page.next_page
|
97
|
+
# end
|
98
|
+
#
|
78
99
|
# @example Activating tracing for a query
|
79
100
|
# result = client.execute("SELECT * FROM users", tracing: true)
|
80
101
|
# p result.trace_id
|
@@ -88,24 +109,26 @@ module Cql
|
|
88
109
|
# integers, and DOUBLE instead of FLOAT for floating point numbers. It
|
89
110
|
# is not recommended to use this feature for anything but convenience,
|
90
111
|
# and the algorithm used to guess types is to be considered experimental.
|
91
|
-
# @param [Hash]
|
92
|
-
#
|
93
|
-
# equivalent to passing the options `consistency: <symbol>`.
|
94
|
-
# @option options_or_consistency [Symbol] :consistency (:quorum) The
|
112
|
+
# @param [Hash] options
|
113
|
+
# @option options [Symbol] :consistency (:quorum) The
|
95
114
|
# consistency to use for this query.
|
96
|
-
# @option
|
115
|
+
# @option options [Symbol] :serial_consistency (nil) The
|
97
116
|
# consistency to use for conditional updates (`:serial` or
|
98
117
|
# `:local_serial`), see the CQL documentation for the semantics of
|
99
118
|
# serial consistencies and conditional updates. The default is assumed
|
100
119
|
# to be `:serial` by the server if none is specified. Ignored for non-
|
101
120
|
# conditional queries.
|
102
|
-
# @option
|
121
|
+
# @option options [Integer] :timeout (nil) How long to wait
|
103
122
|
# for a response. If this timeout expires a {Cql::TimeoutError} will
|
104
123
|
# be raised.
|
105
|
-
# @option
|
124
|
+
# @option options [Boolean] :trace (false) Request tracing
|
106
125
|
# for this request. See {Cql::Client::QueryResult} and
|
107
126
|
# {Cql::Client::VoidResult} for how to retrieve the tracing data.
|
108
|
-
# @option
|
127
|
+
# @option options [Integer] :page_size (nil) Instead of
|
128
|
+
# returning all rows, return the response in pages of this size. The
|
129
|
+
# first result will contain the first page, to load subsequent pages
|
130
|
+
# use {Cql::Client::QueryResult#next_page}.
|
131
|
+
# @option options [Array] :type_hints (nil) When passing
|
109
132
|
# on-the-fly bound values the request encoder will have to guess what
|
110
133
|
# types to encode the values as. Using this option you can give it hints
|
111
134
|
# and avoid it guessing wrong. The hints must be an array that has the
|
@@ -127,7 +150,7 @@ module Cql
|
|
127
150
|
# @!method prepare(cql)
|
128
151
|
#
|
129
152
|
# Returns a prepared statement that can be run over and over again with
|
130
|
-
# different values.
|
153
|
+
# different bound values.
|
131
154
|
#
|
132
155
|
# @see Cql::Client::PreparedStatement
|
133
156
|
# @param [String] cql The CQL to prepare
|
@@ -203,7 +226,7 @@ module Cql
|
|
203
226
|
@port = options[:port] || DEFAULT_PORT
|
204
227
|
@connection_timeout = options[:connection_timeout] || DEFAULT_CONNECTION_TIMEOUT
|
205
228
|
@credentials = options[:credentials]
|
206
|
-
@auth_provider = options[:auth_provider] || @credentials && PlainTextAuthProvider.new(*@credentials.values_at(:username, :password))
|
229
|
+
@auth_provider = options[:auth_provider] || @credentials && Auth::PlainTextAuthProvider.new(*@credentials.values_at(:username, :password))
|
207
230
|
@connected = false
|
208
231
|
@connecting = false
|
209
232
|
@closing = false
|
@@ -314,7 +337,7 @@ module Cql
|
|
314
337
|
MAX_RECONNECTION_ATTEMPTS = 5
|
315
338
|
|
316
339
|
def extract_hosts(options)
|
317
|
-
if options[:hosts]
|
340
|
+
if options[:hosts] && options[:hosts].any?
|
318
341
|
options[:hosts].uniq
|
319
342
|
elsif options[:host]
|
320
343
|
options[:host].split(',').uniq
|
@@ -2,6 +2,34 @@
|
|
2
2
|
|
3
3
|
module Cql
|
4
4
|
module Client
|
5
|
+
# A prepared statement are CQL queries that have been sent to the server
|
6
|
+
# to be precompiled, so that when executed only their ID and not the whole
|
7
|
+
# CQL string need to be sent. They support bound values, or placeholders
|
8
|
+
# for values.
|
9
|
+
#
|
10
|
+
# Using a prepared statement for any query that you execute more than once
|
11
|
+
# is highly recommended. Besides the benefit of having less network overhead,
|
12
|
+
# and less processing overhead on the server side, they don't require you
|
13
|
+
# to build CQL strings and escape special characters, or format non-character
|
14
|
+
# data such as UUIDs, different numeric types, or collections, in the
|
15
|
+
# correct way.
|
16
|
+
#
|
17
|
+
# You should only prepare a statement once and reuse the prepared statement
|
18
|
+
# object every time you want to execute that particular query. The statement
|
19
|
+
# object will make sure that it is prepared on all connections, and will
|
20
|
+
# (lazily, but transparently) make sure it is prepared on any new connections.
|
21
|
+
#
|
22
|
+
# It is an anti-pattern to prepare the same query over and over again. It is
|
23
|
+
# bad for performance, since every preparation requires a roundtrip to all
|
24
|
+
# connected servers, and because of some bookeeping that is done to support
|
25
|
+
# automatic preparation on new connections, it will lead to unnecessary
|
26
|
+
# extra memory usage. There is no performance benefit in creating multiple
|
27
|
+
# prepared statement objects for the same query.
|
28
|
+
#
|
29
|
+
# Prepared statement objects are completely thread safe and can be shared
|
30
|
+
# across all threads in your application.
|
31
|
+
#
|
32
|
+
# @see Cql::Client::Client#prepare
|
5
33
|
class PreparedStatement
|
6
34
|
# Metadata describing the bound values
|
7
35
|
#
|
@@ -28,10 +56,22 @@ module Cql
|
|
28
56
|
# arguments should result in an `ArgumentError` or `TypeError` being
|
29
57
|
# raised.
|
30
58
|
#
|
31
|
-
# @
|
32
|
-
#
|
33
|
-
#
|
34
|
-
#
|
59
|
+
# @example Preparing and executing an `INSERT` statement
|
60
|
+
# statement = client.prepare(%(INSERT INTO metrics (id, time, value) VALUES (?, NOW(), ?)))
|
61
|
+
# statement.execute(1234, 23432)
|
62
|
+
# statement.execute(2345, 34543, tracing: true)
|
63
|
+
# statement.execute(3456, 45654, consistency: :one)
|
64
|
+
#
|
65
|
+
# @example Preparing and executing a `SELECT` statement
|
66
|
+
# statement = client.prepare(%(SELECT * FROM metrics WHERE id = ? AND time > ?))
|
67
|
+
# result = statement.execute(1234, Time.now - 3600)
|
68
|
+
# result.each do |row|
|
69
|
+
# p row
|
70
|
+
# end
|
71
|
+
#
|
72
|
+
# @param args [Array] the values for the bound parameters, and an optional
|
73
|
+
# hash of options as last argument – see {Cql::Client::Client#execute}
|
74
|
+
# for details on which options are available.
|
35
75
|
# @raise [ArgumentError] raised when number of argument does not match
|
36
76
|
# the number of parameters needed to be bound to the statement.
|
37
77
|
# @raise [Cql::NotConnectedError] raised when the client is not connected
|
@@ -45,6 +85,45 @@ module Cql
|
|
45
85
|
# (see {Cql::Client::VoidResult}).
|
46
86
|
def execute(*args)
|
47
87
|
end
|
88
|
+
|
89
|
+
# Yields a batch when called with a block. The batch is automatically
|
90
|
+
# executed at the end of the block and the result is returned.
|
91
|
+
#
|
92
|
+
# Returns a batch when called wihtout a block. The batch will remember
|
93
|
+
# the options given and merge these with any additional options given
|
94
|
+
# when {Cql::Client::PreparedStatementBatch#execute} is called.
|
95
|
+
#
|
96
|
+
# The batch yielded or returned by this method is not identical to the
|
97
|
+
# regular batch objects yielded or returned by {Cql::Client::Client#batch}.
|
98
|
+
# These prepared statement batch objects can be used only to add multiple
|
99
|
+
# executions of the same prepared statement.
|
100
|
+
#
|
101
|
+
# Please note that the batch object returned by this method _is not thread
|
102
|
+
# safe_.
|
103
|
+
#
|
104
|
+
# The type parameter can be ommitted and the options can then be given
|
105
|
+
# as first parameter.
|
106
|
+
#
|
107
|
+
# @example Executing a prepared statement in a batch
|
108
|
+
# statement = client.prepare(%(INSERT INTO metrics (id, time, value) VALUES (?, NOW(), ?)))
|
109
|
+
# statement.batch do |batch|
|
110
|
+
# batch.add(1234, 23423)
|
111
|
+
# batch.add(2346, 13)
|
112
|
+
# batch.add(2342, 2367)
|
113
|
+
# batch.add(4562, 1231)
|
114
|
+
# end
|
115
|
+
#
|
116
|
+
# @see Cql::Client::PreparedStatementBatch
|
117
|
+
# @see Cql::Client::Client#batch
|
118
|
+
# @param [Symbol] type the type of batch, must be one of `:logged`,
|
119
|
+
# `:unlogged` and `:counter`. The precise meaning of these is defined
|
120
|
+
# in the CQL specification.
|
121
|
+
# @yieldparam [Cql::Client::PreparedStatementBatch] batch the batch
|
122
|
+
# @return [Cql::Client::VoidResult, Cql::Client::Batch] when no block is
|
123
|
+
# given the batch is returned, when a block is given the result of
|
124
|
+
# executing the batch is returned (see {Cql::Client::Batch#execute}).
|
125
|
+
def batch(type=:logged, options={})
|
126
|
+
end
|
48
127
|
end
|
49
128
|
|
50
129
|
# @private
|
@@ -2,6 +2,16 @@
|
|
2
2
|
|
3
3
|
module Cql
|
4
4
|
module Client
|
5
|
+
# Query results encapsulate the rows returned by a query.
|
6
|
+
#
|
7
|
+
# In addition to containing the rows it contains metadata about the data
|
8
|
+
# types of the columns of the rows, and it knows the ID of the trace,
|
9
|
+
# if tracing was requested for the query.
|
10
|
+
#
|
11
|
+
# When paging over a big result you can use {#last_page?} to find out if the
|
12
|
+
# page is the last, or {#next_page} to retrieve the next page.
|
13
|
+
#
|
14
|
+
# `QueryResult` is an `Enumerable` so it can be mapped, filtered, reduced, etc.
|
5
15
|
class QueryResult
|
6
16
|
include Enumerable
|
7
17
|
|
@@ -37,8 +47,31 @@ module Cql
|
|
37
47
|
@rows.each(&block)
|
38
48
|
end
|
39
49
|
alias_method :each_row, :each
|
50
|
+
|
51
|
+
# Returns true when there are no more pages to load.
|
52
|
+
#
|
53
|
+
# This is only relevant when you have requested paging of the results with
|
54
|
+
# the `:page_size` option to {Cql::Client::Client#execute} or
|
55
|
+
# {Cql::Client::PreparedStatement#execute}.
|
56
|
+
#
|
57
|
+
# @see Cql::Client::Client#execute
|
58
|
+
def last_page?
|
59
|
+
true
|
60
|
+
end
|
61
|
+
|
62
|
+
# Returns the next page or nil when there is no next page.
|
63
|
+
#
|
64
|
+
# This is only relevant when you have requested paging of the results with
|
65
|
+
# the `:page_size` option to {Cql::Client::Client#execute} or
|
66
|
+
# {Cql::Client::PreparedStatement#execute}.
|
67
|
+
#
|
68
|
+
# @see Cql::Client::Client#execute
|
69
|
+
def next_page
|
70
|
+
nil
|
71
|
+
end
|
40
72
|
end
|
41
73
|
|
74
|
+
# @private
|
42
75
|
class PagedQueryResult < QueryResult
|
43
76
|
def metadata
|
44
77
|
@result.metadata
|
@@ -88,6 +121,7 @@ module Cql
|
|
88
121
|
end
|
89
122
|
|
90
123
|
def next_page
|
124
|
+
return Future.resolved(nil) if last_page?
|
91
125
|
@client.execute(@request.cql, *@request.values, @options)
|
92
126
|
end
|
93
127
|
end
|
@@ -100,6 +134,7 @@ module Cql
|
|
100
134
|
end
|
101
135
|
|
102
136
|
def next_page
|
137
|
+
return Future.resolved(nil) if last_page?
|
103
138
|
@prepared_statement.execute(*@request.values, @options)
|
104
139
|
end
|
105
140
|
end
|
@@ -121,7 +156,10 @@ module Cql
|
|
121
156
|
end
|
122
157
|
|
123
158
|
def next_page
|
124
|
-
synchronous_backtrace
|
159
|
+
synchronous_backtrace do
|
160
|
+
asynchronous_result = @result.next_page.value
|
161
|
+
asynchronous_result && self.class.new(asynchronous_result)
|
162
|
+
end
|
125
163
|
end
|
126
164
|
end
|
127
165
|
|
@@ -2,6 +2,9 @@
|
|
2
2
|
|
3
3
|
module Cql
|
4
4
|
module Client
|
5
|
+
# A collection of metadata (keyspace, table, name and type) of a result set.
|
6
|
+
#
|
7
|
+
# @see Cql::Client::ColumnMetadata
|
5
8
|
class ResultMetadata
|
6
9
|
include Enumerable
|
7
10
|
|
@@ -13,7 +16,6 @@ module Cql
|
|
13
16
|
# Returns the column metadata
|
14
17
|
#
|
15
18
|
# @return [ColumnMetadata] column_metadata the metadata for the column
|
16
|
-
#
|
17
19
|
def [](column_name)
|
18
20
|
@metadata[column_name]
|
19
21
|
end
|
@@ -22,7 +24,6 @@ module Cql
|
|
22
24
|
#
|
23
25
|
# @yieldparam [ColumnMetadata] metadata the metadata for each column
|
24
26
|
# @return [Enumerable<ColumnMetadata>]
|
25
|
-
#
|
26
27
|
def each(&block)
|
27
28
|
@metadata.each_value(&block)
|
28
29
|
end
|
@@ -2,6 +2,16 @@
|
|
2
2
|
|
3
3
|
module Cql
|
4
4
|
module Client
|
5
|
+
# Many CQL queries do not return any rows, but they can still return
|
6
|
+
# data about the query, for example the trace ID. This class exist to make
|
7
|
+
# that data available.
|
8
|
+
#
|
9
|
+
# It has the exact same API as {Cql::Client::QueryResult} so that you don't
|
10
|
+
# need to check the return value of for example {Cql::Client::Client#execute}.
|
11
|
+
#
|
12
|
+
# @see Cql::Client::QueryResult
|
13
|
+
# @see Cql::Client::Client#execute
|
14
|
+
# @see Cql::Client::PreparedStatement#execute
|
5
15
|
class VoidResult
|
6
16
|
include Enumerable
|
7
17
|
|
@@ -26,6 +36,16 @@ module Cql
|
|
26
36
|
true
|
27
37
|
end
|
28
38
|
|
39
|
+
# Always returns true
|
40
|
+
def last_page?
|
41
|
+
true
|
42
|
+
end
|
43
|
+
|
44
|
+
# Always returns nil
|
45
|
+
def next_page
|
46
|
+
nil
|
47
|
+
end
|
48
|
+
|
29
49
|
# No-op for API compatibility with {QueryResult}.
|
30
50
|
#
|
31
51
|
# @return [Enumerable]
|
data/lib/cql/version.rb
CHANGED
@@ -11,7 +11,7 @@ module Cql
|
|
11
11
|
end
|
12
12
|
|
13
13
|
let :default_connection_options do
|
14
|
-
{:
|
14
|
+
{:hosts => %w[example.com], :port => 12321, :io_reactor => io_reactor, :logger => logger}
|
15
15
|
end
|
16
16
|
|
17
17
|
let :connection_options do
|
@@ -162,6 +162,19 @@ module Cql
|
|
162
162
|
expect { client.connect.value }.to raise_error('bork')
|
163
163
|
end
|
164
164
|
|
165
|
+
it 'connects to localhost by default' do
|
166
|
+
connection_options.delete(:hosts)
|
167
|
+
c = described_class.new(connection_options)
|
168
|
+
c.connect.value
|
169
|
+
connections.map(&:host).should == %w[localhost]
|
170
|
+
end
|
171
|
+
|
172
|
+
it 'connects to localhost when an empty list of hosts is given' do
|
173
|
+
c = described_class.new(connection_options.merge(hosts: []))
|
174
|
+
c.connect.value
|
175
|
+
connections.map(&:host).should == %w[localhost]
|
176
|
+
end
|
177
|
+
|
165
178
|
context 'when connecting to multiple hosts' do
|
166
179
|
before do
|
167
180
|
client.close.value
|
@@ -175,6 +188,7 @@ module Cql
|
|
175
188
|
end
|
176
189
|
|
177
190
|
it 'connects to all hosts, when given as a comma-sepatated string' do
|
191
|
+
connection_options.delete(:hosts)
|
178
192
|
c = described_class.new(connection_options.merge(host: 'h1.example.com,h2.example.com,h3.example.com'))
|
179
193
|
c.connect.value
|
180
194
|
connections.should have(3).items
|
@@ -510,7 +524,7 @@ module Cql
|
|
510
524
|
|
511
525
|
context 'when the server requests authentication' do
|
512
526
|
let :auth_provider do
|
513
|
-
PlainTextAuthProvider.new('foo', 'bar')
|
527
|
+
Auth::PlainTextAuthProvider.new('foo', 'bar')
|
514
528
|
end
|
515
529
|
|
516
530
|
def accepting_request_handler(request, *)
|
@@ -84,6 +84,18 @@ module Cql
|
|
84
84
|
result.should_not be_empty
|
85
85
|
end
|
86
86
|
end
|
87
|
+
|
88
|
+
describe '#last_page?' do
|
89
|
+
it 'returns true' do
|
90
|
+
result.should be_last_page
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
describe '#next_page' do
|
95
|
+
it 'returns nil' do
|
96
|
+
result.next_page.should be_nil
|
97
|
+
end
|
98
|
+
end
|
87
99
|
end
|
88
100
|
|
89
101
|
describe LazyQueryResult do
|
@@ -209,11 +221,16 @@ module Cql
|
|
209
221
|
|
210
222
|
describe '#next_page' do
|
211
223
|
let :next_query_result do
|
212
|
-
double(:next_query_result, paging_state: 'thenextpagingstate')
|
224
|
+
described_class.new(client, request, double(:next_query_result, paging_state: 'thenextpagingstate'), options)
|
225
|
+
end
|
226
|
+
|
227
|
+
let :last_query_result do
|
228
|
+
described_class.new(client, request, double(:next_query_result, paging_state: nil), options)
|
213
229
|
end
|
214
230
|
|
215
231
|
before do
|
216
232
|
client.stub(:execute).and_return(Future.resolved(next_query_result))
|
233
|
+
client.stub(:execute).with(anything, anything, hash_including(paging_state: 'thenextpagingstate')).and_return(Future.resolved(last_query_result))
|
217
234
|
end
|
218
235
|
|
219
236
|
it 'calls the client and passes the paging state' do
|
@@ -252,6 +269,11 @@ module Cql
|
|
252
269
|
f = paged_query_result.next_page
|
253
270
|
f.value.should equal(next_query_result)
|
254
271
|
end
|
272
|
+
|
273
|
+
it 'returns nil when it is the last page' do
|
274
|
+
f = paged_query_result.next_page.value.next_page.value.next_page
|
275
|
+
f.value.should be_nil
|
276
|
+
end
|
255
277
|
end
|
256
278
|
end
|
257
279
|
|
@@ -280,16 +302,20 @@ module Cql
|
|
280
302
|
|
281
303
|
describe '#next_page' do
|
282
304
|
let :next_query_result do
|
283
|
-
double(:next_query_result, paging_state: 'thenextpagingstate')
|
305
|
+
described_class.new(statement, request, double(:next_query_result, paging_state: 'thenextpagingstate'), options)
|
306
|
+
end
|
307
|
+
|
308
|
+
let :last_query_result do
|
309
|
+
described_class.new(statement, request, double(:next_query_result, paging_state: nil), options)
|
284
310
|
end
|
285
311
|
|
286
312
|
before do
|
287
313
|
statement.stub(:execute).and_return(Future.resolved(next_query_result))
|
314
|
+
statement.stub(:execute).with(anything, anything, hash_including(paging_state: 'thenextpagingstate')).and_return(Future.resolved(last_query_result))
|
288
315
|
end
|
289
316
|
|
290
317
|
it 'calls the statement and passes the paging state' do
|
291
318
|
paged_query_result.next_page.value
|
292
|
-
statement.should have_received(:execute).with(anything, anything, hash_including(paging_state: 'thepagingstate'))
|
293
319
|
end
|
294
320
|
|
295
321
|
it 'calls the statement and passes the options' do
|
@@ -312,6 +338,11 @@ module Cql
|
|
312
338
|
f = paged_query_result.next_page
|
313
339
|
f.value.should equal(next_query_result)
|
314
340
|
end
|
341
|
+
|
342
|
+
it 'returns nil when it is the last page' do
|
343
|
+
f = paged_query_result.next_page.value.next_page.value.next_page
|
344
|
+
f.value.should be_nil
|
345
|
+
end
|
315
346
|
end
|
316
347
|
end
|
317
348
|
|
@@ -336,6 +367,11 @@ module Cql
|
|
336
367
|
second_page.next_page
|
337
368
|
next_query_result.should have_received(:next_page)
|
338
369
|
end
|
370
|
+
|
371
|
+
it 'returns nil when it is the last page' do
|
372
|
+
asynchronous_paged_query_result.stub(:next_page).and_return(Future.resolved(nil))
|
373
|
+
paged_query_result.next_page.should be_nil
|
374
|
+
end
|
339
375
|
end
|
340
376
|
|
341
377
|
describe '#last_page?' do
|
@@ -12,6 +12,18 @@ module Cql
|
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
|
+
describe '#last_page?' do
|
16
|
+
it 'is true' do
|
17
|
+
described_class.new.should be_last_page
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe '#next_page' do
|
22
|
+
it 'returns nil' do
|
23
|
+
described_class.new.next_page.should be_nil
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
15
27
|
describe '#trace_id' do
|
16
28
|
it 'is nil' do
|
17
29
|
described_class.new.trace_id.should be_nil
|
@@ -192,7 +192,7 @@ describe 'A CQL client' do
|
|
192
192
|
|
193
193
|
it 'raises an error when only an auth provider has been given' do
|
194
194
|
pending('authentication not configured', unless: authentication_enabled) do
|
195
|
-
auth_provider = Cql::
|
195
|
+
auth_provider = Cql::Auth::PlainTextAuthProvider.new('cassandra', 'cassandra')
|
196
196
|
expect { Cql::Client.connect(connection_options.merge(credentials: nil, auth_provider: auth_provider, protocol_version: 1)) }.to raise_error(Cql::AuthenticationError)
|
197
197
|
end
|
198
198
|
end
|
@@ -209,7 +209,7 @@ describe 'A CQL client' do
|
|
209
209
|
end
|
210
210
|
|
211
211
|
it 'uses the auth provider given in the :auth_provider option' do
|
212
|
-
auth_provider = Cql::
|
212
|
+
auth_provider = Cql::Auth::PlainTextAuthProvider.new('cassandra', 'cassandra')
|
213
213
|
client = Cql::Client.connect(connection_options.merge(auth_provider: auth_provider, credentials: nil))
|
214
214
|
client.execute('SELECT * FROM system.schema_keyspaces')
|
215
215
|
end
|
@@ -228,7 +228,7 @@ describe 'A CQL client' do
|
|
228
228
|
it 'raises an error when the credentials are bad' do
|
229
229
|
pending('authentication not configured', unless: authentication_enabled) do
|
230
230
|
expect {
|
231
|
-
auth_provider = Cql::
|
231
|
+
auth_provider = Cql::Auth::PlainTextAuthProvider.new('foo', 'bar')
|
232
232
|
Cql::Client.connect(connection_options.merge(auth_provider: auth_provider, credentials: nil))
|
233
233
|
}.to raise_error(Cql::AuthenticationError)
|
234
234
|
end
|
@@ -408,6 +408,18 @@ describe 'A CQL client' do
|
|
408
408
|
result_page.count.should == row_count - page_size
|
409
409
|
result_page.should be_last_page
|
410
410
|
end
|
411
|
+
|
412
|
+
it 'returns nil from #next_page when the last page has been returned' do
|
413
|
+
page_size = row_count/5 + 1
|
414
|
+
statement = client.prepare('SELECT * FROM counters')
|
415
|
+
result_page = statement.execute(page_size: page_size)
|
416
|
+
page_count = 0
|
417
|
+
while result_page
|
418
|
+
page_count += 1
|
419
|
+
result_page = result_page.next_page
|
420
|
+
end
|
421
|
+
page_count.should == 5
|
422
|
+
end
|
411
423
|
end
|
412
424
|
|
413
425
|
context 'with error conditions' do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cql-rb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.0.
|
4
|
+
version: 2.0.0.rc0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Theo Hultberg
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-05-
|
11
|
+
date: 2014-05-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ione
|
@@ -34,8 +34,9 @@ files:
|
|
34
34
|
- .yardopts
|
35
35
|
- README.md
|
36
36
|
- lib/cql.rb
|
37
|
+
- lib/cql/auth.rb
|
38
|
+
- lib/cql/auth/plain_text_auth.rb
|
37
39
|
- lib/cql/client.rb
|
38
|
-
- lib/cql/client/authenticators.rb
|
39
40
|
- lib/cql/client/batch.rb
|
40
41
|
- lib/cql/client/client.rb
|
41
42
|
- lib/cql/client/column_metadata.rb
|
@@ -47,7 +48,6 @@ files:
|
|
47
48
|
- lib/cql/client/peer_discovery.rb
|
48
49
|
- lib/cql/client/prepared_statement.rb
|
49
50
|
- lib/cql/client/query_result.rb
|
50
|
-
- lib/cql/client/query_trace.rb
|
51
51
|
- lib/cql/client/request_runner.rb
|
52
52
|
- lib/cql/client/result_metadata.rb
|
53
53
|
- lib/cql/client/void_result.rb
|
@@ -91,7 +91,7 @@ files:
|
|
91
91
|
- lib/cql/time_uuid.rb
|
92
92
|
- lib/cql/uuid.rb
|
93
93
|
- lib/cql/version.rb
|
94
|
-
- spec/cql/
|
94
|
+
- spec/cql/auth/plain_text_auth_spec.rb
|
95
95
|
- spec/cql/client/batch_spec.rb
|
96
96
|
- spec/cql/client/client_spec.rb
|
97
97
|
- spec/cql/client/column_metadata_spec.rb
|
@@ -102,7 +102,6 @@ files:
|
|
102
102
|
- spec/cql/client/peer_discovery_spec.rb
|
103
103
|
- spec/cql/client/prepared_statement_spec.rb
|
104
104
|
- spec/cql/client/query_result_spec.rb
|
105
|
-
- spec/cql/client/query_trace_spec.rb
|
106
105
|
- spec/cql/client/request_runner_spec.rb
|
107
106
|
- spec/cql/client/void_result_spec.rb
|
108
107
|
- spec/cql/compression/compression_common.rb
|
@@ -174,7 +173,7 @@ signing_key:
|
|
174
173
|
specification_version: 4
|
175
174
|
summary: Cassandra CQL3 driver
|
176
175
|
test_files:
|
177
|
-
- spec/cql/
|
176
|
+
- spec/cql/auth/plain_text_auth_spec.rb
|
178
177
|
- spec/cql/client/batch_spec.rb
|
179
178
|
- spec/cql/client/client_spec.rb
|
180
179
|
- spec/cql/client/column_metadata_spec.rb
|
@@ -185,7 +184,6 @@ test_files:
|
|
185
184
|
- spec/cql/client/peer_discovery_spec.rb
|
186
185
|
- spec/cql/client/prepared_statement_spec.rb
|
187
186
|
- spec/cql/client/query_result_spec.rb
|
188
|
-
- spec/cql/client/query_trace_spec.rb
|
189
187
|
- spec/cql/client/request_runner_spec.rb
|
190
188
|
- spec/cql/client/void_result_spec.rb
|
191
189
|
- spec/cql/compression/compression_common.rb
|
@@ -1,46 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
module Cql
|
4
|
-
module Client
|
5
|
-
# @private
|
6
|
-
class QueryTrace
|
7
|
-
attr_reader :coordinator, :cql, :started_at, :duration, :events
|
8
|
-
|
9
|
-
# @private
|
10
|
-
def initialize(session, events)
|
11
|
-
if session
|
12
|
-
raise IncompleteTraceError, 'Trace incomplete, try loading it again' unless session['duration']
|
13
|
-
@coordinator = session['coordinator']
|
14
|
-
@cql = (parameters = session['parameters']) && parameters['query']
|
15
|
-
@started_at = session['started_at']
|
16
|
-
@duration = session['duration']/1_000_000.0
|
17
|
-
if events
|
18
|
-
@events = events.map { |e| TraceEvent.new(e) }.freeze
|
19
|
-
end
|
20
|
-
else
|
21
|
-
@events = [].freeze
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
# @private
|
27
|
-
class TraceEvent
|
28
|
-
attr_reader :activity, :source, :source_elapsed, :time
|
29
|
-
|
30
|
-
# @private
|
31
|
-
def initialize(event)
|
32
|
-
@activity = event['activity']
|
33
|
-
@source = event['source']
|
34
|
-
@source_elapsed = event['source_elapsed']/1_000_000.0
|
35
|
-
@time = event['event_id'].to_time
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
# @private
|
40
|
-
class NullQueryTrace < QueryTrace
|
41
|
-
def initialize
|
42
|
-
super(nil, nil)
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
@@ -1,138 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
require 'spec_helper'
|
4
|
-
|
5
|
-
|
6
|
-
module Cql
|
7
|
-
module Client
|
8
|
-
describe QueryTrace do
|
9
|
-
let :trace do
|
10
|
-
described_class.new(session_row, event_rows)
|
11
|
-
end
|
12
|
-
|
13
|
-
let :started_at do
|
14
|
-
Time.now
|
15
|
-
end
|
16
|
-
|
17
|
-
let :session_row do
|
18
|
-
{
|
19
|
-
'session_id' => Uuid.new('a1028490-3f05-11e3-9531-fb72eff05fbb'),
|
20
|
-
'coordinator' => IPAddr.new('127.0.0.1'),
|
21
|
-
'duration' => 1263,
|
22
|
-
'parameters' => {
|
23
|
-
'query' => 'SELECT * FROM something'
|
24
|
-
},
|
25
|
-
'request' => 'Execute CQL3 query',
|
26
|
-
'started_at' => started_at
|
27
|
-
}
|
28
|
-
end
|
29
|
-
|
30
|
-
let :event_rows do
|
31
|
-
[
|
32
|
-
{
|
33
|
-
'session_id' => Uuid.new('a1028490-3f05-11e3-9531-fb72eff05fbb'),
|
34
|
-
'event_id' => TimeUuid.new('a1028491-3f05-11e3-9531-fb72eff05fbb'),
|
35
|
-
'activity' => 'Parsing statement',
|
36
|
-
'source' => IPAddr.new('127.0.0.1'),
|
37
|
-
'source_elapsed' => 52,
|
38
|
-
'thread' => 'Native-Transport-Requests:126'
|
39
|
-
},
|
40
|
-
{
|
41
|
-
'session_id' => Uuid.new('a1028490-3f05-11e3-9531-fb72eff05fbb'),
|
42
|
-
'event_id' => TimeUuid.new('a1028492-3f05-11e3-9531-fb72eff05fbb'),
|
43
|
-
'activity' => 'Peparing statement',
|
44
|
-
'source' => IPAddr.new('127.0.0.1'),
|
45
|
-
'source_elapsed' => 54,
|
46
|
-
'thread' => 'Native-Transport-Requests:126'
|
47
|
-
},
|
48
|
-
]
|
49
|
-
end
|
50
|
-
|
51
|
-
context 'when the session is nil' do
|
52
|
-
it 'returns nil from all methods' do
|
53
|
-
trace = described_class.new(nil, nil)
|
54
|
-
trace.coordinator.should be_nil
|
55
|
-
trace.cql.should be_nil
|
56
|
-
trace.started_at.should be_nil
|
57
|
-
trace.events.should be_empty
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
context 'when the duration field of the session is nil' do
|
62
|
-
it 'raises an IncompleteTraceError' do
|
63
|
-
session_row['duration'] = nil
|
64
|
-
expect { described_class.new(session_row, event_rows) }.to raise_error(IncompleteTraceError)
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
describe '#coordinator' do
|
69
|
-
it 'returns the IP address of the coordinator node' do
|
70
|
-
trace.coordinator.should == IPAddr.new('127.0.0.1')
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
describe '#cql' do
|
75
|
-
it 'returns the query' do
|
76
|
-
trace.cql.should == 'SELECT * FROM something'
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
describe '#started_at' do
|
81
|
-
it 'returns the time the request started' do
|
82
|
-
trace.started_at.should eql(started_at)
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
describe '#duration' do
|
87
|
-
it 'returns the duration in seconds' do
|
88
|
-
trace.duration.should == 0.001263
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
describe '#events' do
|
93
|
-
it 'returns a list of TraceEvents' do
|
94
|
-
trace.events.should have(2).items
|
95
|
-
trace.events.first.should be_a(TraceEvent)
|
96
|
-
end
|
97
|
-
|
98
|
-
it 'returns an unmodifiable list' do
|
99
|
-
expect { trace.events << :foo }.to raise_error
|
100
|
-
end
|
101
|
-
|
102
|
-
context 'returns a list of trace events whose' do
|
103
|
-
let :events do
|
104
|
-
trace.events
|
105
|
-
end
|
106
|
-
|
107
|
-
let :event do
|
108
|
-
events.first
|
109
|
-
end
|
110
|
-
|
111
|
-
describe '#activity' do
|
112
|
-
it 'returns the event activity' do
|
113
|
-
event.activity.should == 'Parsing statement'
|
114
|
-
end
|
115
|
-
end
|
116
|
-
|
117
|
-
describe '#source' do
|
118
|
-
it 'returns the event source' do
|
119
|
-
event.source.should == IPAddr.new('127.0.0.1')
|
120
|
-
end
|
121
|
-
end
|
122
|
-
|
123
|
-
describe '#source_elapsed' do
|
124
|
-
it 'returns the elapsed time at the source' do
|
125
|
-
event.source_elapsed.should == 0.000052
|
126
|
-
end
|
127
|
-
end
|
128
|
-
|
129
|
-
describe '#time' do
|
130
|
-
it 'returns the time component from the event ID' do
|
131
|
-
event.time.to_i.should == TimeUuid.new('a1028492-3f05-11e3-9531-fb72eff05fbb').to_time.to_i
|
132
|
-
end
|
133
|
-
end
|
134
|
-
end
|
135
|
-
end
|
136
|
-
end
|
137
|
-
end
|
138
|
-
end
|