cassandra-driver 3.0.0.beta.1-java → 3.0.0-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (138) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +106 -39
  3. data/lib/cassandra.rb +396 -148
  4. data/lib/cassandra/address_resolution.rb +1 -1
  5. data/lib/cassandra/address_resolution/policies/ec2_multi_region.rb +1 -1
  6. data/lib/cassandra/address_resolution/policies/none.rb +1 -1
  7. data/lib/cassandra/aggregate.rb +21 -7
  8. data/lib/cassandra/argument.rb +2 -2
  9. data/lib/cassandra/attr_boolean.rb +33 -0
  10. data/lib/cassandra/auth.rb +6 -5
  11. data/lib/cassandra/auth/providers.rb +1 -1
  12. data/lib/cassandra/auth/providers/password.rb +5 -13
  13. data/lib/cassandra/cassandra_logger.rb +80 -0
  14. data/lib/cassandra/cluster.rb +49 -9
  15. data/lib/cassandra/cluster/client.rb +835 -209
  16. data/lib/cassandra/cluster/connection_pool.rb +2 -2
  17. data/lib/cassandra/cluster/connector.rb +86 -27
  18. data/lib/cassandra/cluster/control_connection.rb +222 -95
  19. data/lib/cassandra/cluster/failed_connection.rb +1 -1
  20. data/lib/cassandra/cluster/metadata.rb +14 -8
  21. data/lib/cassandra/cluster/options.rb +68 -22
  22. data/lib/cassandra/cluster/registry.rb +81 -17
  23. data/lib/cassandra/cluster/schema.rb +70 -8
  24. data/lib/cassandra/cluster/schema/cql_type_parser.rb +15 -10
  25. data/lib/cassandra/cluster/schema/fetchers.rb +601 -241
  26. data/lib/cassandra/cluster/schema/fqcn_type_parser.rb +39 -38
  27. data/lib/cassandra/cluster/schema/partitioners.rb +1 -1
  28. data/lib/cassandra/cluster/schema/partitioners/murmur3.rb +6 -8
  29. data/lib/cassandra/cluster/schema/partitioners/ordered.rb +1 -1
  30. data/lib/cassandra/cluster/schema/partitioners/random.rb +1 -1
  31. data/lib/cassandra/cluster/schema/replication_strategies.rb +1 -1
  32. data/lib/cassandra/cluster/schema/replication_strategies/network_topology.rb +19 -18
  33. data/lib/cassandra/cluster/schema/replication_strategies/none.rb +1 -1
  34. data/lib/cassandra/cluster/schema/replication_strategies/simple.rb +1 -1
  35. data/lib/cassandra/column.rb +4 -23
  36. data/lib/cassandra/column_container.rb +322 -0
  37. data/lib/cassandra/compression.rb +1 -1
  38. data/lib/cassandra/compression/compressors/lz4.rb +7 -8
  39. data/lib/cassandra/compression/compressors/snappy.rb +4 -3
  40. data/lib/cassandra/driver.rb +107 -46
  41. data/lib/cassandra/errors.rb +303 -52
  42. data/lib/cassandra/execution/info.rb +16 -5
  43. data/lib/cassandra/execution/options.rb +102 -55
  44. data/lib/cassandra/execution/trace.rb +16 -9
  45. data/lib/cassandra/executors.rb +1 -1
  46. data/lib/cassandra/function.rb +19 -13
  47. data/lib/cassandra/function_collection.rb +85 -0
  48. data/lib/cassandra/future.rb +101 -49
  49. data/lib/cassandra/host.rb +25 -5
  50. data/lib/cassandra/index.rb +118 -0
  51. data/lib/cassandra/keyspace.rb +169 -33
  52. data/lib/cassandra/listener.rb +1 -1
  53. data/lib/cassandra/load_balancing.rb +2 -2
  54. data/lib/cassandra/load_balancing/policies.rb +1 -1
  55. data/lib/cassandra/load_balancing/policies/dc_aware_round_robin.rb +39 -25
  56. data/lib/cassandra/load_balancing/policies/round_robin.rb +8 -1
  57. data/lib/cassandra/load_balancing/policies/token_aware.rb +22 -13
  58. data/lib/cassandra/load_balancing/policies/white_list.rb +18 -5
  59. data/lib/cassandra/materialized_view.rb +90 -0
  60. data/lib/cassandra/null_logger.rb +27 -6
  61. data/lib/cassandra/protocol.rb +1 -1
  62. data/lib/cassandra/protocol/coder.rb +81 -42
  63. data/lib/cassandra/protocol/cql_byte_buffer.rb +58 -44
  64. data/lib/cassandra/protocol/cql_protocol_handler.rb +57 -54
  65. data/lib/cassandra/protocol/request.rb +6 -7
  66. data/lib/cassandra/protocol/requests/auth_response_request.rb +3 -3
  67. data/lib/cassandra/protocol/requests/batch_request.rb +17 -8
  68. data/lib/cassandra/protocol/requests/credentials_request.rb +3 -3
  69. data/lib/cassandra/protocol/requests/execute_request.rb +39 -20
  70. data/lib/cassandra/protocol/requests/options_request.rb +1 -1
  71. data/lib/cassandra/protocol/requests/prepare_request.rb +5 -5
  72. data/lib/cassandra/protocol/requests/query_request.rb +28 -23
  73. data/lib/cassandra/protocol/requests/register_request.rb +2 -2
  74. data/lib/cassandra/protocol/requests/startup_request.rb +8 -8
  75. data/lib/cassandra/protocol/requests/void_query_request.rb +1 -1
  76. data/lib/cassandra/protocol/response.rb +3 -4
  77. data/lib/cassandra/protocol/responses/already_exists_error_response.rb +12 -2
  78. data/lib/cassandra/protocol/responses/auth_challenge_response.rb +4 -5
  79. data/lib/cassandra/protocol/responses/auth_success_response.rb +4 -5
  80. data/lib/cassandra/protocol/responses/authenticate_response.rb +4 -5
  81. data/lib/cassandra/protocol/responses/error_response.rb +104 -17
  82. data/lib/cassandra/protocol/responses/event_response.rb +3 -4
  83. data/lib/cassandra/protocol/responses/function_failure_error_response.rb +13 -2
  84. data/lib/cassandra/protocol/responses/prepared_result_response.rb +14 -9
  85. data/lib/cassandra/protocol/responses/raw_rows_result_response.rb +14 -9
  86. data/lib/cassandra/protocol/responses/read_failure_error_response.rb +26 -4
  87. data/lib/cassandra/protocol/responses/read_timeout_error_response.rb +22 -3
  88. data/lib/cassandra/protocol/responses/ready_response.rb +6 -7
  89. data/lib/cassandra/protocol/responses/result_response.rb +11 -10
  90. data/lib/cassandra/protocol/responses/rows_result_response.rb +8 -7
  91. data/lib/cassandra/protocol/responses/schema_change_event_response.rb +8 -8
  92. data/lib/cassandra/protocol/responses/schema_change_result_response.rb +19 -13
  93. data/lib/cassandra/protocol/responses/set_keyspace_result_response.rb +5 -6
  94. data/lib/cassandra/protocol/responses/status_change_event_response.rb +5 -6
  95. data/lib/cassandra/protocol/responses/supported_response.rb +4 -5
  96. data/lib/cassandra/protocol/responses/topology_change_event_response.rb +4 -5
  97. data/lib/cassandra/protocol/responses/unavailable_error_response.rb +20 -3
  98. data/lib/cassandra/protocol/responses/unprepared_error_response.rb +11 -2
  99. data/lib/cassandra/protocol/responses/void_result_response.rb +4 -5
  100. data/lib/cassandra/protocol/responses/write_failure_error_response.rb +26 -4
  101. data/lib/cassandra/protocol/responses/write_timeout_error_response.rb +22 -3
  102. data/lib/cassandra/protocol/v1.rb +98 -37
  103. data/lib/cassandra/protocol/v3.rb +121 -50
  104. data/lib/cassandra/protocol/v4.rb +172 -68
  105. data/lib/cassandra/reconnection.rb +1 -1
  106. data/lib/cassandra/reconnection/policies.rb +1 -1
  107. data/lib/cassandra/reconnection/policies/constant.rb +2 -4
  108. data/lib/cassandra/reconnection/policies/exponential.rb +6 -6
  109. data/lib/cassandra/result.rb +55 -20
  110. data/lib/cassandra/retry.rb +8 -8
  111. data/lib/cassandra/retry/policies.rb +1 -1
  112. data/lib/cassandra/retry/policies/default.rb +1 -1
  113. data/lib/cassandra/retry/policies/downgrading_consistency.rb +4 -2
  114. data/lib/cassandra/retry/policies/fallthrough.rb +1 -1
  115. data/lib/cassandra/session.rb +24 -16
  116. data/lib/cassandra/statement.rb +1 -1
  117. data/lib/cassandra/statements.rb +1 -1
  118. data/lib/cassandra/statements/batch.rb +16 -10
  119. data/lib/cassandra/statements/bound.rb +10 -3
  120. data/lib/cassandra/statements/prepared.rb +62 -18
  121. data/lib/cassandra/statements/simple.rb +23 -10
  122. data/lib/cassandra/statements/void.rb +1 -1
  123. data/lib/cassandra/table.rb +53 -185
  124. data/lib/cassandra/time.rb +11 -6
  125. data/lib/cassandra/time_uuid.rb +12 -14
  126. data/lib/cassandra/timestamp_generator.rb +37 -0
  127. data/lib/cassandra/timestamp_generator/simple.rb +38 -0
  128. data/lib/cassandra/timestamp_generator/ticking_on_duplicate.rb +58 -0
  129. data/lib/cassandra/tuple.rb +4 -4
  130. data/lib/cassandra/types.rb +109 -71
  131. data/lib/cassandra/udt.rb +66 -50
  132. data/lib/cassandra/util.rb +155 -15
  133. data/lib/cassandra/uuid.rb +20 -21
  134. data/lib/cassandra/uuid/generator.rb +7 -5
  135. data/lib/cassandra/version.rb +2 -2
  136. data/lib/cassandra_murmur3.jar +0 -0
  137. data/lib/datastax/cassandra.rb +1 -1
  138. metadata +27 -16
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1e6a787e376fb3c3593c39dfda36008493460d81
4
- data.tar.gz: a2a29ca90926d5bd17a77c3604b79a364acd7824
3
+ metadata.gz: e54478ae2983bea8fe87b5cdb7bb4109b64a0321
4
+ data.tar.gz: 60309ae2ddd6c3adb1b78f932e6985371e1dd1da
5
5
  SHA512:
