cassandra-driver 1.1.1 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- MjJiMTUxNjdlNWI3Y2M3Mzk3NzFiMjQ4NDEzMGNlMTJiYmQyM2Q2MA==
4
+ NzBmYWI0MTgyNjhhMWU2MGNjMjg0NWQzM2M5ODdkZTg4MWVkZjIzMQ==
5
5
  data.tar.gz: !binary |-
6
- ZTIzZjE2NzFjNTI5YTEyZDUxMWUxMjdiYzEwOTg2Nzc0YzhjZDFjZA==
6
+ ZmNlZTUxNGEwNDBmYTdiM2U1N2U1MDUzODRkZDM0ZjBjNTA3YzRkOA==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- YmNiNDQxMmI3OTdhN2FhOTA3Njc2NjlhMmE1ZTkzNTRmOWZkNjVjNTRiZTc5
10
- MjViMDFkMWU1NzRhODIxY2UxMWUxNGVjMGE1N2Y0ZTZlMzQ1NDA5YjFlNDcw
11
- MDk3ZTdjNjM2YjUyMjIwOTZmY2VkN2M4OTY3NGVmNWUzYTgwZGM=
9
+ Nzg5OGIzNzVlNzkyY2U4MWM3NjQwNTIwMDRkNWM0M2EwYjliYmM2MDg1NjA3
10
+ NjZhYmEzYjBiNTdkMWY1MWM2OWY5OTJmYmEzNWMzMzdiYjliNTM4NDE1OWM4
11
+ MDc3N2Y1YWFhMDI3MzU3NjQxODYyZGExMDU4YjM5ODYwYTkyMzE=
12
12
  data.tar.gz: !binary |-
