cql-rb 2.0.0.pre2 → 2.0.0.rc0
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 +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
|