6
- metadata.gz: d0f391e939675d5f22d8e057c9e2a75324e07762c6fd669c5768df8cb118b3d0a38daec56e81871351d80f913b3fe03d5000e3f1f5627522388780acb7accd64
7
- data.tar.gz: 01e6930ee06ca09812de451671c4598119ebb2982619c3e663522e35938f3e6d3c846859b0192e28e00604e2fc00dcbc3c0fcdf51a6736241aa96db48fce591b
6
+ metadata.gz: 6adf8e05498642fa68929c41e0def00dd35bda7ccdddb2caa56e4082a882ef2351fc48c1b33ee824a35f4cd9aa50a9391d40aaa73809311482bd0c352bda3d8c
7
+ data.tar.gz: 97bbc836f22e5ff90431a32ec9d0143b6c7c11d21b9610fa81018728991b55359395f095f55f826b79cd1248a73a8063173ee3541e263ead85e7460f80aa8812
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Datastax Ruby Driver for Apache Cassandra
2
2
 
3
- *If you're reading this on GitHub, please note that this is the readme for the development version and that some features described here might not yet have been released. You can [find the documentation for latest version through ruby driver docs](http://datastax.github.io/ruby-driver/) or via the release tags, [e.g. v1.0.0-beta.3](https://github.com/datastax/ruby-driver/tree/v1.0.0-beta.3).*
3
+ *If you're reading this on GitHub, please note that this is the readme for the development version and that some features described here might not yet have been released. You can [find the documentation for latest version through ruby driver docs](http://datastax.github.io/ruby-driver/) or via the release tags, [e.g. v3.0.0](https://github.com/datastax/ruby-driver/tree/v3.0.0).*
4
4
 
