cequel 2.0.2 → 2.0.3
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/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
|