13
- YmMwN2E5NmIyNWJlNTE3ZjEyMzM0MDI4MmU0NzFlMTM5MGQ5YjkzYWE3ZTk4
14
- OGI2NDZjNTI0MzA1NjdlMjUxYTBiMTFkY2VmZTZmNWEwZjk1MWFiMjlmNDc3
15
- ZmViMDVkNjFkMGU3ZGNmYTgwZjg2YzkxODI5YTE0NzE4NzMxMGI=
13
+ MjVhYTZmZjEwYjFjMDY5ZTU2NzIzYmQ1ZGU3MzE3OWMxYTMzMzYwN2NjZmMx
14
+ ZGFiZGI2NTg1ZjJhMzlmYTc4MDI1MmUxNWI0ZjA5YjVmODVlYmRlOWQzYmEy
15
+ YWM5NWEwNjE0ODM4YWFiYmY1OTFjZDJlYzIyZmU4MmQ1YWRkNDY=
data/README.md CHANGED
@@ -93,16 +93,12 @@ __Note__: if you want to use compression you should also install [snappy](http:/
93
93
 
94
94
  Some of the new features added to the driver have unfortunately led to changes in the original cql-rb API. In the examples directory, you can find [an example of how to wrap the ruby driver to achieve almost complete interface parity with cql-rb](https://github.com/datastax/ruby-driver/blob/master/examples/cql-rb-wrapper.rb) to assist you with gradual upgrade.
95
95
 
96
- ## What's new in v1.1.1
96
+ ## What's new in v1.2.0
97
97
 
98
- Current release introduces the following new features:
98
+ Bug Fixes:
99
99
 
100
- * Ability to disable automatic schema synchronization
101
- * Schema change event storm protection using a sliding delay
102
- * [`Cassandra::LoadBalancing::Policy#teardown`](http://datastax.github.io/ruby-driver/api/load_balancing/policy/#teardown-instance_method) for cleaning up resources
103
- * [`Cassandra::Cluster#refresh_schema`](http://datastax.github.io/ruby-driver/api/cluster/#refresh_schema-instance_method) for manually refreshing schema metadata
104
- * Host list randomization to prevent hotspots between multiple clients
105
- * Future listeners run in a dedicated threadpool to not block the reactor
100
+ * [RUBY-83] Timestamps loses microseconds when retrieved from database
101
+ * [RUBY-85] Driver doesn't always reconnect
106
102
 
107
103
  ## Code examples
108
104
 
@@ -147,6 +143,10 @@ Prereleases will be stable, in the sense that they will have finished and proper
147
143
 
148
144
  Please [refer to the usage documentation for more information on common pitfalls](http://datastax.github.io/ruby-driver/features/)
149
145
 
146
+ ## Contributing
147
+
148
+ For contributing read [CONTRIBUTING.md](https://github.com/datastax/ruby-driver/blob/master/README.md)
149
+
150
150
  ## Credits
151
151
 
152
152
  This driver is based on the original work of [Theo Hultberg](https://github.com/iconara) on [cql-rb](https://github.com/iconara/cql-rb/) and adds a series of advanced features that are common across all other DataStax drivers for Apache Cassandra.
@@ -36,10 +36,10 @@ module Cassandra
36
36
  @address_resolver = address_resolution_policy
37
37
  @connection_options = connection_options
38
38
  @futures = futures_factory
39
- @connecting_hosts = ::Hash.new
40
39
  @connections = ::Hash.new
41
40
  @prepared_statements = ::Hash.new
42
41
  @preparing_statements = ::Hash.new
42
+ @pending_connections = ::Hash.new
43
43
  @keyspace = nil
44
44
  @state = :idle
45
45
 
@@ -47,6 +47,8 @@ module Cassandra
47
47
  end
48
48
 
49
49
  def connect
50
+ connecting_hosts = ::Hash.new
51
+
50
52
  synchronize do
51
53
  return CLIENT_CLOSED if @state == :closed || @state == :closing
52
54
  return @connected_future if @state == :connecting || @state == :connected
@@ -54,9 +56,24 @@ module Cassandra
54
56
  @state = :connecting
55
57
  @registry.each_host do |host|
56
58
  distance = @load_balancing_policy.distance(host)
57
- next if distance == :ignore
58
59
 
59
- @connecting_hosts[host] = distance
60
+ case distance
61
+ when :ignore
62
+ next
63
+ when :local
64
+ pool_size = @connection_options.connections_per_local_node
65
+ when :remote
66
+ pool_size = @connection_options.connections_per_remote_node
67
+ else
68
+ @logger.error("Not connecting to #{host.ip} - invalid load balancing distance. Distance must be one of #{LoadBalancing::DISTANCES.inspect}, #{distance.inspect} given")
69
+ next
70
+ end
71
+
72
+ connecting_hosts[host] = pool_size
73
+ @pending_connections[host] = 0
74
+ @prepared_statements[host] = {}
75
+ @preparing_statements[host] = {}
76
+ @connections[host] = ConnectionPool.new
60
77
  end
61
78
  end
62
79
 
@@ -65,8 +82,8 @@ module Cassandra
65
82
  @registry.add_listener(self)
66
83
  @schema.add_listener(self)
67
84
 
68
- futures = @connecting_hosts.map do |(host, distance)|
69
- f = connect_to_host(host, distance)
85
+ futures = connecting_hosts.map do |(host, pool_size)|
86
+ f = connect_to_host(host, pool_size)
70
87
  f.recover do |error|
71
88
  FailedConnection.new(error, host)
72
89
  end
@@ -125,30 +142,40 @@ module Cassandra
125
142
  end
126
143
 
127
144
  def host_up(host)
128
- distance = nil
145
+ pool_size = 0
129
146
 
130
147
  synchronize do
131
- return Ione::Future.resolved if @connecting_hosts.include?(host)
132
-
133
148
  distance = @load_balancing_policy.distance(host)
134
- return Ione::Future.resolved if distance == :ignore
149
+ case distance
150
+ when :ignore
151
+ return Ione::Future.resolved
152
+ when :local
153
+ pool_size = @connection_options.connections_per_local_node
154
+ when :remote
155
+ pool_size = @connection_options.connections_per_remote_node
156
+ else
157
+ @logger.error("Not connecting to #{host.ip} - invalid load balancing distance. Distance must be one of #{LoadBalancing::DISTANCES.inspect}, #{distance.inspect} given")
158
+ return Ione::Future.resolved
159
+ end
135
160
 
136
- @connecting_hosts[host] = distance
161
+ @pending_connections[host] ||= 0
162
+ @prepared_statements[host] = {}
163
+ @preparing_statements[host] = {}
164
+ @connections[host] = ConnectionPool.new
137
165
  end
138
166
 
139
- connect_to_host_maybe_retry(host, distance).map(nil)
167
+ connect_to_host_maybe_retry(host, pool_size)
140
168
  end
141
169
 
142
170
  def host_down(host)
143
171
  pool = nil
144
172
 
145
173
  synchronize do
146
- return Ione::Future.resolved if !@connections.has_key?(host) && !@connecting_hosts.include?(host)
174
+ return Ione::Future.resolved unless @connections.has_key?(host)
147
175
 
148
- @connecting_hosts.delete(host)
176
+ @pending_connections.delete(host) unless @pending_connections[host] > 0
149
177
  @prepared_statements.delete(host)
150
178
  @preparing_statements.delete(host)
151
-
152
179
  pool = @connections.delete(host)
153
180
  end
154
181
 
@@ -299,93 +326,89 @@ module Cassandra
299
326
  Ione::Future.all(*futures).map(self)
300
327
  end
301
328
 
302
- def connect_to_host_maybe_retry(host, distance)
303
- f = connect_to_host(host, distance)
304
-
305
- f.on_failure do |e|
306
- connect_to_host_with_retry(host, @reconnection_policy.schedule)
307
- end
308
-
309
- f
329
+ def connect_to_host_maybe_retry(host, pool_size)
330
+ connect_to_host(host, pool_size).fallback do |e|
331
+ @logger.error("Scheduling initial connection retry to #{host.ip} (#{e.class.name}: #{e.message})")
332
+ connect_to_host_with_retry(host, pool_size, @reconnection_policy.schedule)
333
+ end.map(nil)
310
334
  end
311
335
 
312
- def connect_to_host_with_retry(host, schedule)
336
+ def connect_to_host_with_retry(host, pool_size, schedule)
313
337
  interval = schedule.next
314
338
 
315
339
  @logger.debug("Reconnecting to #{host.ip} in #{interval} seconds")
316
340
 
317
341
  f = @reactor.schedule_timer(interval)
318
342
  f.flat_map do
319
- distance = nil
320
-
321
- synchronize do
322
- if @connecting_hosts.include?(host)
323
- distance = @load_balancing_policy.distance(host)
324
- end
325
- end
326
-
327
- if distance && distance != :ignore
328
- connect_to_host(host, distance).fallback do |e|
329
- connect_to_host_with_retry(host, schedule)
330
- end
331
- else
332
- NO_CONNECTIONS
343
+ connect_to_host(host, pool_size).fallback do |e|
344
+ @logger.error("Scheduling connection retry to #{host.ip} (#{e.class.name}: #{e.message})")
345
+ connect_to_host_with_retry(host, pool_size, schedule)
333
346
  end
334
347
  end
335
348
  end
336
349
 
337
- def connect_to_host(host, distance)
338
- case distance
339
- when :ignore
340
- return NO_CONNECTIONS
341
- when :local
342
- pool_size = @connection_options.connections_per_local_node
343
- when :remote
344
- pool_size = @connection_options.connections_per_remote_node
345
- else
346
- @logger.error("Invalid load balancing distance, not connecting to #{host.ip}. Distance must be one of #{LoadBalancing::DISTANCES.inspect}, #{distance.inspect} given")
347
- return NO_CONNECTIONS
348
- end
349
-
350
- pool = nil
351
- existing_connections = 0
350
+ def connect_to_host(host, pool_size)
351
+ size = 0
352
352
 
353
353
  synchronize do
354
+ unless @connections.include?(host)
355
+ @logger.info("Not connecting to #{host.ip} - host is currently down")
356
+ return NO_CONNECTIONS
357
+ end
358
+
354
359
  pool = @connections[host]
355
- end
360
+ size = pool_size - pool.size
356
361
 
357
- existing_connections = pool.size if pool
358
- pool = nil
359
- missing_connections = (pool_size - existing_connections)
360
- return Ione::Future.resolved if missing_connections <= 0
362
+ if size <= 0
363
+ @logger.info("Not connecting to #{host.ip} - host is already fully connected")
364
+ return NO_CONNECTIONS
365
+ end
366
+
367
+ size -= @pending_connections[host]
361
368
 
362
- f = @connector.connect_many(host, missing_connections)
369
+ if size <= 0
370
+ @logger.info("Not connecting to #{host.ip} - host is already pending connections")
371
+ return NO_CONNECTIONS
372
+ end
373
+
374
+ @pending_connections[host] += size
375
+ end
376
+
377
+ @logger.debug("Creating #{size} connections to #{host.ip}")
378
+ f = @connector.connect_many(host, size)
363
379
 
364
380
  f.on_value do |connections|
381
+ @logger.debug("Created #{connections.size} connections to #{host.ip}")
382
+
365
383
  pool = nil
366
384
 
367
385
  synchronize do
368
- @connecting_hosts.delete(host)
369
- @prepared_statements[host] = {}
370
- @preparing_statements[host] = {}
371
- pool = @connections[host] ||= ConnectionPool.new
372
- end
386
+ @pending_connections[host] -= size
373
387
 
374
- pool.add_connections(connections)
388
+ if @connections.include?(host)
389
+ pool = @connections[host]
390
+ else
391
+ @pending_connections.delete(host) unless @pending_connections[host] > 0
392
+ end
393
+ end
375
394
 
376
- connections.each do |connection|
377
- connection.on_closed do
378
- distance = nil
395
+ if pool
396
+ pool.add_connections(connections)
379
397
 
380
- synchronize do
381
- if !(@state == :closed || @state == :closing) && !@connecting_hosts.include?(host) && @connections.include?(host)
382
- distance = @load_balancing_policy.distance(host)
383
- @connecting_hosts[host] = distance unless distance == :ignore
384
- end
398
+ connections.each do |connection|
399
+ connection.on_closed do |cause|
400
+ connect_to_host_maybe_retry(host, pool_size) if cause
385
401
  end
386
-
387
- connect_to_host_maybe_retry(host, distance).map(nil) if distance
388
402
  end
403
+ else
404
+ connections.each {|c| c.close}
405
+ end
406
+ end
407
+
408
+ f.on_failure do |error|
409
+ synchronize do
410
+ @pending_connections[host] -= size
411
+ @pending_connections.delete(host) unless @pending_connections[host] > 0 || @connections.include?(host)
389
412
  end
390
413
  end
391
414
 
@@ -116,12 +116,12 @@ module Cassandra
116
116
  UNCLAIMED_TIMEOUT = 5 # close unclaimed connections in five seconds
117
117
 
118
118
  def do_connect(host)
119
- f = @reactor.connect(host.ip.to_s, @connection_options.port, {:timeout => @connection_options.connect_timeout, :ssl => @connection_options.ssl}) do |connection|
119
+ @reactor.connect(host.ip.to_s, @connection_options.port, {:timeout => @connection_options.connect_timeout, :ssl => @connection_options.ssl}) do |connection|
120
120
  raise Errors::ClientError, 'Not connected, reactor stopped' unless connection
121
121
  Protocol::CqlProtocolHandler.new(connection, @reactor, @connection_options.protocol_version, @connection_options.compressor, @connection_options.heartbeat_interval, @connection_options.idle_timeout)
122
- end
123
- f = f.flat_map do |connection|
124
- request_options(connection).flat_map do |options|
122
+ end.flat_map do |connection|
123
+ f = request_options(connection)
124
+ f = f.flat_map do |options|
125
125
  compression = @connection_options.compression
126
126
  supported_algorithms = options['COMPRESSION']
127
127
 
@@ -135,19 +135,30 @@ module Cassandra
135
135
 
136
136
  startup_connection(connection, cql_version, compression)
137
137
  end
138
- end
139
- f.fallback do |error|
140
- case error
141
- when Errors::ProtocolError
142
- synchronize do
143
- if @connection_options.protocol_version > 1
144
- @logger.info("Host #{host.ip} doesn't support protocol version #{@connection_options.protocol_version}, downgrading")
145
- @connection_options.protocol_version -= 1
146
- do_connect(host)
147
- else
148
- Ione::Future.failed(error)
138
+ f.fallback do |error|
139
+ case error
140
+ when Errors::ProtocolError
141
+ synchronize do
142
+ if @connection_options.protocol_version > 1
143
+ @logger.info("Host #{host.ip} doesn't support protocol version #{@connection_options.protocol_version}, downgrading")
144
+ @connection_options.protocol_version -= 1
145
+ do_connect(host)
146
+ else
147
+ Ione::Future.failed(error)
148
+ end
149
149
  end
150
+ when Errors::TimeoutError
151
+ future = Ione::CompletableFuture.new
152
+ connection.close(error).on_complete do |f|
153
+ future.fail(error)
154
+ end
155
+ future
156
+ else
157
+ Ione::Future.failed(error)
150
158
  end
159
+ end
160
+ end.fallback do |error|
161
+ case error
151
162
  when Error
152
163
  Ione::Future.failed(error)
153
164
  else
@@ -337,7 +337,7 @@ module Cassandra
337
337
  @request_queue_out.clear
338
338
  end
339
339
  promises_to_fail.each do |promise|
340
- promise.fail(request_failure_cause)
340
+ promise.fail(request_failure_cause) unless promise.timed_out?
341
341
  end
342
342
  if cause
343
343
  @closed_promise.fail(cause)
@@ -188,7 +188,11 @@ module Cassandra
188
188
  def bytes_to_timestamp(buffer, size_bytes)
189
189
  return nil unless read_size(buffer, size_bytes)
190
190
  timestamp = buffer.read_long
191
- Time.at(timestamp/1000.0)
191
+
192
+ seconds = timestamp / 1_000
193
+ microsenconds = (timestamp % 1_000) * 1_000
194
+
195
+ Time.at(seconds, microsenconds)
192
196
  end
193
197
 
194
198
  def bytes_to_varchar(buffer, size_bytes)
@@ -339,7 +343,7 @@ module Cassandra
339
343
 
340
344
  def timestamp_to_bytes(buffer, value, size_bytes)
341
345
  if value
342
- ms = (value.to_f * 1000).to_i
346
+ ms = (value.to_r.to_f * 1000).to_i
343
347
  size_to_bytes(buffer, 8, size_bytes)
344
348
  buffer.append_long(ms)
345
349
  else
@@ -382,4 +386,4 @@ module Cassandra
382
386
  end
383
387
  end
384
388
  end
385
- end
389
+ end
@@ -17,5 +17,5 @@
17
17
  #++
18
18
 
19
19
  module Cassandra
20
- VERSION = '1.1.1'.freeze
20
+ VERSION = '1.2.0'.freeze
21
21
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cassandra-driver
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Theo Hultberg
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-12-15 00:00:00.000000000 Z
12
+ date: 2015-01-22 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: ione