cequel 2.0.2 → 2.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -0
- data/Gemfile.lock +1 -1
- data/README.md +10 -10
- data/lib/cequel.rb +1 -0
- data/lib/cequel/metal/data_set.rb +16 -0
- data/lib/cequel/metal/keyspace.rb +47 -46
- data/lib/cequel/metal/policy/cassandra_error.rb +75 -0
- data/lib/cequel/record/persistence.rb +2 -2
- data/lib/cequel/record/record_set.rb +4 -0
- data/lib/cequel/record/schema.rb +15 -4
- data/lib/cequel/schema/keyspace.rb +18 -0
- data/lib/cequel/schema/table_reader.rb +34 -10
- data/lib/cequel/type.rb +14 -14
- data/lib/cequel/version.rb +1 -1
- data/spec/examples/metal/keyspace_spec.rb +94 -0
- data/spec/examples/schema/table_reader_spec.rb +36 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2ff9eed6cd9463e5896643152fd7314b4df3324e
|
4
|
+
data.tar.gz: 6e62173d98fa94f77d6022ca22501ae778e40139
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4ad422adb85827d83efa126565c8d43f4c1b34626a154e2f627fcdb7d04e0567d6635941cc88bfa622a3f79aeec875fd30da653ecbe9c71f0f9a45a1bc738c1b
|
7
|
+
data.tar.gz: 1d157437a9c7d435412a22206d8e2103b2b214c494acdd2719a3aae8d7ef5969dd674036b271f823bd026aa50b664afd99413afc5b4edb0bfd61cb2f249f773c
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
## 2.0.3
|
2
|
+
|
3
|
+
* Add synchronization around use of @cluster and other variables Fix ([PR 333](https://github.com/cequel/cequel/pull/333))
|
4
|
+
* expose if the dataset is on the last page ([PR 335](https://github.com/cequel/cequel/pull/335))
|
5
|
+
* Delegate error handling to a policy object, allow for arbitrary options to be passed to cassandra driver gem ([PR 336](https://github.com/cequel/cequel/pull/336))
|
6
|
+
* Fixes README.md ([PR 340](https://github.com/cequel/cequel/pull/340))
|
7
|
+
* skip synchronizing materialized view ([PR 346](https://github.com/cequel/cequel/pull/346))
|
8
|
+
* Fixed link to cassandra documentation ([PR 347](https://github.com/cequel/cequel/pull/347))
|
9
|
+
|
10
|
+
|
1
11
|
## 2.0.2
|
2
12
|
|
3
13
|
* Fix intermittent failures around preparing statements ([PR 330](https://github.com/cequel/cequel/pull/330))
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -231,7 +231,7 @@ multiple queries in your logs if you're iterating over a huge result set.
|
|
231
231
|
CQL has [special handling for the `timeuuid`
|
232
232
|
type](https://docs.datastax.com/en/cql/3.3/cql/cql_reference/uuid_type_r.html),
|
233
233
|
which allows you to return a rows whose UUID keys correspond to a range of
|
234
|
-
timestamps.
|
234
|
+
timestamps.
|
235
235
|
|
236
236
|
Cequel automatically constructs timeuuid range queries if you pass a `Time`
|
237
237
|
value for a range over a `timeuuid` column. So, if you want to get the posts
|
@@ -393,7 +393,7 @@ Both read and write consistency default to `QUORUM`.
|
|
393
393
|
### Compression ###
|
394
394
|
|
395
395
|
Cassandra supports [frame compression](http://datastax.github.io/ruby-driver/features/#compression),
|
396
|
-
which can give you a performance boost if your requests or responses are big. To enable it you can
|
396
|
+
which can give you a performance boost if your requests or responses are big. To enable it you can
|
397
397
|
specify `client_compression` to use in cequel.yaml.
|
398
398
|
|
399
399
|
```yaml
|
@@ -539,14 +539,6 @@ essentially the same thing: both simply persist the given column data at the
|
|
539
539
|
given key(s). So, you may think you are creating a new record, but in fact
|
540
540
|
you're overwriting data at an existing record:
|
541
541
|
|
542
|
-
#### Counting ####
|
543
|
-
|
544
|
-
Counting is not the same as in a RDB, as it can have a much longer runtime and
|
545
|
-
can put unexpected load on your cluster. As a result Cequel does not support
|
546
|
-
this feature. It is still possible to execute raw cql to get the counts, should
|
547
|
-
you require this functionality.
|
548
|
-
`MyModel.connection.execute('select count(*) from table_name;').first['count']`
|
549
|
-
|
550
542
|
``` ruby
|
551
543
|
# I'm just creating a blog here.
|
552
544
|
blog1 = Blog.create!(
|
@@ -566,6 +558,14 @@ above code will just overwrite the `name` in that row. Note that the
|
|
566
558
|
`description` will not be touched by the second statement; upserts only work on
|
567
559
|
the columns that are given.
|
568
560
|
|
561
|
+
#### Counting ####
|
562
|
+
|
563
|
+
Counting is not the same as in a RDB, as it can have a much longer runtime and
|
564
|
+
can put unexpected load on your cluster. As a result Cequel does not support
|
565
|
+
this feature. It is still possible to execute raw cql to get the counts, should
|
566
|
+
you require this functionality.
|
567
|
+
`MyModel.connection.execute('select count(*) from table_name;').first['count']`
|
568
|
+
|
569
569
|
## Compatibility ##
|
570
570
|
|
571
571
|
### Rails ###
|
data/lib/cequel.rb
CHANGED
@@ -579,10 +579,26 @@ module Cequel
|
|
579
579
|
end
|
580
580
|
end
|
581
581
|
|
582
|
+
#
|
583
|
+
# Exposes current paging state for stateless pagination
|
584
|
+
#
|
585
|
+
# @return [String] or nil
|
586
|
+
#
|
587
|
+
# @see http://docs.datastax.com/en/developer/ruby-driver/3.0/api/cassandra/result/#paging_state-instance_method
|
588
|
+
#
|
582
589
|
def next_paging_state
|
583
590
|
results.paging_state
|
584
591
|
end
|
585
592
|
|
593
|
+
#
|
594
|
+
# @return [Boolean] Returns whether no more pages are available
|
595
|
+
#
|
596
|
+
# @see http://docs.datastax.com/en/developer/ruby-driver/3.0/api/cassandra/result/#last_page?-instance_method
|
597
|
+
#
|
598
|
+
def last_page?
|
599
|
+
results.last_page?
|
600
|
+
end
|
601
|
+
|
586
602
|
# rubocop:enable LineLength
|
587
603
|
|
588
604
|
#
|
@@ -21,10 +21,6 @@ module Cequel
|
|
21
21
|
attr_reader :hosts
|
22
22
|
# @return Integer port to connect to Cassandra nodes on
|
23
23
|
attr_reader :port
|
24
|
-
# @return Integer maximum number of retries to reconnect to Cassandra
|
25
|
-
attr_reader :max_retries
|
26
|
-
# @return Float delay between retries to reconnect to Cassandra
|
27
|
-
attr_reader :retry_delay
|
28
24
|
# @return [Symbol] the default consistency for queries in this keyspace
|
29
25
|
# @since 1.1.0
|
30
26
|
attr_writer :default_consistency
|
@@ -34,6 +30,10 @@ module Cequel
|
|
34
30
|
attr_reader :ssl_config
|
35
31
|
# @return [Symbol] The client compression option
|
36
32
|
attr_reader :client_compression
|
33
|
+
# @return [Hash] A hash of additional options passed to Cassandra, if any
|
34
|
+
attr_reader :cassandra_options
|
35
|
+
# @return [Object] The error policy object in use by this keyspace
|
36
|
+
attr_reader :error_policy
|
37
37
|
|
38
38
|
#
|
39
39
|
# @!method write(statement, *bind_vars)
|
@@ -52,7 +52,7 @@ module Cequel
|
|
52
52
|
# consistency. Will be included the current batch operation if one is
|
53
53
|
# present.
|
54
54
|
#
|
55
|
-
# @param (see #
|
55
|
+
# @param (see #execute_with_options)
|
56
56
|
# @return [void]
|
57
57
|
#
|
58
58
|
def_delegator :write_target, :execute_with_options,
|
@@ -94,8 +94,8 @@ module Cequel
|
|
94
94
|
# @see Cequel.connect
|
95
95
|
#
|
96
96
|
def initialize(configuration={})
|
97
|
-
configure(configuration)
|
98
97
|
@lock = Monitor.new
|
98
|
+
configure(configuration)
|
99
99
|
end
|
100
100
|
|
101
101
|
#
|
@@ -122,8 +122,12 @@ module Cequel
|
|
122
122
|
# certificate
|
123
123
|
# @option configuration [String] :private_key path to ssl client private
|
124
124
|
# key
|
125
|
-
# @option
|
125
|
+
# @option configuration [String] :passphrase the passphrase for client
|
126
126
|
# private key
|
127
|
+
# @option configuration [String] :cassandra_error_policy A mixin for
|
128
|
+
# handling errors from Cassandra
|
129
|
+
# @option configuration [Hash] :cassandra_options A hash of arbitrary
|
130
|
+
# options to pass to Cassandra
|
127
131
|
# @return [void]
|
128
132
|
#
|
129
133
|
def configure(configuration = {})
|
@@ -132,11 +136,11 @@ module Cequel
|
|
132
136
|
"with Cassandra. The :thrift option is deprecated and ignored."
|
133
137
|
end
|
134
138
|
@configuration = configuration
|
135
|
-
|
139
|
+
|
140
|
+
@error_policy = extract_cassandra_error_policy(configuration)
|
141
|
+
@cassandra_options = extract_cassandra_options(configuration)
|
136
142
|
@hosts, @port = extract_hosts_and_port(configuration)
|
137
143
|
@credentials = extract_credentials(configuration)
|
138
|
-
@max_retries = extract_max_retries(configuration)
|
139
|
-
@retry_delay = extract_retry_delay(configuration)
|
140
144
|
@ssl_config = extract_ssl_config(configuration)
|
141
145
|
|
142
146
|
@name = configuration[:keyspace]
|
@@ -184,7 +188,7 @@ module Cequel
|
|
184
188
|
# @param bind_vars [Object] values for bind variables
|
185
189
|
# @return [Enumerable] the results of the query
|
186
190
|
#
|
187
|
-
# @see #
|
191
|
+
# @see #execute_with_options
|
188
192
|
#
|
189
193
|
def execute(statement, *bind_vars)
|
190
194
|
execute_with_options(Statement.new(statement, bind_vars), { consistency: default_consistency })
|
@@ -211,7 +215,7 @@ module Cequel
|
|
211
215
|
end
|
212
216
|
|
213
217
|
log('CQL', statement) do
|
214
|
-
|
218
|
+
error_policy.execute_stmt(self) do
|
215
219
|
client.execute(cql, options)
|
216
220
|
end
|
217
221
|
end
|
@@ -230,7 +234,7 @@ module Cequel
|
|
230
234
|
else
|
231
235
|
statement
|
232
236
|
end
|
233
|
-
|
237
|
+
error_policy.execute_stmt(self) do
|
234
238
|
client.prepare(cql)
|
235
239
|
end
|
236
240
|
end
|
@@ -241,15 +245,17 @@ module Cequel
|
|
241
245
|
# @return [void]
|
242
246
|
#
|
243
247
|
def clear_active_connections!
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
@cluster
|
252
|
-
|
248
|
+
synchronize do
|
249
|
+
if defined? @client
|
250
|
+
remove_instance_variable(:@client)
|
251
|
+
end
|
252
|
+
if defined? @client_without_keyspace
|
253
|
+
remove_instance_variable(:@client_without_keyspace)
|
254
|
+
end
|
255
|
+
if defined? @cluster
|
256
|
+
@cluster.close
|
257
|
+
remove_instance_variable(:@cluster)
|
258
|
+
end
|
253
259
|
end
|
254
260
|
end
|
255
261
|
|
@@ -308,21 +314,6 @@ module Cequel
|
|
308
314
|
def_delegator :lock, :synchronize
|
309
315
|
private :lock
|
310
316
|
|
311
|
-
def client_retry
|
312
|
-
retries = max_retries
|
313
|
-
begin
|
314
|
-
yield
|
315
|
-
rescue Cassandra::Errors::NoHostsAvailable,
|
316
|
-
Cassandra::Errors::ExecutionError,
|
317
|
-
Cassandra::Errors::TimeoutError
|
318
|
-
clear_active_connections!
|
319
|
-
raise if retries == 0
|
320
|
-
retries -= 1
|
321
|
-
sleep(retry_delay)
|
322
|
-
retry
|
323
|
-
end
|
324
|
-
end
|
325
|
-
|
326
317
|
def client_without_keyspace
|
327
318
|
synchronize do
|
328
319
|
@client_without_keyspace ||= cluster.connect
|
@@ -334,6 +325,7 @@ module Cequel
|
|
334
325
|
options.merge!(credentials) if credentials
|
335
326
|
options.merge!(ssl_config) if ssl_config
|
336
327
|
options.merge!(compression: client_compression) if client_compression
|
328
|
+
options.merge!(cassandra_options) if cassandra_options
|
337
329
|
end
|
338
330
|
end
|
339
331
|
|
@@ -373,14 +365,6 @@ module Cequel
|
|
373
365
|
configuration.slice(:username, :password).presence
|
374
366
|
end
|
375
367
|
|
376
|
-
def extract_max_retries(configuration)
|
377
|
-
configuration.fetch(:max_retries, 3)
|
378
|
-
end
|
379
|
-
|
380
|
-
def extract_retry_delay(configuration)
|
381
|
-
configuration.fetch(:retry_delay, 0.5)
|
382
|
-
end
|
383
|
-
|
384
368
|
def extract_ssl_config(configuration)
|
385
369
|
ssl_config = {}
|
386
370
|
ssl_config[:ssl] = configuration.fetch(:ssl, nil)
|
@@ -391,7 +375,24 @@ module Cequel
|
|
391
375
|
ssl_config.each { |key, value| ssl_config.delete(key) unless value }
|
392
376
|
ssl_config
|
393
377
|
end
|
394
|
-
|
378
|
+
|
379
|
+
def extract_cassandra_error_policy(configuration)
|
380
|
+
value = configuration.fetch(:cassandra_error_policy, ::Cequel::Metal::Policy::CassandraError::ClearAndRetryPolicy)
|
381
|
+
# Accept a class name as a string, create an instance of it
|
382
|
+
if value.is_a?(String)
|
383
|
+
value.constantize.new(configuration)
|
384
|
+
# Accept a class, instantiate it
|
385
|
+
elsif value.is_a?(Class)
|
386
|
+
value.new(configuration)
|
387
|
+
# Accept a value, assume it is a ready to use policy object
|
388
|
+
else
|
389
|
+
value
|
390
|
+
end
|
391
|
+
end
|
392
|
+
|
393
|
+
def extract_cassandra_options(configuration)
|
394
|
+
configuration[:cassandra_options]
|
395
|
+
end
|
395
396
|
end
|
396
397
|
end
|
397
398
|
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
module Cequel
|
3
|
+
module Metal
|
4
|
+
module Policy
|
5
|
+
module CassandraError
|
6
|
+
class ErrorPolicyBase
|
7
|
+
# This class is used by the Keyspace object to dictate
|
8
|
+
# how a failure from Cassandra is handled.
|
9
|
+
# The only method defined is
|
10
|
+
# execute_stmt(keyspace)
|
11
|
+
# The first argument is an instance of the Keyspace class
|
12
|
+
# This method may raise an error to abort the operation,
|
13
|
+
#
|
14
|
+
# The specific instance is chosen by passing configuration options
|
15
|
+
# See Keyspace#configure
|
16
|
+
|
17
|
+
# On instantiation, the configuraiton hash passed to Cequel is
|
18
|
+
# available here
|
19
|
+
def initialize(options = {})
|
20
|
+
end
|
21
|
+
|
22
|
+
def execute_stmt(keyspace)
|
23
|
+
raise NotImplementedError, "#execute_stmt must be implemented in #{self.class.name}"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class ClearAndRetryPolicy < ErrorPolicyBase
|
28
|
+
# @return Integer maximum number of retries to reconnect to Cassandra
|
29
|
+
attr_reader :max_retries
|
30
|
+
# @return Float delay between retries to reconnect to Cassandra
|
31
|
+
attr_reader :retry_delay
|
32
|
+
# @return Boolean if this policy clears connections before retry
|
33
|
+
attr_reader :clear_before_retry
|
34
|
+
def initialize(options = {})
|
35
|
+
@max_retries = options.fetch(:max_retries, 3)
|
36
|
+
@retry_delay = options.fetch(:retry_delay, 0.5)
|
37
|
+
@clear_before_retry = !!options.fetch(:clear_before_retry, true)
|
38
|
+
|
39
|
+
if @retry_delay <= 0.0
|
40
|
+
raise ArgumentError, "The value for retry must be a positive number, not '#{@retry_delay}'"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def execute_stmt(keyspace)
|
45
|
+
retries_remaining = max_retries
|
46
|
+
begin
|
47
|
+
yield
|
48
|
+
rescue Cassandra::Errors::NoHostsAvailable,
|
49
|
+
Cassandra::Errors::ExecutionError,
|
50
|
+
Cassandra::Errors::TimeoutError => exc
|
51
|
+
raise error if retries_remaining == 0
|
52
|
+
sleep(retry_delay)
|
53
|
+
keyspace.clear_active_connections! if clear_before_retry
|
54
|
+
retries_remaining -= 1
|
55
|
+
retry
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
module RetryPolicy
|
61
|
+
def self.new(options = {})
|
62
|
+
options[:clear_before_retry] = false
|
63
|
+
ClearAndRetryPolicy.new(options)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class RaisePolicy < ErrorPolicyBase
|
68
|
+
def execute_stmt(keyspace)
|
69
|
+
yield
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -173,7 +173,7 @@ module Cequel
|
|
173
173
|
# @param options [Options] options for save
|
174
174
|
# @option options [Boolean] :validate (true) whether to run validations
|
175
175
|
# before saving
|
176
|
-
# @option options [Symbol] :consistency
|
176
|
+
# @option options [Symbol] :consistency what consistency with
|
177
177
|
# which to persist the changes
|
178
178
|
# @option options [Integer] :ttl time-to-live of the updated rows in
|
179
179
|
# seconds
|
@@ -211,7 +211,7 @@ module Cequel
|
|
211
211
|
# Remove this record from the database
|
212
212
|
#
|
213
213
|
# @param options [Options] options for deletion
|
214
|
-
# @option options [Symbol] :consistency
|
214
|
+
# @option options [Symbol] :consistency what consistency with
|
215
215
|
# which to persist the deletion
|
216
216
|
# @option options [Time] :timestamp the writetime to use for the deletion
|
217
217
|
#
|
data/lib/cequel/record/schema.rb
CHANGED
@@ -80,8 +80,21 @@ module Cequel
|
|
80
80
|
# @return [void]
|
81
81
|
#
|
82
82
|
def synchronize_schema
|
83
|
+
reader = get_table_reader
|
84
|
+
return if reader.materialized_view?
|
83
85
|
Cequel::Schema::TableSynchronizer
|
84
|
-
.apply(connection,
|
86
|
+
.apply(connection, reader.read, table_schema)
|
87
|
+
end
|
88
|
+
|
89
|
+
#
|
90
|
+
# Return a {TableReader} instance
|
91
|
+
#
|
92
|
+
# @return [Schema::TableReader] object
|
93
|
+
#
|
94
|
+
def get_table_reader
|
95
|
+
fail MissingTableNameError unless table_name
|
96
|
+
|
97
|
+
connection.schema.get_table_reader(table_name)
|
85
98
|
end
|
86
99
|
|
87
100
|
#
|
@@ -92,9 +105,7 @@ module Cequel
|
|
92
105
|
# table in the database
|
93
106
|
#
|
94
107
|
def read_schema
|
95
|
-
|
96
|
-
|
97
|
-
connection.schema.read_table(table_name)
|
108
|
+
table_reader.read
|
98
109
|
end
|
99
110
|
|
100
111
|
#
|
@@ -118,6 +118,14 @@ module Cequel
|
|
118
118
|
TableReader.read(keyspace, name)
|
119
119
|
end
|
120
120
|
|
121
|
+
#
|
122
|
+
# @param name [Symbol] name of the table to read
|
123
|
+
# @return [TableReader] object
|
124
|
+
#
|
125
|
+
def get_table_reader(name)
|
126
|
+
TableReader.get(keyspace, name)
|
127
|
+
end
|
128
|
+
|
121
129
|
#
|
122
130
|
# Create a table in the keyspace
|
123
131
|
#
|
@@ -190,6 +198,16 @@ module Cequel
|
|
190
198
|
keyspace.execute("DROP TABLE #{name}")
|
191
199
|
end
|
192
200
|
|
201
|
+
#
|
202
|
+
# Drop this materialized view from the keyspace
|
203
|
+
#
|
204
|
+
# @param name [Symbol] name of the materialized view to drop
|
205
|
+
# @return [void]
|
206
|
+
#
|
207
|
+
def drop_materialized_view(name)
|
208
|
+
keyspace.execute("DROP MATERIALIZED VIEW #{name}")
|
209
|
+
end
|
210
|
+
|
193
211
|
#
|
194
212
|
# Create or update a table to match a given schema structure. The desired
|
195
213
|
# schema structure is defined by the directives given in the block; this
|
@@ -29,6 +29,16 @@ module Cequel
|
|
29
29
|
new(keyspace, table_name).read
|
30
30
|
end
|
31
31
|
|
32
|
+
#
|
33
|
+
# Return a {TableReader} instance
|
34
|
+
#
|
35
|
+
# @param (see #initialize)
|
36
|
+
# @return [TableReader] object
|
37
|
+
#
|
38
|
+
def self.get(keyspace, table_name)
|
39
|
+
new(keyspace, table_name)
|
40
|
+
end
|
41
|
+
|
32
42
|
#
|
33
43
|
# @param keyspace [Metal::Keyspace] keyspace to read the table from
|
34
44
|
# @param table_name [Symbol] name of the table to read
|
@@ -59,6 +69,18 @@ module Cequel
|
|
59
69
|
end
|
60
70
|
end
|
61
71
|
|
72
|
+
#
|
73
|
+
# Check if it is materialized view
|
74
|
+
#
|
75
|
+
# @return [boolean] true if it is materialized view
|
76
|
+
#
|
77
|
+
# @api private
|
78
|
+
#
|
79
|
+
def materialized_view?
|
80
|
+
cluster.keyspace(keyspace.name)
|
81
|
+
.has_materialized_view?(table_name.to_s)
|
82
|
+
end
|
83
|
+
|
62
84
|
protected
|
63
85
|
|
64
86
|
attr_reader :keyspace, :table_name, :table
|
@@ -150,18 +172,20 @@ module Cequel
|
|
150
172
|
end
|
151
173
|
|
152
174
|
def table_data
|
153
|
-
@table_data ||=
|
154
|
-
|
155
|
-
|
156
|
-
cluster.refresh_schema
|
175
|
+
@table_data ||= cluster.keyspace(keyspace.name)
|
176
|
+
.table(table_name.to_s)
|
177
|
+
end
|
157
178
|
|
158
|
-
|
159
|
-
|
179
|
+
def cluster
|
180
|
+
@cluster ||= begin
|
181
|
+
cluster = keyspace.cluster
|
182
|
+
cluster.refresh_schema
|
160
183
|
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
184
|
+
fail(NoSuchKeyspaceError, "No such keyspace #{keyspace.name}") if
|
185
|
+
!cluster.has_keyspace?(keyspace.name)
|
186
|
+
|
187
|
+
cluster
|
188
|
+
end
|
165
189
|
end
|
166
190
|
end
|
167
191
|
end
|
data/lib/cequel/type.rb
CHANGED
@@ -195,7 +195,7 @@ module Cequel
|
|
195
195
|
#
|
196
196
|
# `ascii` columns store 7-bit ASCII character data
|
197
197
|
#
|
198
|
-
# @see
|
198
|
+
# @see https://cassandra.apache.org/doc/latest/cql/types.html
|
199
199
|
# CQL3 data type documentation
|
200
200
|
#
|
201
201
|
class Ascii < String
|
@@ -215,7 +215,7 @@ module Cequel
|
|
215
215
|
# `blob` columns store arbitrary bytes of data, represented as 8-bit ASCII
|
216
216
|
# strings of hex digits
|
217
217
|
#
|
218
|
-
# @see
|
218
|
+
# @see https://cassandra.apache.org/doc/latest/cql/types.html
|
219
219
|
# CQL3 data type documentation
|
220
220
|
#
|
221
221
|
class Blob < String
|
@@ -239,7 +239,7 @@ module Cequel
|
|
239
239
|
#
|
240
240
|
# `boolean` types store boolean values
|
241
241
|
#
|
242
|
-
# @see
|
242
|
+
# @see https://cassandra.apache.org/doc/latest/cql/types.html
|
243
243
|
# CQL3 data type documentation
|
244
244
|
#
|
245
245
|
class Boolean < Base
|
@@ -256,7 +256,7 @@ module Cequel
|
|
256
256
|
# counter columns cannot be updated without Cassandra internally reading
|
257
257
|
# the existing state of the column
|
258
258
|
#
|
259
|
-
# @see
|
259
|
+
# @see https://cassandra.apache.org/doc/latest/cql/types.html
|
260
260
|
# CQL3 data type documentation
|
261
261
|
#
|
262
262
|
class Counter < Base
|
@@ -277,7 +277,7 @@ module Cequel
|
|
277
277
|
#
|
278
278
|
# `decimal` columns store decimal numeric values
|
279
279
|
#
|
280
|
-
# @see
|
280
|
+
# @see https://cassandra.apache.org/doc/latest/cql/types.html
|
281
281
|
# CQL3 data type documentation
|
282
282
|
#
|
283
283
|
class Decimal < Base
|
@@ -290,7 +290,7 @@ module Cequel
|
|
290
290
|
#
|
291
291
|
# `double` columns store 64-bit floating-point numeric values
|
292
292
|
#
|
293
|
-
# @see
|
293
|
+
# @see https://cassandra.apache.org/doc/latest/cql/types.html
|
294
294
|
# CQL3 data type documentation
|
295
295
|
#
|
296
296
|
class Double < Base
|
@@ -303,7 +303,7 @@ module Cequel
|
|
303
303
|
#
|
304
304
|
# `inet` columns store IP addresses
|
305
305
|
#
|
306
|
-
# @see
|
306
|
+
# @see https://cassandra.apache.org/doc/latest/cql/types.html
|
307
307
|
# CQL3 data type documentation
|
308
308
|
#
|
309
309
|
class Inet < Base
|
@@ -316,7 +316,7 @@ module Cequel
|
|
316
316
|
#
|
317
317
|
# `int` columns store 32-bit integer values
|
318
318
|
#
|
319
|
-
# @see
|
319
|
+
# @see https://cassandra.apache.org/doc/latest/cql/types.html
|
320
320
|
# CQL3 data type documentation
|
321
321
|
#
|
322
322
|
class Int < Base
|
@@ -333,7 +333,7 @@ module Cequel
|
|
333
333
|
#
|
334
334
|
# `float` columns store 32-bit floating-point numeric values
|
335
335
|
#
|
336
|
-
# @see
|
336
|
+
# @see https://cassandra.apache.org/doc/latest/cql/types.html
|
337
337
|
# CQL3 data type documentation
|
338
338
|
#
|
339
339
|
class Float < Double; end
|
@@ -342,7 +342,7 @@ module Cequel
|
|
342
342
|
#
|
343
343
|
# `bigint` columns store 64-bit integer values
|
344
344
|
#
|
345
|
-
# @see
|
345
|
+
# @see https://cassandra.apache.org/doc/latest/cql/types.html
|
346
346
|
# CQL3 data type documentation
|
347
347
|
#
|
348
348
|
class Bigint < Int
|
@@ -357,7 +357,7 @@ module Cequel
|
|
357
357
|
# `varchar` columns; the names can be used interchangeably. Text columns do
|
358
358
|
# not have a length limit
|
359
359
|
#
|
360
|
-
# @see
|
360
|
+
# @see https://cassandra.apache.org/doc/latest/cql/types.html
|
361
361
|
# CQL3 data type documentation
|
362
362
|
#
|
363
363
|
class Text < String
|
@@ -406,7 +406,7 @@ module Cequel
|
|
406
406
|
# created using the {Cequel.uuid} method, and a value can be checked to see
|
407
407
|
# if it is a UUID recognized by Cequel using the {Cequel.uuid?} method.
|
408
408
|
#
|
409
|
-
# @see
|
409
|
+
# @see https://cassandra.apache.org/doc/latest/cql/types.html
|
410
410
|
# CQL3 data type documentation
|
411
411
|
#
|
412
412
|
class Uuid < Base
|
@@ -435,7 +435,7 @@ module Cequel
|
|
435
435
|
# functionality presumes the use of type 1 UUIDs, which encode the
|
436
436
|
# timestamp of their creation.
|
437
437
|
#
|
438
|
-
# @see
|
438
|
+
# @see https://cassandra.apache.org/doc/latest/cql/types.html
|
439
439
|
# CQL3 data type documentation
|
440
440
|
#
|
441
441
|
class Timeuuid < Uuid
|
@@ -452,7 +452,7 @@ module Cequel
|
|
452
452
|
#
|
453
453
|
# `varint` columns store arbitrary-length integer data
|
454
454
|
#
|
455
|
-
# @see
|
455
|
+
# @see https://cassandra.apache.org/doc/latest/cql/types.html
|
456
456
|
# CQL3 data type documentation
|
457
457
|
#
|
458
458
|
class Varint < Int
|
data/lib/cequel/version.rb
CHANGED
@@ -115,6 +115,100 @@ describe Cequel::Metal::Keyspace do
|
|
115
115
|
expect(connect.client_compression).to eq client_compression
|
116
116
|
end
|
117
117
|
end
|
118
|
+
|
119
|
+
describe '#cassandra_options' do
|
120
|
+
let(:cassandra_options) { {foo: :bar} }
|
121
|
+
let(:connect) do
|
122
|
+
Cequel.connect host: Cequel::SpecSupport::Helpers.host,
|
123
|
+
port: Cequel::SpecSupport::Helpers.port,
|
124
|
+
cassandra_options: cassandra_options
|
125
|
+
end
|
126
|
+
it 'passes the cassandra options as part of the client options' do
|
127
|
+
expect(connect.send(:client_options)).to have_key(:foo)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
describe 'cassandra error handling' do
|
132
|
+
let(:connect_options) do
|
133
|
+
{
|
134
|
+
host: Cequel::SpecSupport::Helpers.host,
|
135
|
+
port: Cequel::SpecSupport::Helpers.port
|
136
|
+
}
|
137
|
+
end
|
138
|
+
|
139
|
+
let(:default_connect) do
|
140
|
+
Cequel.connect(connect_options)
|
141
|
+
end
|
142
|
+
|
143
|
+
class SpecCassandraErrorHandler
|
144
|
+
def initialize(options = {})
|
145
|
+
end
|
146
|
+
|
147
|
+
def execute_stmt(keyspace)
|
148
|
+
yield
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
it 'uses the error handler passed in as a string' do
|
153
|
+
obj = Cequel.connect connect_options.merge(
|
154
|
+
cassandra_error_policy: 'SpecCassandraErrorHandler')
|
155
|
+
|
156
|
+
expect(obj.error_policy.class).to equal(SpecCassandraErrorHandler)
|
157
|
+
end
|
158
|
+
|
159
|
+
it 'uses the error handler passed in as a module' do
|
160
|
+
obj = Cequel.connect connect_options.merge(
|
161
|
+
cassandra_error_policy: SpecCassandraErrorHandler)
|
162
|
+
|
163
|
+
expect(obj.error_policy.class).to equal(SpecCassandraErrorHandler)
|
164
|
+
end
|
165
|
+
|
166
|
+
it 'uses the instance of an error handler passed in' do
|
167
|
+
policy = SpecCassandraErrorHandler.new
|
168
|
+
|
169
|
+
obj = Cequel.connect connect_options.merge(
|
170
|
+
cassandra_error_policy: policy)
|
171
|
+
|
172
|
+
expect(obj.error_policy).to equal(policy)
|
173
|
+
end
|
174
|
+
|
175
|
+
it 'responds to error policy' do
|
176
|
+
# Always defined, even if config does not specify it
|
177
|
+
expect(default_connect).to respond_to(:error_policy)
|
178
|
+
end
|
179
|
+
|
180
|
+
it 'calls execute_stmt on the error policy' do
|
181
|
+
policy = ::Cequel::Metal::Policy::CassandraError::RetryPolicy.new
|
182
|
+
|
183
|
+
obj = Cequel.connect connect_options.merge(
|
184
|
+
cassandra_error_policy: policy)
|
185
|
+
expect(policy).to receive(:execute_stmt).at_least(:once)
|
186
|
+
obj.execute_with_options(Cequel::Metal::Statement.new('select * from system.peers;'))
|
187
|
+
end
|
188
|
+
|
189
|
+
it 'rejects a negative value for retry delay' do
|
190
|
+
expect { Cequel.connect connect_options.merge(
|
191
|
+
retry_delay: -1.0)
|
192
|
+
}.to raise_error(ArgumentError)
|
193
|
+
end
|
194
|
+
|
195
|
+
it 'accepts a configured value for retry delay' do
|
196
|
+
obj = Cequel.connect connect_options.merge(
|
197
|
+
retry_delay: 1337.0)
|
198
|
+
|
199
|
+
# do not compare floats exactly, it is error prone
|
200
|
+
# the value is passed to the error policy
|
201
|
+
expect(obj.error_policy.retry_delay).to be_within(0.1).of(1337.0)
|
202
|
+
end
|
203
|
+
|
204
|
+
it 'can clear active connections' do
|
205
|
+
expect {
|
206
|
+
default_connect.clear_active_connections!
|
207
|
+
}.to change {
|
208
|
+
default_connect.client
|
209
|
+
}
|
210
|
+
end
|
211
|
+
end
|
118
212
|
|
119
213
|
describe "#execute" do
|
120
214
|
let(:statement) { "SELECT id FROM posts" }
|
@@ -396,4 +396,40 @@ describe Cequel::Schema::TableReader do
|
|
396
396
|
[Cequel::Schema::DataColumn.new(:value, :text)]
|
397
397
|
) }
|
398
398
|
end
|
399
|
+
|
400
|
+
describe 'materialized view exists', thrift: true do
|
401
|
+
let!(:name) { table_name }
|
402
|
+
let(:view_name) { "#{name}_view" }
|
403
|
+
before do
|
404
|
+
cequel.execute <<-CQL
|
405
|
+
CREATE TABLE #{table_name} (
|
406
|
+
blog_subdomain text,
|
407
|
+
permalink ascii,
|
408
|
+
PRIMARY KEY (blog_subdomain, permalink)
|
409
|
+
)
|
410
|
+
CQL
|
411
|
+
cequel.execute <<-CQL
|
412
|
+
CREATE MATERIALIZED VIEW #{view_name} AS
|
413
|
+
SELECT blog_subdomain, permalink
|
414
|
+
FROM #{name}
|
415
|
+
WHERE blog_subdomain IS NOT NULL AND permalink IS NOT NULL
|
416
|
+
PRIMARY KEY ( blog_subdomain, permalink )
|
417
|
+
CQL
|
418
|
+
end
|
419
|
+
after do
|
420
|
+
cequel.schema.drop_materialized_view(view_name)
|
421
|
+
end
|
422
|
+
|
423
|
+
context 'when materialized_view' do
|
424
|
+
let(:reader) { cequel.schema.get_table_reader(view_name) }
|
425
|
+
subject { reader }
|
426
|
+
its(:materialized_view?) { should eq true }
|
427
|
+
end
|
428
|
+
|
429
|
+
context 'when table' do
|
430
|
+
let(:reader) { cequel.schema.get_table_reader(name) }
|
431
|
+
subject { reader }
|
432
|
+
its(:materialized_view?) { should eq false }
|
433
|
+
end
|
434
|
+
end
|
399
435
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cequel
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.
|
4
|
+
version: 2.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mat Brown
|
@@ -29,7 +29,7 @@ authors:
|
|
29
29
|
autorequire:
|
30
30
|
bindir: bin
|
31
31
|
cert_chain: []
|
32
|
-
date:
|
32
|
+
date: 2017-01-06 00:00:00.000000000 Z
|
33
33
|
dependencies:
|
34
34
|
- !ruby/object:Gem::Dependency
|
35
35
|
name: activemodel
|
@@ -219,6 +219,7 @@ files:
|
|
219
219
|
- lib/cequel/metal/keyspace.rb
|
220
220
|
- lib/cequel/metal/logging.rb
|
221
221
|
- lib/cequel/metal/new_relic_instrumentation.rb
|
222
|
+
- lib/cequel/metal/policy/cassandra_error.rb
|
222
223
|
- lib/cequel/metal/request_logger.rb
|
223
224
|
- lib/cequel/metal/row.rb
|
224
225
|
- lib/cequel/metal/row_specification.rb
|