5
5
  [![Build Status](https://travis-ci.org/datastax/ruby-driver.svg?branch=master)](https://travis-ci.org/datastax/ruby-driver)
6
6
 
@@ -12,7 +12,7 @@ the Cassandra Query Language version 3 (CQL3) and Cassandra's native protocol.
12
12
  - Jira: https://datastax-oss.atlassian.net/browse/RUBY
13
13
  - Mailing List: https://groups.google.com/a/lists.datastax.com/forum/#!forum/ruby-driver-user
14
14
  - IRC: #datastax-drivers on [irc.freenode.net](http://freenode.net>)
15
- - Twitter: Follow the latest news about DataStax Drivers - [@avalanche123](http://twitter.com/avalanche123), [@mfiguiere](http://twitter.com/mfiguiere), [@al3xandru](https://twitter.com/al3xandru)
15
+ - Twitter: Follow the latest news about DataStax Drivers - [@avalanche123](http://twitter.com/avalanche123), [@stamhankar999](http://twitter.com/stamhankar999), [@al3xandru](https://twitter.com/al3xandru)
16
16
 
17
17
  This driver is based on [the cql-rb gem](https://github.com/iconara/cql-rb) by [Theo Hultberg](https://github.com/iconara) and we added support for:
18
18
 
@@ -27,17 +27,18 @@ This driver is based on [the cql-rb gem](https://github.com/iconara/cql-rb) by [
27
27
 
28
28
  [Check out the slides from Ruby Driver Explained](https://speakerdeck.com/avalanche123/ruby-driver-explained) for a detailed overview of the Ruby Driver architecture.
29
29
 
30
- ## Compability
30
+ ## Compatibility
31
31
 
32
32
  This driver works exclusively with the Cassandra Query Language v3 (CQL3) and Cassandra's native protocol. The current version works with:
33
33
 
34
- * Apache Cassandra versions 1.2, 2.0 and 2.1
35
- * DataStax Enterprise 3.1, 3.2, 4.0 and 4.5
36
- * Ruby (MRI) 1.9.3, 2.0, 2.1 and 2.2
34
+ * Apache Cassandra versions 1.2, 2.0, 2.1, 2.2, and 3.x
35
+ * DataStax Enterprise 4.0 and above.
36
+ * Ruby (MRI) 2.2, 2.3
37
37
  * JRuby 1.7
38
- * Rubinius 2.2
39
38
 
40
- __Note__: JRuby 1.6 is not officially supported, although 1.6.8 should work.
39
+ __Note__: JRuby 1.6 is not officially supported, although 1.6.8 should work. Similarly,
40
+ MRI 2.0 and 2.1 are not officially supported, but they should work. 1.9.3 is deprecated
41
+ and is likely to break in the release following 3.0.
41
42
 
42
43
  ## Quick start
43
44
 
@@ -53,10 +54,10 @@ end
53
54
  keyspace = 'system'
54
55
  session = cluster.connect(keyspace) # create session, optionally scoped to a keyspace, to execute queries
55
56
 
56
- future = session.execute_async('SELECT keyspace_name, columnfamily_name FROM schema_columnfamilies') # fully asynchronous api
57
+ future = session.execute_async('SELECT keyspace_name, table_name FROM system_schema.tables') # fully asynchronous api
57
58
  future.on_success do |rows|
58
59
  rows.each do |row|
59
- puts "The keyspace #{row['keyspace_name']} has a table called #{row['columnfamily_name']}"
60
+ puts "The keyspace #{row['keyspace_name']} has a table called #{row['table_name']}"
60
61
  end
61
62
  end
62
63
  future.join
@@ -91,31 +92,77 @@ __Note__: if you want to use compression you should also install [snappy](http:/
91
92
 
92
93
  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.
93
94
 
94
- ## What's new in v2.1.3
95
-
96
- Features:
95
+ ## What's new in v3.0.0
96
+
97
+ ### Features:
98
+
99
+ * Add support for Apache Cassandra native protocol v4
100
+ * Add support for smallint, tinyint, date (`Cassandra::Date`) and time (`Cassandra::Time`) data types.
101
+ * Include schema metadata for User Defined Functions and User Defined Aggregates.
102
+ * Augment the `Cassandra::Table` object to expose many more attributes: `id`, `options`, `keyspace`, `partition_key`, `clustering_columns`, and `clustering_order`. This makes it significantly easier to write administration scripts that report various attributes of your schema, which may help to highlight areas for improvement.
103
+ * Include client ip addresses in request traces, only on Cassandra 3.x.
104
+ * Add new retry policy decision `Cassandra::Retry::Policy#try_next_host`.
105
+ * Support specifying statement idempotence with the new `idempotent` option when executing.
106
+ * Support sending custom payloads when preparing or executing statements using the new `payload` option.
107
+ * Expose custom payloads received with responses on server exceptions and `Cassandra::Execution::Info` instances.
108
+ * Expose server warnings on server exceptions and `Cassandra::Execution::Info` instances.
109
+ * Add `connections_per_local_node`, `connections_per_remote_node`, `requests_per_connection` cluster configuration options to tune parallel query execution and resource usage.
110
+ * Add `Cassandra::Logger` class to make it easy for users to enable debug logging in the client.
111
+ * Add `protocol_version` configuration option to allow the user to force the protocol version to use for communication with nodes.
112
+ * Add support for materialized views and indexes in the schema metadata.
113
+ * Support the `ReadError`, `WriteError`, and `FunctionCallError` Cassandra error responses introduced in Cassandra 2.2.
114
+ * Add support for unset variables in bound statements.
115
+ * Support DSE security (`DseAuthenticator`, configured for LDAP).
116
+ * Add a timeout option to `Cassandra::Future#get`.
117
+
118
+ ### Breaking Changes from 2.x:
119
+
120
+ * `Cassandra::Future#join` is now an alias to Cassandra::Future#get and will raise an error if the future is resolved with one.
121
+ * Default consistency level is now LOCAL_ONE.
122
+ * Enable tcp no-delay by default.
123
+ * Unavailable errors are retried on the next host in the load balancing plan by default.
124
+ * Statement execution no longer retried on timeouts, unless the statement is marked as idempotent in the call to `Cassandra::Session#execute*` or when creating a `Cassandra::Statement` object.
125
+ * `Cassandra::Statements::Batch#add` and `Cassandra::Session#execute*` signatures have changed in how one specifies query parameters. Specify the query parameters array as the value of the arguments key:
126
+
127
+ ```ruby
128
+ batch.add(query, ['val1', 'val2'])
129
+ # becomes
130
+ batch.add(query, arguments: ['val1', 'val2'])
97
131
 
98
- * Apache Cassandra native protocol v3
99
- * [User-defined types](http://datastax.github.io/ruby-driver/features/basics/user_defined_types/) and [tuples](http://datastax.github.io/ruby-driver/features/basics/datatypes/#using-tuples)
100
- * [Schema metadata includes user-defined types](http://datastax.github.io/ruby-driver/api/keyspace/#type-instance_method)
101
- * [Named arguments](http://datastax.github.io/ruby-driver/features/basics/prepared_statements/#an-insert-statement-is-prepared-with-named-parameters)
102
- * [Public types api for type definition and introspection](http://datastax.github.io/ruby-driver/api/types/)
132
+ batch.add(query, {p1: 'val1'})
133
+ # becomes
134
+ batch.add(query, arguments: {p1: 'val1'})
135
+ ```
136
+ * The Datacenter-aware load balancing policy (`Cassandra::LoadBalancing::Policies::DCAwareRoundRobin`) defaults to using
137
+ nodes in the local DC only. In prior releases, the policy would fall back to remote nodes after exhausting local nodes.
138
+ Specify a positive value (or nil for unlimited) for `max_remote_hosts_to_use` when initializing the policy to allow remote node use.
139
+ * Unspecified variables in statements previously resulted in an exception. Now they are essentially ignored or treated as null.
103
140
 
104
- Breaking Changes:
141
+ ### Bug Fixes:
105
142
 
106
- * Splat style positional arguments support, deprecated in 2.0.0, has been dropped
143
+ * [[RUBY-120](https://datastax-oss.atlassian.net/browse/RUBY-120)] Tuples and UDTs can be used in sets and hash keys.
144
+ * [[RUBY-143](https://datastax-oss.atlassian.net/browse/RUBY-143)] Retry querying system table for metadata of new hosts when prior attempts fail, ultimately enabling use of new hosts.
145
+ * [[RUBY-150](https://datastax-oss.atlassian.net/browse/RUBY-150)] Fixed a protocol decoding error that occurred when multiple messages are available in a stream.
146
+ * [[RUBY-151](https://datastax-oss.atlassian.net/browse/RUBY-151)] Decode incomplete UDTs properly.
147
+ * [[RUBY-155](https://datastax-oss.atlassian.net/browse/RUBY-155)] Request timeout timer should not include request queuing time.
148
+ * [[RUBY-161](https://datastax-oss.atlassian.net/browse/RUBY-161)] Protocol version negotiation in mixed version clusters should not fall back to v1 unless it is truly warranted.
149
+ * [[RUBY-214](https://datastax-oss.atlassian.net/browse/RUBY-214)] Ensure client timestamps have microsecond precision in JRuby. Previously, some row updates would get lost in high transaction environments.
107
150
 
108
- Bug Fixes:
151
+ ## Feedback Requested
109
152
 
110
- * [[RUBY-93](https://datastax-oss.atlassian.net/browse/RUBY-93)] Reconnection can overflow the stack
153
+ *Help us focus our efforts!* [Provide your input](http://goo.gl/forms/pCs8PTpHLf) on the Ruby Driver Platform and Runtime Survey (we kept it short).
111
154
 
112
155
  ## Code examples
113
156
 
114
- The DataStax Ruby Driver uses the awesome [Cucumber Framework](http://cukes.info/) for both end-to-end, or acceptance, testing and constructing documentation. All of the features supported by the driver have appropriate acceptance tests with easy-to-copy code examples in the `features/` directory.
157
+ The DataStax Ruby Driver uses the awesome [Cucumber Framework](http://cukes.info/) for
158
+ both end-to-end, or acceptance, testing and constructing documentation. All of the
159
+ features supported by the driver have appropriate acceptance tests with easy-to-copy code
160
+ examples in the `features/` directory.
115
161
 
116
162
  ## Running tests
117
163
 
118
- If you don't feel like reading through the following instructions on how to run ruby-driver tests, feel free to [check out .travis.yml for the entire build code](https://github.com/datastax/ruby-driver/blob/master/.travis.yml).
164
+ If you don't feel like reading through the following instructions on how to run
165
+ ruby-driver tests, feel free to [check out .travis.yml for the entire build code](https://github.com/datastax/ruby-driver/blob/master/.travis.yml).
119
166
 
120
167
  * Check out the driver codebase and install test dependencies:
121
168
 
@@ -127,26 +174,39 @@ bundle install --without docs
127
174
 
128
175
  * [Install ccm](http://www.datastax.com/dev/blog/ccm-a-development-tool-for-creating-local-cassandra-clusters)
129
176
 
130
- * Run tests:
177
+ * Run tests against different versions of Cassandra:
131
178
 
132
179
  ```bash
133
- bundle exec cucumber # runs end-to-end tests (or bundle exec rake cucumber)
134
- bundle exec rspec # runs unit tests (or bundle exec rake rspec)
135
- bundle exec rake integration # run integration tests
136
- bundle exec rake test # run both as well as integration tests
180
+ CASSANDRA_VERSION=3.1.1 bundle exec cucumber # runs end-to-end tests (or bundle exec rake cucumber)
181
+ CASSANDRA_VERSION=3.0.0 bundle exec rspec # runs unit tests (or bundle exec rake rspec)
182
+ CASSANDRA_VERSION=2.1.12 bundle exec rake integration # run integration tests
183
+ CASSANDRA_VERSION=2.0.17 bundle exec rake test # run both as well as integration tests
137
184
  ```
138
185
 
139
186
  ## Changelog & versioning
140
187
 
141
- Check out the [releases on GitHub](https://github.com/datastax/ruby-driver/releases) and [changelog](https://github.com/datastax/ruby-driver/blob/master/CHANGELOG.md). Version numbering follows the [semantic versioning](http://semver.org/) scheme.
188
+ Check out the [releases on GitHub](https://github.com/datastax/ruby-driver/releases) and
189
+ [changelog](https://github.com/datastax/ruby-driver/blob/master/CHANGELOG.md). Version
190
+ numbering follows the [semantic versioning](http://semver.org/) scheme.
142
191
 
143
- Private and experimental APIs, defined as whatever is not in the [public API documentation][1], i.e. classes and methods marked as `@private`, will change without warning. If you've been recommended to try an experimental API by the maintainers, please let them know if you depend on that API. Experimental APIs will eventually become public, and knowing how they are used helps in determining their maturity.
192
+ Private and experimental APIs, defined as whatever is not in the
193
+ [public API documentation][1], i.e. classes and methods marked as `@private`, will change
194
+ without warning. If you've been recommended to try an experimental API by the maintainers,
195
+ please let them know if you depend on that API. Experimental APIs will eventually become
196
+ public, and knowing how they are used helps in determining their maturity.
144
197
 
145
- Prereleases will be stable, in the sense that they will have finished and properly tested features only, but may introduce APIs that will change before the final release. Please use the prereleases and report bugs, but don't deploy them to production without consulting the maintainers, or doing extensive testing yourself. If you do deploy to production please let the maintainers know as this helps determining the maturity of the release.
198
+ Prereleases will be stable, in the sense that they will have finished and properly tested
199
+ features only, but may introduce APIs that will change before the final release. Please
200
+ use the prereleases and report bugs, but don't deploy them to production without
201
+ consulting the maintainers, or doing extensive testing yourself. If you do deploy to
202
+ production please let the maintainers know as this helps in determining the maturity of
203
+ the release.
146
204
 
147
205
  ## Known bugs & limitations
148
206
 
149
- * JRuby 1.6 is not officially supported, although 1.6.8 should work, if you're stuck in JRuby 1.6.8 try and see if it works for you.
207
+ * Specifying a `protocol_version` option of 1 or 2 in cluster options will fail with a
208
+ `NoHostsAvailable` error rather than a `ProtocolError` against Cassandra node versions 3.0-3.4.
209
+ * JRuby 1.6 is not officially supported, although 1.6.8 should work.
150
210
  * Because the driver reactor is using `IO.select`, the maximum number of tcp connections allowed is 1024.
151
211
  * Because the driver uses `IO#write_nonblock`, Windows is not supported.
152
212
 
@@ -158,19 +218,26 @@ For contributing read [CONTRIBUTING.md](https://github.com/datastax/ruby-driver/
158
218
 
159
219
  ## Credits
160
220
 
161
- 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.
162
-
163
- The development effort to provide an up to date, high performance, fully featured Ruby Driver for Apache Cassandra will continue on this project, while [cql-rb](https://github.com/iconara/cql-rb/) will be discontinued.
221
+ This driver is based on the original work of [Theo Hultberg](https://github.com/iconara)
222
+ on [cql-rb](https://github.com/iconara/cql-rb/) and adds a series of advanced features
223
+ that are common across all other DataStax drivers for Apache Cassandra.
164
224
 
225
+ The development effort to provide an up to date, high performance, fully featured Ruby
226
+ Driver for Apache Cassandra will continue on this project, while
227
+ [cql-rb](https://github.com/iconara/cql-rb/) will be discontinued.
165
228
 
166
229
  ## Copyright
167
230
 
168
- Copyright 2013-2015 DataStax, Inc.
231
+ Copyright 2013-2016 DataStax, Inc.
169
232
 
170
- Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
233
+ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
234
+ except in compliance with the License. You may obtain a copy of the License at
171
235
 
172
236
  [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0)
173
237
 
174
- Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
238
+ Unless required by applicable law or agreed to in writing, software distributed under the
239
+ License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
240
+ either express or implied. See the License for the specific language governing permissions
241
+ and limitations under the License.
175
242
 
176
243
  [1]: http://datastax.github.io/ruby-driver/api
@@ -1,7 +1,7 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  #--
4
- # Copyright 2013-2015 DataStax, Inc.
4
+ # Copyright 2013-2016 DataStax, Inc.
5
5
  #
6
6
  # Licensed under the Apache License, Version 2.0 (the "License");
7
7
  # you may not use this file except in compliance with the License.
@@ -16,7 +16,6 @@
16
16
  # limitations under the License.
17
17
  #++
18
18
 
19
-
20
19
  require 'ione'
21
20
  require 'json'
22
21
 
@@ -35,11 +34,13 @@ require 'date'
35
34
 
36
35
  module Cassandra
37
36
  # A list of all supported request consistencies
38
- # @see http://www.datastax.com/documentation/cassandra/2.0/cassandra/dml/dml_config_consistency_c.html Consistency levels in Apache Cassandra 2.0
39
- # @see http://www.datastax.com/documentation/cassandra/1.2/cassandra/dml/dml_config_consistency_c.html Consistency levels in Apache Cassandra 1.2
37
+ # @see http://www.datastax.com/documentation/cassandra/2.0/cassandra/dml/dml_config_consistency_c.html Consistency
38
+ # levels in Apache Cassandra 2.0
39
+ # @see http://www.datastax.com/documentation/cassandra/1.2/cassandra/dml/dml_config_consistency_c.html Consistency
40
+ # levels in Apache Cassandra 1.2
40
41
  # @see Cassandra::Session#execute_async
41
- CONSISTENCIES = [ :any, :one, :two, :three, :quorum, :all, :local_quorum,
42
- :each_quorum, :serial, :local_serial, :local_one ].freeze
42
+ CONSISTENCIES = [:any, :one, :two, :three, :quorum, :all, :local_quorum,
43
+ :each_quorum, :serial, :local_serial, :local_one].freeze
43
44
 
44
45
  # A list of all supported serial consistencies
45
46
  # @see Cassandra::Session#execute_async
@@ -48,9 +49,52 @@ module Cassandra
48
49
  # A list of all possible write types that a
49
50
  # {Cassandra::Errors::WriteTimeoutError} can have.
50
51
  #
51
- # @see https://github.com/apache/cassandra/blob/cassandra-2.0.16/doc/native_protocol_v2.spec#L872-L887 Description of possible types of writes in Apache Cassandra native protocol spec v1
52
+ # @see https://github.com/apache/cassandra/blob/cassandra-2.0.16/doc/native_protocol_v2.spec#L872-L887 Description of
53
+ # possible types of writes in Apache Cassandra native protocol spec v1
52
54
  WRITE_TYPES = [:simple, :batch, :unlogged_batch, :counter, :batch_log].freeze
53
55
 
56
+ CLUSTER_OPTIONS = [
57
+ :address_resolution,
58
+ :address_resolution_policy,
59
+ :auth_provider,
60
+ :client_cert,
61
+ :client_timestamps,
62
+ :compression,
63
+ :compressor,
64
+ :connect_timeout,
65
+ :connections_per_local_node,
66
+ :connections_per_remote_node,
67
+ :consistency,
68
+ :credentials,
69
+ :datacenter,
70
+ :futures_factory,
71
+ :heartbeat_interval,
72
+ :hosts,
73
+ :idle_timeout,
74
+ :listeners,
75
+ :load_balancing_policy,
76
+ :logger,
77
+ :nodelay,
78
+ :reconnection_policy,
79
+ :retry_policy,
80
+ :page_size,
81
+ :passphrase,
82
+ :password,
83
+ :port,
84
+ :private_key,
85
+ :protocol_version,
86
+ :requests_per_connection,
87
+ :schema_refresh_delay,
88
+ :schema_refresh_timeout,
89
+ :server_cert,
90
+ :shuffle_replicas,
91
+ :ssl,
92
+ :synchronize_schema,
93
+ :timeout,
94
+ :trace,
95
+ :username
96
+ ].freeze
97
+
54
98
  # Creates a {Cassandra::Cluster Cluster instance}.
55
99
  #
56
100
  # @option options [Array<String, IPAddr>] :hosts (['127.0.0.1']) a list of
@@ -126,11 +170,34 @@ module Cassandra
126
170
  # address resolver to use. Must be one of `:none` or
127
171
  # `:ec2_multi_region`.
128
172
  #
129
- # @option options [Boolean] :client_timestamps (false) whether the driver
130
- # should send timestamps for each executed statement. Enabling this setting
131
- # allows mitigating Cassandra cluster clock skew because the timestamp of
132
- # the client machine will be used. This does not help mitigate application
133
- # cluster clock skew.
173
+ # @option options [Integer] :connections_per_local_node (nil) Number of connections to
174
+ # open to each local node; the value of this option directly correlates to the number
175
+ # of requests the client can make to the local node concurrently. When `nil`, the
176
+ # setting is `1` for nodes that use the v3 or later protocol, and `2` for nodes that
177
+ # use the v2 or earlier protocol.
178
+ #
179
+ # @option options [Integer] :connections_per_remote_node (1) Number of connections to
180
+ # open to each remote node; the value of this option directly correlates to the
181
+ # number of requests the client can make to the remote node concurrently.
182
+ #
183
+ # @option options [Integer] :requests_per_connection (nil) Number of outstanding
184
+ # requests to support on one connection. Depending on the types of requests, some may
185
+ # get processed in parallel in the Cassandra node. When `nil`, the setting is `1024`
186
+ # for nodes that use the v3 or later protocol, and `128` for nodes that use the
187
+ # v2 or earlier protocol.
188
+ #
189
+ # @option options [Integer] :protocol_version (nil) Version of protocol to speak to
190
+ # nodes. By default, this is auto-negotiated to the highest common protocol version
191
+ # that all nodes in `:hosts` speak.
192
+ #
193
+ # @option options [Boolean, Cassandra::TimestampGenerator] :client_timestamps (false) whether the driver
194
+ # should send timestamps for each executed statement and possibly which timestamp generator to use. Enabling this
195
+ # setting helps mitigate Cassandra cluster clock skew because the timestamp of the client machine will be used.
196
+ # This does not help mitigate application cluster clock skew. Also accepts an initialized
197
+ # {Cassandra::TimestampGenerator}, `:simple` (indicating an instance of {Cassandra::TimestampGenerator::Simple}),
198
+ # or `:monotonic` (indicating an instance of {Cassandra::TimestampGenerator::TickingOnDuplicate}). If set to true,
199
+ # it defaults to {Cassandra::TimestampGenerator::Simple} for all Ruby flavors except JRuby. On JRuby, it defaults to
200
+ # {Cassandra::TimestampGenerator::TickingOnDuplicate}.
134
201
  #
135
202
  # @option options [Boolean] :synchronize_schema (true) whether the driver
136
203
  # should automatically keep schema metadata synchronized. When enabled, the
@@ -139,7 +206,8 @@ module Cassandra
139
206
  # automatic schema updates. Schema metadata is used by the driver to
140
207
  # determine cluster partitioners as well as to find partition keys and
141
208
  # replicas of prepared statements, this information makes token aware load
142
- # balancing possible. One can still {Cassandra::Cluster#refresh_schema refresh schema manually}.
209
+ # balancing possible. One can still
210
+ # {Cassandra::Cluster#refresh_schema refresh schema manually}.
143
211
  #
144
212
  # @option options [Numeric] :schema_refresh_delay (1) the driver will wait
145
213
  # for `:schema_refresh_delay` before fetching metadata after receiving a
@@ -177,9 +245,15 @@ module Cassandra
177
245
  # @option options [Integer] :page_size (10000) default page size for all
178
246
  # select queries. Set this value to `nil` to disable paging.
179
247
  #
180
- # @option options [Hash{String => String}] :credentials (none) a hash of credentials - to be used with [credentials authentication in cassandra 1.2](https://github.com/apache/cassandra/blob/cassandra-2.0.16/doc/native_protocol_v1.spec#L238-L250). Note that if you specified `:username` and `:password` options, those credentials are configured automatically.
248
+ # @option options [Hash{String => String}] :credentials (none) a hash of credentials -
249
+ # to be used with [credentials authentication in cassandra 1.2](https://github.com/apache/cassandra/blob/cassandra-2.0.16/doc/native_protocol_v1.spec#L238-L250).
250
+ # Note that if you specified `:username` and `:password` options, those credentials
251
+ # are configured automatically.
181
252
  #
182
- # @option options [Cassandra::Auth::Provider] :auth_provider (none) a custom auth provider to be used with [SASL authentication in cassandra 2.0](https://github.com/apache/cassandra/blob/cassandra-2.0.16/doc/native_protocol_v2.spec#L257-L273). Note that if you have specified `:username` and `:password`, then a {Cassandra::Auth::Providers::Password Password Provider} will be used automatically.
253
+ # @option options [Cassandra::Auth::Provider] :auth_provider (none) a custom auth
254
+ # provider to be used with [SASL authentication in cassandra 2.0](https://github.com/apache/cassandra/blob/cassandra-2.0.16/doc/native_protocol_v2.spec#L257-L273).
255
+ # Note that if you have specified `:username` and `:password`, then a
256
+ # {Cassandra::Auth::Providers::Password Password Provider} will be used automatically.
183
257
  #
184
258
  # @option options [Cassandra::Compression::Compressor] :compressor (none) a
185
259
  # custom compressor. Note that if you have specified `:compression`, an
@@ -187,8 +261,8 @@ module Cassandra
187
261
  #
188
262
  # @option options [Cassandra::AddressResolution::Policy]
189
263
  # :address_resolution_policy default:
190
- # {Cassandra::AddressResolution::Policies::None No Resolution Policy} a custom address resolution
191
- # policy. Note that if you have specified `:address_resolution`, an
264
+ # {Cassandra::AddressResolution::Policies::None No Resolution Policy} a custom address
265
+ # resolution policy. Note that if you have specified `:address_resolution`, an
192
266
  # appropriate address resolution policy will be provided automatically.
193
267
  #
194
268
  # @option options [Object<#all, #error, #value, #promise>] :futures_factory
@@ -219,78 +293,140 @@ module Cassandra
219
293
  # @return [Cassandra::Future<Cassandra::Cluster>] a future resolving to the
220
294
  # cluster instance.
221
295
  def self.cluster_async(options = {})
222
- options = options.select do |key, value|
223
- [ :credentials, :auth_provider, :compression, :hosts, :logger, :port,
224
- :load_balancing_policy, :reconnection_policy, :retry_policy, :listeners,
225
- :consistency, :trace, :page_size, :compressor, :username, :password,
226
- :ssl, :server_cert, :client_cert, :private_key, :passphrase,
227
- :connect_timeout, :futures_factory, :datacenter, :address_resolution,
228
- :address_resolution_policy, :idle_timeout, :heartbeat_interval, :timeout,
229
- :synchronize_schema, :schema_refresh_delay, :schema_refresh_timeout,
230
- :shuffle_replicas, :client_timestamps
231
- ].include?(key)
232
- end
233
-
234
- has_username = options.has_key?(:username)
235
- has_password = options.has_key?(:password)
296
+ options = validate_and_massage_options(options)
297
+ hosts = []
298
+
299
+ Array(options.fetch(:hosts, '127.0.0.1')).each do |host|
300
+ case host
301
+ when ::IPAddr
302
+ hosts << host
303
+ when ::String # ip address or hostname
304
+ Resolv.each_address(host) do |ip|
305
+ hosts << ::IPAddr.new(ip)
306
+ end
307
+ else
308
+ raise ::ArgumentError, ":hosts must be String or IPAddr, #{host.inspect} given"
309
+ end
310
+ end
311
+
312
+ if hosts.empty?
313
+ raise ::ArgumentError,
314
+ ":hosts #{options[:hosts].inspect} could not be resolved to any ip address"
315
+ end
316
+
317
+ hosts.shuffle!
318
+ rescue => e
319
+ futures = options.fetch(:futures_factory) { return Future::Error.new(e) }
320
+ futures.error(e)
321
+ else
322
+ driver = Driver.new(options)
323
+ driver.connect(hosts)
324
+ end
325
+
326
+ # @private
327
+ SSL_CLASSES = [::TrueClass, ::FalseClass, ::OpenSSL::SSL::SSLContext].freeze
328
+
329
+ # rubocop:disable Metrics/AbcSize
330
+ # rubocop:disable Metrics/CyclomaticComplexity
331
+ # rubocop:disable Metrics/PerceivedComplexity
332
+ # @private
333
+ def self.validate_and_massage_options(options)
334
+ options = options.select do |key, _|
335
+ CLUSTER_OPTIONS.include?(key)
336
+ end
337
+
338
+ has_username = options.key?(:username)
339
+ has_password = options.key?(:password)
236
340
  if has_username || has_password
237
341
  if has_username && !has_password
238
- raise ::ArgumentError, "both :username and :password options must be specified, but only :username given"
342
+ raise ::ArgumentError,
343
+ 'both :username and :password options must be specified, ' \
344
+ 'but only :username given'
239
345
  end
240
346
 
241
347
  if !has_username && has_password
242
- raise ::ArgumentError, "both :username and :password options must be specified, but only :password given"
348
+ raise ::ArgumentError,
349
+ 'both :username and :password options must be specified, ' \
350
+ 'but only :password given'
243
351
  end
244
352
 
245
353
  username = options.delete(:username)
246
354
  password = options.delete(:password)
247
355
 
248
- Util.assert_instance_of(::String, username) { ":username must be a String, #{username.inspect} given" }
249
- Util.assert_instance_of(::String, password) { ":password must be a String, #{password.inspect} given" }
250
- Util.assert_not_empty(username) { ":username cannot be empty" }
251
- Util.assert_not_empty(password) { ":password cannot be empty" }
356
+ Util.assert_instance_of(::String, username) do
357
+ ":username must be a String, #{username.inspect} given"
358
+ end
359
+ Util.assert_instance_of(::String, password) do
360
+ ":password must be a String, #{password.inspect} given"
361
+ end
362
+ Util.assert_not_empty(username) { ':username cannot be empty' }
363
+ Util.assert_not_empty(password) { ':password cannot be empty' }
252
364
 
253
- options[:credentials] = {:username => username, :password => password}
365
+ options[:credentials] = {username: username, password: password}
254
366
  options[:auth_provider] = Auth::Providers::Password.new(username, password)
255
367
  end
256
368
 
257
- if options.has_key?(:credentials)
369
+ if options.key?(:credentials)
258
370
  credentials = options[:credentials]
259
371
 
260
- Util.assert_instance_of(::Hash, credentials) { ":credentials must be a hash, #{credentials.inspect} given" }
372
+ Util.assert_instance_of(::Hash, credentials) do
373
+ ":credentials must be a hash, #{credentials.inspect} given"
374
+ end
261
375
  end
262
376
 
263
- if options.has_key?(:auth_provider)
377
+ if options.key?(:auth_provider)
264
378
  auth_provider = options[:auth_provider]
265
379
 
266
- Util.assert_responds_to(:create_authenticator, auth_provider) { ":auth_provider #{auth_provider.inspect} must respond to :create_authenticator, but doesn't" }
380
+ Util.assert_responds_to(:create_authenticator, auth_provider) do
381
+ ":auth_provider #{auth_provider.inspect} must respond to " \
382
+ ":create_authenticator, but doesn't"
383
+ end
267
384
  end
268
385
 
269
- has_client_cert = options.has_key?(:client_cert)
270
- has_private_key = options.has_key?(:private_key)
386
+ has_client_cert = options.key?(:client_cert)
387
+ has_private_key = options.key?(:private_key)
271
388
 
272
389
  if has_client_cert || has_private_key
273
390
  if has_client_cert && !has_private_key
274
- raise ::ArgumentError, "both :client_cert and :private_key options must be specified, but only :client_cert given"
391
+ raise ::ArgumentError,
392
+ 'both :client_cert and :private_key options must be specified, ' \
393
+ 'but only :client_cert given'
275
394
  end
276
395
 
277
396
  if !has_client_cert && has_private_key
278
- raise ::ArgumentError, "both :client_cert and :private_key options must be specified, but only :private_key given"
397
+ raise ::ArgumentError,
398
+ 'both :client_cert and :private_key options must be specified, ' \
399
+ 'but only :private_key given'
279
400
  end
280
401
 
402
+ Util.assert_instance_of(::String, options[:client_cert]) do
403
+ ":client_cert must be a string, #{options[:client_cert].inspect} given"
404
+ end
405
+ Util.assert_instance_of(::String, options[:private_key]) do
406
+ ":client_cert must be a string, #{options[:private_key].inspect} given"
407
+ end
281
408
  client_cert = ::File.expand_path(options[:client_cert])
282
409
  private_key = ::File.expand_path(options[:private_key])
283
410
 
284
- Util.assert_file_exists(client_cert) { ":client_cert #{client_cert.inspect} doesn't exist" }
285
- Util.assert_file_exists(private_key) { ":private_key #{private_key.inspect} doesn't exist" }
411
+ Util.assert_file_exists(client_cert) do
412
+ ":client_cert #{client_cert.inspect} doesn't exist"
413
+ end
414
+ Util.assert_file_exists(private_key) do
415
+ ":private_key #{private_key.inspect} doesn't exist"
416
+ end
286
417
  end
287
418
 
288
- has_server_cert = options.has_key?(:server_cert)
419
+ has_server_cert = options.key?(:server_cert)
289
420
 
290
421
  if has_server_cert
422
+ Util.assert_instance_of(::String, options[:server_cert]) do
423
+ ":server_cert must be a string, #{options[:server_cert].inspect} given"
424
+ end
291
425
  server_cert = ::File.expand_path(options[:server_cert])
292
426
 
293
- Util.assert_file_exists(server_cert) { ":server_cert #{server_cert.inspect} doesn't exist" }
427
+ Util.assert_file_exists(server_cert) do
428
+ ":server_cert #{server_cert.inspect} doesn't exist"
429
+ end
294
430
  end
295
431
 
296
432
  if has_client_cert || has_server_cert
@@ -304,23 +440,26 @@ module Cassandra
304
440
  if has_client_cert
305
441
  context.cert = ::OpenSSL::X509::Certificate.new(File.read(client_cert))
306
442
 
307
- if options.has_key?(:passphrase)
308
- context.key = ::OpenSSL::PKey::RSA.new(File.read(private_key), options[:passphrase])
309
- else
310
- context.key = ::OpenSSL::PKey::RSA.new(File.read(private_key))
311
- end
443
+ context.key = if options.key?(:passphrase)
444
+ ::OpenSSL::PKey::RSA.new(File.read(private_key),
445
+ options[:passphrase])
446
+ else
447
+ ::OpenSSL::PKey::RSA.new(File.read(private_key))
448
+ end
312
449
  end
313
450
 
314
451
  options[:ssl] = context
315
452
  end
316
453
 
317
- if options.has_key?(:ssl)
454
+ if options.key?(:ssl)
318
455
  ssl = options[:ssl]
319
456
 
320
- Util.assert_instance_of_one_of([::TrueClass, ::FalseClass, ::OpenSSL::SSL::SSLContext], ssl) { ":ssl must be a boolean or an OpenSSL::SSL::SSLContext, #{ssl.inspect} given" }
457
+ Util.assert_instance_of_one_of(SSL_CLASSES, ssl) do
458
+ ":ssl must be a boolean or an OpenSSL::SSL::SSLContext, #{ssl.inspect} given"
459
+ end
321
460
  end
322
461
 
323
- if options.has_key?(:compression)
462
+ if options.key?(:compression)
324
463
  compression = options.delete(:compression)
325
464
 
326
465
  case compression
@@ -329,195 +468,296 @@ module Cassandra
329
468
  when :lz4
330
469
  options[:compressor] = Compression::Compressors::Lz4.new
331
470
  else
332
- raise ::ArgumentError, ":compression must be either :snappy or :lz4, #{compression.inspect} given"
471
+ raise ::ArgumentError,
472
+ ":compression must be either :snappy or :lz4, #{compression.inspect} given"
333
473
  end
334
474
  end
335
475
 
336
- if options.has_key?(:compressor)
476
+ if options.key?(:compressor)
337
477
  compressor = options[:compressor]
338
478
  methods = [:algorithm, :compress?, :compress, :decompress]
339
479
 
340
- Util.assert_responds_to_all(methods, compressor) { ":compressor #{compressor.inspect} must respond to #{methods.inspect}, but doesn't" }
480
+ Util.assert_responds_to_all(methods, compressor) do
481
+ ":compressor #{compressor.inspect} must respond to #{methods.inspect}, " \
482
+ "but doesn't"
483
+ end
341
484
  end
342
485
 
343
- if options.has_key?(:logger)
344
- logger = options[:logger]
345
- methods = [:debug, :info, :warn, :error, :fatal]
486
+ if options.key?(:logger)
487
+ if options[:logger].nil?
488
+ # Delete the key because we want to fallback to the default logger in Driver.
489
+ options.delete(:logger)
490
+ else
491
+ # Validate
492
+ logger = options[:logger]
493
+ methods = [:debug, :info, :warn, :error, :fatal]
346
494
 
347
- Util.assert_responds_to_all(methods, logger) { ":logger #{logger.inspect} must respond to #{methods.inspect}, but doesn't" }
495
+ Util.assert_responds_to_all(methods, logger) do
496
+ ":logger #{logger.inspect} must respond to #{methods.inspect}, but doesn't"
497
+ end
498
+ end
348
499
  end
349
500
 
350
- if options.has_key?(:port)
351
- port = options[:port] = Integer(options[:port])
352
-
353
- Util.assert_one_of(0..65536, port) { ":port must be a valid ip port, #{port} given" }
501
+ if options.key?(:port)
502
+ unless options[:port].nil?
503
+ port = options[:port]
504
+ Util.assert_instance_of(::Integer, port)
505
+ Util.assert_one_of(1...2**16, port) do
506
+ ":port must be a valid ip port, #{port} given"
507
+ end
508
+ end
354
509
  end
355
510
 
356
- if options.has_key?(:datacenter)
357
- options[:datacenter] = String(options[:datacenter])
358
- end
511
+ options[:datacenter] = String(options[:datacenter]) if options.key?(:datacenter)
359
512
 
360
- if options.has_key?(:connect_timeout)
513
+ if options.key?(:connect_timeout)
361
514
  timeout = options[:connect_timeout]
362
515
 
363
516
  unless timeout.nil?
364
- Util.assert_instance_of(::Numeric, timeout) { ":connect_timeout must be a number of seconds, #{timeout} given" }
365
- Util.assert(timeout > 0) { ":connect_timeout must be greater than 0, #{timeout} given" }
517
+ Util.assert_instance_of(::Numeric, timeout) do
518
+ ":connect_timeout must be a number of seconds, #{timeout.inspect} given"
519
+ end
520
+ Util.assert(timeout > 0) do
521
+ ":connect_timeout must be greater than 0, #{timeout} given"
522
+ end
366
523
  end
367
524
  end
368
525
 
369
- if options.has_key?(:timeout)
526
+ if options.key?(:timeout)
370
527
  timeout = options[:timeout]
371
528
 
372
529
  unless timeout.nil?
373
- Util.assert_instance_of(::Numeric, timeout) { ":timeout must be a number of seconds, #{timeout} given" }
530
+ Util.assert_instance_of(::Numeric, timeout) do
531
+ ":timeout must be a number of seconds, #{timeout.inspect} given"
532
+ end
374
533
  Util.assert(timeout > 0) { ":timeout must be greater than 0, #{timeout} given" }
375
534
  end
376
535
  end
377
536
 
378
- if options.has_key?(:heartbeat_interval)
537
+ if options.key?(:heartbeat_interval)
379
538
  timeout = options[:heartbeat_interval]
380
539
 
381
540
  unless timeout.nil?
382
- Util.assert_instance_of(::Numeric, timeout) { ":heartbeat_interval must be a number of seconds, #{timeout} given" }
383
- Util.assert(timeout > 0) { ":heartbeat_interval must be greater than 0, #{timeout} given" }
541
+ Util.assert_instance_of(::Numeric, timeout) do
542
+ ":heartbeat_interval must be a number of seconds, #{timeout.inspect} given"
543
+ end
544
+ Util.assert(timeout > 0) do
545
+ ":heartbeat_interval must be greater than 0, #{timeout} given"
546
+ end
384
547
  end
385
548
  end
386
549
 
387
- if options.has_key?(:idle_timeout)
550
+ if options.key?(:idle_timeout)
388
551
  timeout = options[:idle_timeout]
389
552
 
390
553
  unless timeout.nil?
391
- Util.assert_instance_of(::Numeric, timeout) { ":idle_timeout must be a number of seconds, #{timeout} given" }
392
- Util.assert(timeout > 0) { ":idle_timeout must be greater than 0, #{timeout} given" }
554
+ Util.assert_instance_of(::Numeric, timeout) do
555
+ ":idle_timeout must be a number of seconds, #{timeout.inspect} given"
556
+ end
557
+ Util.assert(timeout > 0) do
558
+ ":idle_timeout must be greater than 0, #{timeout} given"
559
+ end
393
560
  end
394
561
  end
395
562
 
396
- if options.has_key?(:schema_refresh_delay)
563
+ if options.key?(:schema_refresh_delay)
397
564
  timeout = options[:schema_refresh_delay]
398
565
 
399
- Util.assert_instance_of(::Numeric, timeout) { ":schema_refresh_delay must be a number of seconds, #{timeout} given" }
400
- Util.assert(timeout > 0) { ":schema_refresh_delay must be greater than 0, #{timeout} given" }
566
+ Util.assert_instance_of(::Numeric, timeout) do
567
+ ":schema_refresh_delay must be a number of seconds, #{timeout.inspect} given"
568
+ end
569
+ Util.assert(timeout > 0) do
570
+ ":schema_refresh_delay must be greater than 0, #{timeout} given"
571
+ end
401
572
  end
402
573
 
403
- if options.has_key?(:schema_refresh_timeout)
574
+ if options.key?(:schema_refresh_timeout)
404
575
  timeout = options[:schema_refresh_timeout]
405
576
 
406
- Util.assert_instance_of(::Numeric, timeout) { ":schema_refresh_timeout must be a number of seconds, #{timeout} given" }
407
- Util.assert(timeout > 0) { ":schema_refresh_timeout must be greater than 0, #{timeout} given" }
577
+ Util.assert_instance_of(::Numeric, timeout) do
578
+ ":schema_refresh_timeout must be a number of seconds, #{timeout.inspect} given"
579
+ end
580
+ Util.assert(timeout > 0) do
581
+ ":schema_refresh_timeout must be greater than 0, #{timeout} given"
582
+ end
408
583
  end
409
584
 
410
- if options.has_key?(:load_balancing_policy)
585
+ if options.key?(:load_balancing_policy)
411
586
  load_balancing_policy = options[:load_balancing_policy]
412
- methods = [:host_up, :host_down, :host_found, :host_lost, :setup, :teardown, :distance, :plan]
587
+ methods = [:host_up, :host_down, :host_found, :host_lost, :setup, :teardown,
588
+ :distance, :plan]
413
589
 
414
- Util.assert_responds_to_all(methods, load_balancing_policy) { ":load_balancing_policy #{load_balancing_policy.inspect} must respond to #{methods.inspect}, but doesn't" }
590
+ Util.assert_responds_to_all(methods, load_balancing_policy) do
591
+ ":load_balancing_policy #{load_balancing_policy.inspect} must respond " \
592
+ "to #{methods.inspect}, but doesn't"
593
+ end
415
594
  end
416
595
 
417
- if options.has_key?(:reconnection_policy)
596
+ if options.key?(:reconnection_policy)
418
597
  reconnection_policy = options[:reconnection_policy]
419
598
 
420
- Util.assert_responds_to(:schedule, reconnection_policy) { ":reconnection_policy #{reconnection_policy.inspect} must respond to :schedule, but doesn't" }
599
+ Util.assert_responds_to(:schedule, reconnection_policy) do
600
+ ":reconnection_policy #{reconnection_policy.inspect} must respond to " \
601
+ ":schedule, but doesn't"
602
+ end
421
603
  end
422
604
 
423
- if options.has_key?(:retry_policy)
605
+ if options.key?(:retry_policy)
424
606
  retry_policy = options[:retry_policy]
425
607
  methods = [:read_timeout, :write_timeout, :unavailable]
426
608
 
427
- Util.assert_responds_to_all(methods, retry_policy) { ":retry_policy #{retry_policy.inspect} must respond to #{methods.inspect}, but doesn't" }
609
+ Util.assert_responds_to_all(methods, retry_policy) do
610
+ ":retry_policy #{retry_policy.inspect} must respond to #{methods.inspect}, " \
611
+ "but doesn't"
612
+ end
428
613
  end
429
614
 
430
- if options.has_key?(:listeners)
431
- options[:listeners] = Array(options[:listeners])
432
- end
615
+ options[:listeners] = Array(options[:listeners]) if options.key?(:listeners)
433
616
 
434
- if options.has_key?(:consistency)
617
+ if options.key?(:consistency)
435
618
  consistency = options[:consistency]
436
619
 
437
- Util.assert_one_of(CONSISTENCIES, consistency) { ":consistency must be one of #{CONSISTENCIES.inspect}, #{consistency.inspect} given" }
438
- end
439
-
440
- if options.has_key?(:nodelay)
441
- options[:nodelay] = !!options[:nodelay]
620
+ Util.assert_one_of(CONSISTENCIES, consistency) do
621
+ ":consistency must be one of #{CONSISTENCIES.inspect}, " \
622
+ "#{consistency.inspect} given"
623
+ end
442
624
  end
443
625
 
444
- if options.has_key?(:trace)
445
- options[:trace] = !!options[:trace]
446
- end
626
+ options[:nodelay] = !!options[:nodelay] if options.key?(:nodelay)
627
+ options[:trace] = !!options[:trace] if options.key?(:trace)
628
+ options[:shuffle_replicas] = !!options[:shuffle_replicas] if options.key?(:shuffle_replicas)
447
629
 
448
- if options.has_key?(:shuffle_replicas)
449
- options[:shuffle_replicas] = !!options[:shuffle_replicas]
450
- end
451
-
452
- if options.has_key?(:page_size)
630
+ if options.key?(:page_size)
453
631
  page_size = options[:page_size]
454
632
 
455
633
  unless page_size.nil?
456
- page_size = options[:page_size] = Integer(page_size)
457
- Util.assert(page_size > 0) { ":page_size must be a positive integer, #{page_size.inspect} given" }
634
+ page_size = options[:page_size]
635
+ Util.assert_instance_of(::Integer, page_size)
636
+ Util.assert_one_of(1...2**32, page_size) do
637
+ ":page_size must be a positive integer, #{page_size.inspect} given"
638
+ end
639
+ end
640
+ end
641
+
642
+ if options.key?(:protocol_version)
643
+ protocol_version = options[:protocol_version]
644
+ unless protocol_version.nil?
645
+ Util.assert_instance_of(::Integer, protocol_version)
646
+ Util.assert_one_of(1..4, protocol_version) do
647
+ ":protocol_version must be a positive integer, #{protocol_version.inspect} given"
648
+ end
458
649
  end
459
650
  end
460
651
 
461
- if options.has_key?(:futures_factory)
652
+ if options.key?(:futures_factory)
462
653
  futures_factory = options[:futures_factory]
463
654
  methods = [:error, :value, :promise, :all]
464
655
 
465
- Util.assert_responds_to_all(methods, futures_factory) { ":futures_factory #{futures_factory.inspect} must respond to #{methods.inspect}, but doesn't" }
656
+ Util.assert_responds_to_all(methods, futures_factory) do
657
+ ":futures_factory #{futures_factory.inspect} must respond to " \
658
+ "#{methods.inspect}, but doesn't"
659
+ end
466
660
  end
467
661
 
468
- if options.has_key?(:address_resolution)
662
+ if options.key?(:address_resolution)
469
663
  address_resolution = options.delete(:address_resolution)
470
664
 
471
665
  case address_resolution
472
666
  when :none
473
667
  # do nothing
474
668
  when :ec2_multi_region
475
- options[:address_resolution_policy] = AddressResolution::Policies::EC2MultiRegion.new
669
+ options[:address_resolution_policy] =
670
+ AddressResolution::Policies::EC2MultiRegion.new
476
671
  else
477
- raise ::ArgumentError, ":address_resolution must be either :none or :ec2_multi_region, #{address_resolution.inspect} given"
672
+ raise ::ArgumentError,
673
+ ':address_resolution must be either :none or :ec2_multi_region, ' \
674
+ "#{address_resolution.inspect} given"
478
675
  end
479
676
  end
480
677
 
481
- if options.has_key?(:address_resolution_policy)
678
+ if options.key?(:address_resolution_policy)
482
679
  address_resolver = options[:address_resolution_policy]
483
680
 
484
- Util.assert_responds_to(:resolve, address_resolver) { ":address_resolution_policy must respond to :resolve, #{address_resolver.inspect} but doesn't" }
681
+ Util.assert_responds_to(:resolve, address_resolver) do
682
+ ':address_resolution_policy must respond to :resolve, ' \
683
+ "#{address_resolver.inspect} but doesn't"
684
+ end
485
685
  end
486
686
 
487
- if options.has_key?(:synchronize_schema)
488
- options[:synchronize_schema] = !!options[:synchronize_schema]
687
+ options[:synchronize_schema] = !!options[:synchronize_schema] if options.key?(:synchronize_schema)
688
+
689
+ if options.key?(:client_timestamps)
690
+ timestamp_generator = case options[:client_timestamps]
691
+ when true
692
+ if RUBY_ENGINE == 'jruby'
693
+ Cassandra::TimestampGenerator::TickingOnDuplicate.new
694
+ else
695
+ Cassandra::TimestampGenerator::Simple.new
696
+ end
697
+ when false
698
+ nil
699
+ when :simple
700
+ Cassandra::TimestampGenerator::Simple.new
701
+ when :monotonic
702
+ Cassandra::TimestampGenerator::TickingOnDuplicate.new
703
+ else
704
+ # The value must be a generator instance.
705
+ options[:client_timestamps]
706
+ end
707
+
708
+ if timestamp_generator
709
+ Util.assert_responds_to(:next, timestamp_generator) do
710
+ ":client_timestamps #{options[:client_timestamps].inspect} must be a boolean, :simple, :monotonic, or " \
711
+ 'an object that responds to :next'
712
+ end
713
+ end
714
+ options.delete(:client_timestamps)
715
+ options[:timestamp_generator] = timestamp_generator
489
716
  end
490
717
 
491
- if options.has_key?(:client_timestamps)
492
- options[:client_timestamps] = !!options[:client_timestamps]
718
+ if options.key?(:connections_per_local_node)
719
+ connections_per_node = options[:connections_per_local_node]
720
+
721
+ unless connections_per_node.nil?
722
+ connections_per_node = options[:connections_per_local_node]
723
+ Util.assert_instance_of(::Integer, connections_per_node)
724
+ Util.assert_one_of(1...2**16, connections_per_node) do
725
+ ':connections_per_local_node must be a positive integer between ' \
726
+ "1 and 65535, #{connections_per_node.inspect} given"
727
+ end
728
+ end
493
729
  end
494
730
 
495
- hosts = []
731
+ if options.key?(:connections_per_remote_node)
732
+ connections_per_node = options[:connections_per_remote_node]
496
733
 
497
- Array(options.fetch(:hosts, '127.0.0.1')).each do |host|
498
- case host
499
- when ::IPAddr
500
- hosts << host
501
- when ::String # ip address or hostname
502
- Resolv.each_address(host) do |ip|
503
- hosts << ::IPAddr.new(ip)
734
+ unless connections_per_node.nil?
735
+ connections_per_node = options[:connections_per_remote_node]
736
+ Util.assert_instance_of(::Integer, connections_per_node)
737
+ Util.assert_one_of(1...2**16, connections_per_node) do
738
+ ':connections_per_remote_node must be a positive integer between ' \
739
+ "1 and 65535, #{connections_per_node.inspect} given"
504
740
  end
505
- else
506
- raise ::ArgumentError, ":hosts must be String or IPAddr, #{host.inspect} given"
507
741
  end
508
742
  end
509
743
 
510
- if hosts.empty?
511
- raise ::ArgumentError, ":hosts #{options[:hosts].inspect} could not be resolved to any ip address"
512
- end
744
+ if options.key?(:requests_per_connection)
745
+ requests_per_connection = options[:requests_per_connection]
513
746
 
514
- hosts.shuffle!
515
- rescue => e
516
- futures = options.fetch(:futures_factory) { return Future::Error.new(e) }
517
- futures.error(e)
518
- else
519
- driver = Driver.new(options)
520
- driver.connect(hosts)
747
+ unless requests_per_connection.nil?
748
+ requests_per_connection = options[:requests_per_connection]
749
+ Util.assert_instance_of(::Integer, requests_per_connection)
750
+
751
+ # v3 protocol says that max stream-id is 32767 (2^15-1). This setting might be
752
+ # used to talk to a v2 (or less) node, but then we'll adjust it down.
753
+
754
+ Util.assert_one_of(1...2**15, requests_per_connection) do
755
+ ':requests_per_connection must be a positive integer, ' \
756
+ "#{requests_per_connection.inspect} given"
757
+ end
758
+ end
759
+ end
760
+ options
521
761
  end
522
762
 
523
763
  # @private
@@ -535,9 +775,11 @@ module Cassandra
535
775
  # => 1970-1-1
536
776
  # ::Date.jd(DATE_OFFSET + 2 ** 32, ::Date::GREGORIAN)
537
777
  # => 5881580-07-12
538
- DATE_OFFSET = (::Time.utc(1970, 1, 1).to_date.jd - 2 ** 31)
778
+ DATE_OFFSET = (::Time.utc(1970, 1, 1).to_date.jd - 2**31)
539
779
  end
540
780
 
781
+ require 'cassandra/attr_boolean'
782
+ require 'cassandra/version'
541
783
  require 'cassandra/uuid'
542
784
  require 'cassandra/time_uuid'
543
785
  require 'cassandra/tuple'
@@ -550,6 +792,7 @@ require 'cassandra/errors'
550
792
  require 'cassandra/compression'
551
793
  require 'cassandra/protocol'
552
794
  require 'cassandra/auth'
795
+ require 'cassandra/cassandra_logger'
553
796
  require 'cassandra/null_logger'
554
797
 
555
798
  require 'cassandra/executors'
@@ -565,9 +808,13 @@ require 'cassandra/statements'
565
808
  require 'cassandra/aggregate'
566
809
  require 'cassandra/argument'
567
810
  require 'cassandra/function'
811
+ require 'cassandra/function_collection'
568
812
  require 'cassandra/column'
813
+ require 'cassandra/column_container'
569
814
  require 'cassandra/table'
815
+ require 'cassandra/materialized_view'
570
816
  require 'cassandra/keyspace'
817
+ require 'cassandra/index'
571
818
 
572
819
  require 'cassandra/execution/info'
573
820
  require 'cassandra/execution/options'
@@ -577,6 +824,7 @@ require 'cassandra/load_balancing'
577
824
  require 'cassandra/reconnection'
578
825
  require 'cassandra/retry'
579
826
  require 'cassandra/address_resolution'
827
+ require 'cassandra/timestamp_generator'
580
828
 
581
829
  require 'cassandra/util'
582
830
 
@@ -587,7 +835,7 @@ module Cassandra
587
835
  # @private
588
836
  VOID_STATEMENT = Statements::Void.new
589
837
  # @private
590
- VOID_OPTIONS = Execution::Options.new({:consistency => :one})
838
+ VOID_OPTIONS = Execution::Options.new(consistency: :one)
591
839
  # @private
592
840
  NO_HOSTS = Errors::NoHostsAvailable.new
593
841
  end