ilios 1.1.1 → 1.1.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2b605b28e1524aca634a188b28f9f557745673a789912e4971f173f5687d7ef6
4
- data.tar.gz: 46cc89d6f8f096f926339f2e9d14e76b72858167a9332d4e53ba1800fe10f527
3
+ metadata.gz: 2c20c800bf59b7673cdb60ce4dd5086412e3461f4fa75f8f729eef7b2bba3ade
4
+ data.tar.gz: 1edb52689033a9047388c4be27f81ac3c1078fefcd845bc6f03c8d0080983670
5
5
  SHA512:
6
- metadata.gz: d7008c470792420f2ff04f03c7d48d4f267a89de816f90d1381ccef7f0c19a7a5623067c916ba58c2a3dd14e4698e68822313692b0a7e19f1e6275803297e4ce
7
- data.tar.gz: 2c073c1b1b2e8227579bfd5b9e1847d4124f379615e236bf573a15a971aa55a9a26e920efa3f8e857363465cab24af2d17e49730a33043a45299f0907d81dbd6
6
+ metadata.gz: 0724d14f13175a62b25aeda0926ea4f9bb6593f64c75cf871bf55426594ec2cf1b4d76fcd61a86a85c686cec49902e37b0f9302bce9933467c688d55dad9c78d
7
+ data.tar.gz: 7c3cd165e571bbdccbf627a2bb50aadfa84a02188fed7ede80a6bb41d7bfb7b75d43c0b31ab92dcf9de882d3dbfd7a5170663708e72023c9c640da9b3bedf14d
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Change Log
2
2
 
3
+ ## 1.1.2
4
+
5
+ - Support username/password authentication with `Cluster#credentials` (#33)
6
+ - Add cluster options: consistency, serial_consistency, num_threads_io, queue_size_io, core_connections_per_host, constant_reconnect, exponential_reconnect, tcp_nodelay, tcp_keepalive, connection_heartbeat_interval, connection_idle_timeout, load_balance_round_robin, load_balance_dc_aware, token_aware_routing, latency_aware_routing and use_schema (#33)
7
+ - Accept Symbol consistency levels (e.g. `:quorum`) in `Cluster#consistency` and `Cluster#serial_consistency` (#33)
8
+ - Fix infinite loop when binding a `list`/`set` whose element appends to the array while it is being converted (#34)
9
+ - Fix infinite loop in `Cluster#hosts` when an element appends to the array while it is being converted (#34)
10
+
3
11
  ## 1.1.1
4
12
 
5
13
  - Fix out-of-bounds read when binding a `list`/`set` whose element mutates the array while it is being converted (#31)
data/README.md CHANGED
@@ -171,6 +171,25 @@ Notes:
171
171
  - Because a `Symbol` map key is stored as its `String` equivalent, binding a map that contains both (for example `{ k1: 1, 'k1' => 2 }`) ends up as a single key on the server; the entry bound last wins.
172
172
  - Binding a `String` containing a NUL character (`\0`) to a `text` (or `ascii` / `varchar`) column raises `ArgumentError` (known limitation).
173
173
 
174
+ ### Authentication and cluster options
175
+
176
+ Username/password authentication (Cassandra's `PasswordAuthenticator`) and
177
+ common cluster options are configured on `Cluster` before `connect`:
178
+
179
+ ```ruby
180
+ cluster = Ilios::Cassandra::Cluster.new
181
+ cluster.hosts(['127.0.0.1'])
182
+ cluster.credentials('username', 'password')
183
+ cluster.consistency(:quorum) # or Ilios::Cassandra::Cluster::CONSISTENCY_QUORUM
184
+ cluster.num_threads_io(4)
185
+ cluster.exponential_reconnect(2_000, 60_000)
186
+ cluster.tcp_keepalive(true, 60)
187
+ cluster.load_balance_dc_aware('dc1')
188
+ session = cluster.connect
189
+ ```
190
+
191
+ See the RBS signatures in `sig/ilios.rbs` for the full list of options.
192
+
174
193
  ### Synchronous API
175
194
  `Ilios::Cassandra::Session#prepare` and `Ilios::Cassandra::Session#execute` are provided as synchronous API.
176
195
 
data/Rakefile CHANGED
@@ -4,9 +4,6 @@ require 'bundler/gem_tasks'
4
4
  require 'rake/extensiontask'
5
5
  require 'rake/testtask'
6
6
 
7
- desc 'Run tests'
8
- task test: :compile
9
-
10
7
  task default: :test
11
8
 
12
9
  Rake::ExtensionTask.new('ilios') do |ext|
@@ -14,9 +11,10 @@ Rake::ExtensionTask.new('ilios') do |ext|
14
11
  end
15
12
 
16
13
  test_config = lambda do |t|
14
+ t.deps = [:compile]
17
15
  t.pattern = 'test/test_*.rb'
18
16
  end
19
- Rake::TestTask.new(test: :compile, &test_config)
17
+ Rake::TestTask.new(:test, &test_config)
20
18
 
21
19
  namespace :rbs do
22
20
  desc 'Validate RBS definitions'
@@ -31,7 +29,9 @@ end
31
29
  if RUBY_PLATFORM.include?('linux')
32
30
  require 'ruby_memcheck'
33
31
 
32
+ RubyMemcheck.config(binary_name: 'ilios')
33
+
34
34
  namespace :test do
35
- RubyMemcheck::TestTask.new(valgrind: :compile, &test_config)
35
+ RubyMemcheck::TestTask.new(:valgrind, &test_config)
36
36
  end
37
37
  end
data/docker-compose.yml CHANGED
@@ -6,17 +6,32 @@ services:
6
6
  - 9042:9042
7
7
  volumes:
8
8
  - cassandra-data:/var/lib/cassandra
9
+ cassandra-auth:
10
+ image: cassandra:4
11
+ ports:
12
+ - "9043:9042"
13
+ command: >
14
+ bash -c "sed -i 's/^authenticator:.*/authenticator: PasswordAuthenticator/'
15
+ /etc/cassandra/cassandra.yaml
16
+ && exec docker-entrypoint.sh cassandra -f"
17
+ volumes:
18
+ - cassandra-auth-data:/var/lib/cassandra
9
19
  ilios:
10
20
  build:
11
21
  context: ./dockerfiles
12
22
  dockerfile: ubuntu.dockerfile
13
23
  depends_on:
14
24
  - cassandra
25
+ - cassandra-auth
15
26
  environment:
16
27
  CASSANDRA_HOST: cassandra
28
+ CASSANDRA_AUTH_HOST: cassandra-auth
29
+ CASSANDRA_AUTH_PORT: 9042
17
30
  command: bash -c 'sleep infinity'
18
31
  volumes:
19
32
  - "./:/opt/ilios"
20
33
  volumes:
21
34
  cassandra-data:
22
35
  driver: local
36
+ cassandra-auth-data:
37
+ driver: local
data/ext/ilios/cluster.c CHANGED
@@ -86,16 +86,18 @@ static VALUE cluster_connect(VALUE self)
86
86
  static VALUE cluster_hosts(VALUE self, VALUE hosts)
87
87
  {
88
88
  CassandraCluster *cassandra_cluster;
89
+ long length;
89
90
 
90
91
  GET_CLUSTER(self, cassandra_cluster);
91
92
 
92
93
  Check_Type(hosts, T_ARRAY);
93
- if (RARRAY_LEN(hosts) == 0) {
94
+ length = RARRAY_LEN(hosts);
95
+ if (length == 0) {
94
96
  rb_raise(rb_eArgError, "No host exists.");
95
97
  }
96
98
 
97
- for (int i = 0; i < RARRAY_LEN(hosts); i++) {
98
- VALUE host = RARRAY_AREF(hosts, i);
99
+ for (long i = 0; i < length && i < RARRAY_LEN(hosts); i++) {
100
+ VALUE host = rb_ary_entry(hosts, i);
99
101
  cass_cluster_set_contact_points(cassandra_cluster->cluster, StringValueCStr(host));
100
102
  }
101
103
 
@@ -141,7 +143,7 @@ static VALUE cluster_keyspace(VALUE self, VALUE keyspace)
141
143
  * Sets the protocol version. The driver will automatically downgrade to the lowest supported protocol version.
142
144
  * Default is +PROTOCOL_VERSION_V4+.
143
145
  *
144
- * @param timeout_ms [Integer] A connect timeout in milliseconds.
146
+ * @param version [Integer] A protocol version.
145
147
  * @return [Cassandra::Cluster] self.
146
148
  */
147
149
  static VALUE cluster_protocol_version(VALUE self, VALUE version)
@@ -226,6 +228,427 @@ static VALUE cluster_constant_speculative_execution_policy(VALUE self, VALUE con
226
228
  return self;
227
229
  }
228
230
 
231
+ /**
232
+ * Sets credentials for plain text authentication.
233
+ *
234
+ * @param username [String] A username.
235
+ * @param password [String] A password.
236
+ * @return [Cassandra::Cluster] self.
237
+ */
238
+ static VALUE cluster_credentials(VALUE self, VALUE username, VALUE password)
239
+ {
240
+ CassandraCluster *cassandra_cluster;
241
+
242
+ GET_CLUSTER(self, cassandra_cluster);
243
+ // The driver copies both strings into its own memory
244
+ // (cluster_config.cpp: cass_cluster_set_credentials_n), so the Ruby
245
+ // strings do not need to be retained.
246
+ cass_cluster_set_credentials(cassandra_cluster->cluster, StringValueCStr(username), StringValueCStr(password));
247
+
248
+ return self;
249
+ }
250
+
251
+ static void cluster_check_error(CassError error, const char *name)
252
+ {
253
+ if (error != CASS_OK) {
254
+ rb_raise(rb_eArgError, "Invalid %s: %s", name, cass_error_desc(error));
255
+ }
256
+ }
257
+
258
+ // Casting a negative value to unsigned wraps it into a huge number, so check
259
+ // the range on the signed value NUM2LONG/NUM2LL already produced. Converting a
260
+ // second time would run to_int again, which may return a different value.
261
+ static unsigned cluster_value_to_uint(VALUE value, const char *name)
262
+ {
263
+ long v = NUM2LONG(value);
264
+
265
+ if (v < 0 || v > UINT_MAX) {
266
+ rb_raise(rb_eRangeError, "Invalid %s: %ld", name, v);
267
+ }
268
+ return (unsigned)v;
269
+ }
270
+
271
+ static cass_uint64_t cluster_value_to_uint64(VALUE value, const char *name)
272
+ {
273
+ long long v = NUM2LL(value);
274
+
275
+ if (v < 0) {
276
+ rb_raise(rb_eRangeError, "Invalid %s: %lld", name, v);
277
+ }
278
+ return (cass_uint64_t)v;
279
+ }
280
+
281
+ // The driver only rejects CASS_CONSISTENCY_UNKNOWN at set time; any other
282
+ // out-of-range value is accepted and later fails every query with an
283
+ // opaque protocol error, so validate the range here.
284
+ static void cluster_check_consistency(long value, const char *name)
285
+ {
286
+ if (value < CASS_CONSISTENCY_ANY || value > CASS_CONSISTENCY_LOCAL_ONE) {
287
+ rb_raise(rb_eArgError, "Invalid %s: %ld", name, value);
288
+ }
289
+ }
290
+
291
+ static struct {
292
+ const char *name;
293
+ CassConsistency consistency;
294
+ ID id;
295
+ } cluster_consistency_symbols[] = {
296
+ { "any", CASS_CONSISTENCY_ANY, 0 },
297
+ { "one", CASS_CONSISTENCY_ONE, 0 },
298
+ { "two", CASS_CONSISTENCY_TWO, 0 },
299
+ { "three", CASS_CONSISTENCY_THREE, 0 },
300
+ { "quorum", CASS_CONSISTENCY_QUORUM, 0 },
301
+ { "all", CASS_CONSISTENCY_ALL, 0 },
302
+ { "local_quorum", CASS_CONSISTENCY_LOCAL_QUORUM, 0 },
303
+ { "each_quorum", CASS_CONSISTENCY_EACH_QUORUM, 0 },
304
+ { "serial", CASS_CONSISTENCY_SERIAL, 0 },
305
+ { "local_serial", CASS_CONSISTENCY_LOCAL_SERIAL, 0 },
306
+ { "local_one", CASS_CONSISTENCY_LOCAL_ONE, 0 },
307
+ };
308
+
309
+ // Accepts either an Integer consistency constant or a Symbol such as
310
+ // +:quorum+/+:local_serial+ naming one of the CONSISTENCY_* constants.
311
+ static CassConsistency cluster_value_to_consistency(VALUE value, const char *name)
312
+ {
313
+ long v;
314
+
315
+ if (SYMBOL_P(value)) {
316
+ // rb_sym2id would intern an unknown Symbol and leave it uncollectable, so
317
+ // repeated invalid values grow memory with no way to reclaim it.
318
+ VALUE name_value = value;
319
+ ID id = rb_check_id(&name_value);
320
+
321
+ for (size_t i = 0; id && i < sizeof(cluster_consistency_symbols) / sizeof(cluster_consistency_symbols[0]); i++) {
322
+ if (id == cluster_consistency_symbols[i].id) {
323
+ return cluster_consistency_symbols[i].consistency;
324
+ }
325
+ }
326
+ rb_raise(rb_eArgError, "Invalid %s: %"PRIsVALUE"", name, value);
327
+ }
328
+
329
+ v = NUM2LONG(value);
330
+ cluster_check_consistency(v, name);
331
+ return (CassConsistency)v;
332
+ }
333
+
334
+ /**
335
+ * Sets the default consistency level of the statement.
336
+ * Default is +CONSISTENCY_LOCAL_ONE+.
337
+ *
338
+ * @param consistency [Integer, Symbol] A consistency level.
339
+ * Symbols such as +:quorum+ or +:local_serial+ are also accepted.
340
+ * @return [Cassandra::Cluster] self.
341
+ * @raise [ArgumentError] If an invalid consistency level was given.
342
+ * @raise [RangeError] If the value does not fit in a C long.
343
+ */
344
+ static VALUE cluster_consistency(VALUE self, VALUE consistency)
345
+ {
346
+ CassandraCluster *cassandra_cluster;
347
+ CassConsistency consistency_value = cluster_value_to_consistency(consistency, "consistency");
348
+
349
+ GET_CLUSTER(self, cassandra_cluster);
350
+ cluster_check_error(cass_cluster_set_consistency(cassandra_cluster->cluster, consistency_value), "consistency");
351
+
352
+ return self;
353
+ }
354
+
355
+ /**
356
+ * Sets the default serial consistency level of the statement.
357
+ * Default is +CONSISTENCY_ANY+.
358
+ *
359
+ * @param consistency [Integer, Symbol] A serial consistency level. Only
360
+ * +CONSISTENCY_SERIAL+/+CONSISTENCY_LOCAL_SERIAL+ (or +:serial+/+:local_serial+)
361
+ * are accepted.
362
+ * @return [Cassandra::Cluster] self.
363
+ * @raise [ArgumentError] If an invalid consistency level was given, or if it is
364
+ * not +CONSISTENCY_SERIAL+ or +CONSISTENCY_LOCAL_SERIAL+.
365
+ * @raise [RangeError] If the value does not fit in a C long.
366
+ */
367
+ static VALUE cluster_serial_consistency(VALUE self, VALUE consistency)
368
+ {
369
+ CassandraCluster *cassandra_cluster;
370
+ CassConsistency consistency_value = cluster_value_to_consistency(consistency, "serial_consistency");
371
+
372
+ // Cassandra only accepts SERIAL/LOCAL_SERIAL as a serial consistency
373
+ // level (used for lightweight transactions); any other value passes
374
+ // the generic range check above but is later rejected by the server
375
+ // at query time with an opaque error, so validate it here.
376
+ if (consistency_value != CASS_CONSISTENCY_SERIAL && consistency_value != CASS_CONSISTENCY_LOCAL_SERIAL) {
377
+ rb_raise(rb_eArgError, "Invalid serial_consistency: %"PRIsVALUE"", consistency);
378
+ }
379
+
380
+ GET_CLUSTER(self, cassandra_cluster);
381
+ cluster_check_error(cass_cluster_set_serial_consistency(cassandra_cluster->cluster, consistency_value), "serial_consistency");
382
+
383
+ return self;
384
+ }
385
+
386
+ /**
387
+ * Sets the number of IO threads that will handle query requests.
388
+ * Default is +1+.
389
+ *
390
+ * @param num_threads [Integer] A number of IO threads.
391
+ * @return [Cassandra::Cluster] self.
392
+ * @raise [ArgumentError] If zero was given.
393
+ * @raise [RangeError] If a negative value was given.
394
+ */
395
+ static VALUE cluster_num_threads_io(VALUE self, VALUE num_threads)
396
+ {
397
+ CassandraCluster *cassandra_cluster;
398
+
399
+ GET_CLUSTER(self, cassandra_cluster);
400
+ cluster_check_error(cass_cluster_set_num_threads_io(cassandra_cluster->cluster, cluster_value_to_uint(num_threads, "num_threads_io")), "num_threads_io");
401
+
402
+ return self;
403
+ }
404
+
405
+ /**
406
+ * Sets the size of the fixed size queue that stores pending requests.
407
+ * Default is +8192+.
408
+ *
409
+ * @param queue_size [Integer] A queue size.
410
+ * @return [Cassandra::Cluster] self.
411
+ * @raise [ArgumentError] If zero was given.
412
+ * @raise [RangeError] If a negative value was given.
413
+ */
414
+ static VALUE cluster_queue_size_io(VALUE self, VALUE queue_size)
415
+ {
416
+ CassandraCluster *cassandra_cluster;
417
+
418
+ GET_CLUSTER(self, cassandra_cluster);
419
+ cluster_check_error(cass_cluster_set_queue_size_io(cassandra_cluster->cluster, cluster_value_to_uint(queue_size, "queue_size_io")), "queue_size_io");
420
+
421
+ return self;
422
+ }
423
+
424
+ /**
425
+ * Sets the number of connections made to each server in each IO thread.
426
+ * Default is +1+.
427
+ *
428
+ * @param num_connections [Integer] A number of connections.
429
+ * @return [Cassandra::Cluster] self.
430
+ * @raise [ArgumentError] If zero was given.
431
+ * @raise [RangeError] If a negative value was given.
432
+ */
433
+ static VALUE cluster_core_connections_per_host(VALUE self, VALUE num_connections)
434
+ {
435
+ CassandraCluster *cassandra_cluster;
436
+
437
+ GET_CLUSTER(self, cassandra_cluster);
438
+ cluster_check_error(cass_cluster_set_core_connections_per_host(cassandra_cluster->cluster, cluster_value_to_uint(num_connections, "core_connections_per_host")), "core_connections_per_host");
439
+
440
+ return self;
441
+ }
442
+
443
+ /**
444
+ * Configures the cluster to use a reconnection policy that waits a constant
445
+ * time between each reconnection attempt.
446
+ * The exponential policy is used unless this method is called.
447
+ *
448
+ * @param delay_ms [Integer] A delay in milliseconds.
449
+ * @return [Cassandra::Cluster] self.
450
+ * @raise [RangeError] If a negative value was given.
451
+ */
452
+ static VALUE cluster_constant_reconnect(VALUE self, VALUE delay_ms)
453
+ {
454
+ CassandraCluster *cassandra_cluster;
455
+
456
+ GET_CLUSTER(self, cassandra_cluster);
457
+ cass_cluster_set_constant_reconnect(cassandra_cluster->cluster, cluster_value_to_uint64(delay_ms, "constant_reconnect"));
458
+
459
+ return self;
460
+ }
461
+
462
+ /**
463
+ * Configures the cluster to use a reconnection policy that waits
464
+ * exponentially longer between each reconnection attempt; however
465
+ * will maintain a constant delay once the maximum delay is reached.
466
+ * This is the default policy, with a base delay of +2000+ milliseconds and
467
+ * a maximum delay of +60000+ milliseconds.
468
+ *
469
+ * @param base_delay_ms [Integer] A base delay in milliseconds.
470
+ * @param max_delay_ms [Integer] A maximum delay in milliseconds.
471
+ * @return [Cassandra::Cluster] self.
472
+ * @raise [ArgumentError] If the base delay is 1 or less, the maximum delay is 1 or less,
473
+ * or the maximum delay is less than the base delay.
474
+ * @raise [RangeError] If a negative value was given.
475
+ */
476
+ static VALUE cluster_exponential_reconnect(VALUE self, VALUE base_delay_ms, VALUE max_delay_ms)
477
+ {
478
+ CassandraCluster *cassandra_cluster;
479
+
480
+ GET_CLUSTER(self, cassandra_cluster);
481
+ cluster_check_error(cass_cluster_set_exponential_reconnect(cassandra_cluster->cluster, cluster_value_to_uint64(base_delay_ms, "exponential_reconnect base_delay_ms"), cluster_value_to_uint64(max_delay_ms, "exponential_reconnect max_delay_ms")), "exponential_reconnect");
482
+
483
+ return self;
484
+ }
485
+
486
+ /**
487
+ * Enables/Disables Nagle's algorithm on the connections.
488
+ * Default is +true+ (disables Nagle's algorithm).
489
+ *
490
+ * @param enabled [Boolean] Whether to disable Nagle's algorithm.
491
+ * @return [Cassandra::Cluster] self.
492
+ */
493
+ static VALUE cluster_tcp_nodelay(VALUE self, VALUE enabled)
494
+ {
495
+ CassandraCluster *cassandra_cluster;
496
+
497
+ GET_CLUSTER(self, cassandra_cluster);
498
+ cass_cluster_set_tcp_nodelay(cassandra_cluster->cluster, RTEST(enabled) ? cass_true : cass_false);
499
+
500
+ return self;
501
+ }
502
+
503
+ /**
504
+ * Enables/Disables TCP keep-alive.
505
+ * Default is +false+ (disabled).
506
+ *
507
+ * @param enabled [Boolean] Whether to enable TCP keep-alive.
508
+ * @param delay_secs [Integer] The initial delay in seconds. Ignored when disabled.
509
+ * @return [Cassandra::Cluster] self.
510
+ * @raise [RangeError] If a negative value was given.
511
+ */
512
+ static VALUE cluster_tcp_keepalive(VALUE self, VALUE enabled, VALUE delay_secs)
513
+ {
514
+ CassandraCluster *cassandra_cluster;
515
+
516
+ GET_CLUSTER(self, cassandra_cluster);
517
+ cass_cluster_set_tcp_keepalive(cassandra_cluster->cluster, RTEST(enabled) ? cass_true : cass_false, cluster_value_to_uint(delay_secs, "tcp_keepalive delay_secs"));
518
+
519
+ return self;
520
+ }
521
+
522
+ /**
523
+ * Sets the amount of time between heartbeat messages and controls the amount
524
+ * of time the connection must be idle before sending heartbeat messages.
525
+ * This is useful for preventing intermediate network devices from dropping connections.
526
+ * Default is +30+ seconds.
527
+ *
528
+ * @param interval_secs [Integer] An interval in seconds. +0+ disables heartbeat messages.
529
+ * @return [Cassandra::Cluster] self.
530
+ * @raise [RangeError] If a negative value was given.
531
+ */
532
+ static VALUE cluster_connection_heartbeat_interval(VALUE self, VALUE interval_secs)
533
+ {
534
+ CassandraCluster *cassandra_cluster;
535
+
536
+ GET_CLUSTER(self, cassandra_cluster);
537
+ cass_cluster_set_connection_heartbeat_interval(cassandra_cluster->cluster, cluster_value_to_uint(interval_secs, "connection_heartbeat_interval"));
538
+
539
+ return self;
540
+ }
541
+
542
+ /**
543
+ * Sets the amount of time a connection is allowed to be without a successful
544
+ * heartbeat response before being terminated and scheduled for reconnection.
545
+ * Default is +60+ seconds.
546
+ *
547
+ * @param timeout_secs [Integer] A timeout in seconds.
548
+ * @return [Cassandra::Cluster] self.
549
+ * @raise [RangeError] If a negative value was given.
550
+ */
551
+ static VALUE cluster_connection_idle_timeout(VALUE self, VALUE timeout_secs)
552
+ {
553
+ CassandraCluster *cassandra_cluster;
554
+
555
+ GET_CLUSTER(self, cassandra_cluster);
556
+ cass_cluster_set_connection_idle_timeout(cassandra_cluster->cluster, cluster_value_to_uint(timeout_secs, "connection_idle_timeout"));
557
+
558
+ return self;
559
+ }
560
+
561
+ /**
562
+ * Configures the cluster to use round-robin load balancing.
563
+ * The driver discovers all nodes in a cluster and cycles through them per request.
564
+ *
565
+ * @return [Cassandra::Cluster] self.
566
+ */
567
+ static VALUE cluster_load_balance_round_robin(VALUE self)
568
+ {
569
+ CassandraCluster *cassandra_cluster;
570
+
571
+ GET_CLUSTER(self, cassandra_cluster);
572
+ cass_cluster_set_load_balance_round_robin(cassandra_cluster->cluster);
573
+
574
+ return self;
575
+ }
576
+
577
+ /**
578
+ * Configures the cluster to use DC-aware load balancing.
579
+ * For each query, all live nodes in a primary 'local' DC are tried first,
580
+ * followed by any node from other DCs.
581
+ *
582
+ * @param local_dc [String] The primary data center to try first.
583
+ * @return [Cassandra::Cluster] self.
584
+ * @raise [ArgumentError] If an invalid data center name was given.
585
+ */
586
+ static VALUE cluster_load_balance_dc_aware(VALUE self, VALUE local_dc)
587
+ {
588
+ CassandraCluster *cassandra_cluster;
589
+
590
+ GET_CLUSTER(self, cassandra_cluster);
591
+ // The 3rd and 4th parameters (used_hosts_per_remote_dc and
592
+ // allow_remote_dcs_for_local_cl) are deprecated in driver 2.16, so they
593
+ // are fixed to 0/cass_false and not exposed to Ruby.
594
+ cluster_check_error(cass_cluster_set_load_balance_dc_aware(cassandra_cluster->cluster, StringValueCStr(local_dc), 0, cass_false), "load_balance_dc_aware");
595
+
596
+ return self;
597
+ }
598
+
599
+ /**
600
+ * Configures the cluster to use token-aware request routing or not.
601
+ * Default is +true+ (enabled).
602
+ *
603
+ * @param enabled [Boolean] Whether to enable token-aware routing.
604
+ * @return [Cassandra::Cluster] self.
605
+ */
606
+ static VALUE cluster_token_aware_routing(VALUE self, VALUE enabled)
607
+ {
608
+ CassandraCluster *cassandra_cluster;
609
+
610
+ GET_CLUSTER(self, cassandra_cluster);
611
+ cass_cluster_set_token_aware_routing(cassandra_cluster->cluster, RTEST(enabled) ? cass_true : cass_false);
612
+
613
+ return self;
614
+ }
615
+
616
+ /**
617
+ * Configures the cluster to use latency-aware request routing or not.
618
+ * Default is +false+ (disabled).
619
+ *
620
+ * @param enabled [Boolean] Whether to enable latency-aware routing.
621
+ * @return [Cassandra::Cluster] self.
622
+ */
623
+ static VALUE cluster_latency_aware_routing(VALUE self, VALUE enabled)
624
+ {
625
+ CassandraCluster *cassandra_cluster;
626
+
627
+ GET_CLUSTER(self, cassandra_cluster);
628
+ cass_cluster_set_latency_aware_routing(cassandra_cluster->cluster, RTEST(enabled) ? cass_true : cass_false);
629
+
630
+ return self;
631
+ }
632
+
633
+ /**
634
+ * Enables/Disables retrieving and updating schema metadata. Disabling this
635
+ * can be useful to improve startup performance, but it means that
636
+ * 'Session#schema_metadata'-like features will not work.
637
+ * Default is +true+ (enabled).
638
+ *
639
+ * @param enabled [Boolean] Whether to keep schema metadata synchronized.
640
+ * @return [Cassandra::Cluster] self.
641
+ */
642
+ static VALUE cluster_use_schema(VALUE self, VALUE enabled)
643
+ {
644
+ CassandraCluster *cassandra_cluster;
645
+
646
+ GET_CLUSTER(self, cassandra_cluster);
647
+ cass_cluster_set_use_schema(cassandra_cluster->cluster, RTEST(enabled) ? cass_true : cass_false);
648
+
649
+ return self;
650
+ }
651
+
229
652
  static void cluster_mark(void *ptr)
230
653
  {
231
654
  CassandraCluster *cassandra_cluster = (CassandraCluster *)ptr;
@@ -256,6 +679,10 @@ static void cluster_compact(void *ptr)
256
679
 
257
680
  void Init_cluster(void)
258
681
  {
682
+ for (size_t i = 0; i < sizeof(cluster_consistency_symbols) / sizeof(cluster_consistency_symbols[0]); i++) {
683
+ cluster_consistency_symbols[i].id = rb_intern(cluster_consistency_symbols[i].name);
684
+ }
685
+
259
686
  rb_define_alloc_func(cCluster, cluster_allocator);
260
687
  rb_define_method(cCluster, "initialize", cluster_initialize, 0);
261
688
  rb_define_method(cCluster, "connect", cluster_connect, 0);
@@ -267,6 +694,23 @@ void Init_cluster(void)
267
694
  rb_define_method(cCluster, "request_timeout", cluster_request_timeout, 1);
268
695
  rb_define_method(cCluster, "resolve_timeout", cluster_resolve_timeout, 1);
269
696
  rb_define_method(cCluster, "constant_speculative_execution_policy", cluster_constant_speculative_execution_policy, 2);
697
+ rb_define_method(cCluster, "credentials", cluster_credentials, 2);
698
+ rb_define_method(cCluster, "consistency", cluster_consistency, 1);
699
+ rb_define_method(cCluster, "serial_consistency", cluster_serial_consistency, 1);
700
+ rb_define_method(cCluster, "num_threads_io", cluster_num_threads_io, 1);
701
+ rb_define_method(cCluster, "queue_size_io", cluster_queue_size_io, 1);
702
+ rb_define_method(cCluster, "core_connections_per_host", cluster_core_connections_per_host, 1);
703
+ rb_define_method(cCluster, "constant_reconnect", cluster_constant_reconnect, 1);
704
+ rb_define_method(cCluster, "exponential_reconnect", cluster_exponential_reconnect, 2);
705
+ rb_define_method(cCluster, "tcp_nodelay", cluster_tcp_nodelay, 1);
706
+ rb_define_method(cCluster, "tcp_keepalive", cluster_tcp_keepalive, 2);
707
+ rb_define_method(cCluster, "connection_heartbeat_interval", cluster_connection_heartbeat_interval, 1);
708
+ rb_define_method(cCluster, "connection_idle_timeout", cluster_connection_idle_timeout, 1);
709
+ rb_define_method(cCluster, "load_balance_round_robin", cluster_load_balance_round_robin, 0);
710
+ rb_define_method(cCluster, "load_balance_dc_aware", cluster_load_balance_dc_aware, 1);
711
+ rb_define_method(cCluster, "token_aware_routing", cluster_token_aware_routing, 1);
712
+ rb_define_method(cCluster, "latency_aware_routing", cluster_latency_aware_routing, 1);
713
+ rb_define_method(cCluster, "use_schema", cluster_use_schema, 1);
270
714
 
271
715
  rb_define_const(cCluster, "PROTOCOL_VERSION_V1", INT2NUM(CASS_PROTOCOL_VERSION_V1));
272
716
  rb_define_const(cCluster, "PROTOCOL_VERSION_V2", INT2NUM(CASS_PROTOCOL_VERSION_V2));
@@ -275,4 +719,16 @@ void Init_cluster(void)
275
719
  rb_define_const(cCluster, "PROTOCOL_VERSION_V5", INT2NUM(CASS_PROTOCOL_VERSION_V5));
276
720
  rb_define_const(cCluster, "PROTOCOL_VERSION_DSEV1", INT2NUM(CASS_PROTOCOL_VERSION_DSEV1));
277
721
  rb_define_const(cCluster, "PROTOCOL_VERSION_DSEV2", INT2NUM(CASS_PROTOCOL_VERSION_DSEV2));
722
+
723
+ rb_define_const(cCluster, "CONSISTENCY_ANY", INT2NUM(CASS_CONSISTENCY_ANY));
724
+ rb_define_const(cCluster, "CONSISTENCY_ONE", INT2NUM(CASS_CONSISTENCY_ONE));
725
+ rb_define_const(cCluster, "CONSISTENCY_TWO", INT2NUM(CASS_CONSISTENCY_TWO));
726
+ rb_define_const(cCluster, "CONSISTENCY_THREE", INT2NUM(CASS_CONSISTENCY_THREE));
727
+ rb_define_const(cCluster, "CONSISTENCY_QUORUM", INT2NUM(CASS_CONSISTENCY_QUORUM));
728
+ rb_define_const(cCluster, "CONSISTENCY_ALL", INT2NUM(CASS_CONSISTENCY_ALL));
729
+ rb_define_const(cCluster, "CONSISTENCY_LOCAL_QUORUM", INT2NUM(CASS_CONSISTENCY_LOCAL_QUORUM));
730
+ rb_define_const(cCluster, "CONSISTENCY_EACH_QUORUM", INT2NUM(CASS_CONSISTENCY_EACH_QUORUM));
731
+ rb_define_const(cCluster, "CONSISTENCY_SERIAL", INT2NUM(CASS_CONSISTENCY_SERIAL));
732
+ rb_define_const(cCluster, "CONSISTENCY_LOCAL_SERIAL", INT2NUM(CASS_CONSISTENCY_LOCAL_SERIAL));
733
+ rb_define_const(cCluster, "CONSISTENCY_LOCAL_ONE", INT2NUM(CASS_CONSISTENCY_LOCAL_ONE));
278
734
  }
@@ -428,7 +428,7 @@ static VALUE statement_snapshot_value(const CassDataType *data_type, VALUE value
428
428
 
429
429
  length = RARRAY_LEN(array);
430
430
  snapshot = rb_ary_new_capa(length);
431
- for (long i = 0; i < RARRAY_LEN(array); i++) {
431
+ for (long i = 0; i < length && i < RARRAY_LEN(array); i++) {
432
432
  rb_ary_push(snapshot, statement_snapshot_value(element_type, rb_ary_entry(array, i)));
433
433
  }
434
434
  return rb_ary_freeze(snapshot);
data/lib/ilios/version.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Ilios
4
- VERSION = '1.1.1'
4
+ VERSION = '1.1.2'
5
5
  public_constant :VERSION
6
6
 
7
7
  CASSANDRA_CPP_DRIVER_VERSION = '2.17.1'
data/sig/ilios.rbs CHANGED
@@ -21,6 +21,18 @@ module Ilios
21
21
  PROTOCOL_VERSION_DSEV1: Integer
22
22
  PROTOCOL_VERSION_DSEV2: Integer
23
23
 
24
+ CONSISTENCY_ANY: Integer
25
+ CONSISTENCY_ONE: Integer
26
+ CONSISTENCY_TWO: Integer
27
+ CONSISTENCY_THREE: Integer
28
+ CONSISTENCY_QUORUM: Integer
29
+ CONSISTENCY_ALL: Integer
30
+ CONSISTENCY_LOCAL_QUORUM: Integer
31
+ CONSISTENCY_EACH_QUORUM: Integer
32
+ CONSISTENCY_SERIAL: Integer
33
+ CONSISTENCY_LOCAL_SERIAL: Integer
34
+ CONSISTENCY_LOCAL_ONE: Integer
35
+
24
36
  def connect: () -> Ilios::Cassandra::Session
25
37
  def hosts: (Array[String]) -> self
26
38
  def port: (Integer) -> self
@@ -30,6 +42,23 @@ module Ilios
30
42
  def request_timeout: (Integer) -> self
31
43
  def resolve_timeout: (Integer) -> self
32
44
  def constant_speculative_execution_policy: (Integer, Integer) -> self
45
+ def credentials: (String, String) -> self
46
+ def consistency: (Integer | Symbol) -> self
47
+ def serial_consistency: (Integer | Symbol) -> self
48
+ def num_threads_io: (Integer) -> self
49
+ def queue_size_io: (Integer) -> self
50
+ def core_connections_per_host: (Integer) -> self
51
+ def constant_reconnect: (Integer) -> self
52
+ def exponential_reconnect: (Integer, Integer) -> self
53
+ def tcp_nodelay: (bool) -> self
54
+ def tcp_keepalive: (bool, Integer) -> self
55
+ def connection_heartbeat_interval: (Integer) -> self
56
+ def connection_idle_timeout: (Integer) -> self
57
+ def load_balance_round_robin: () -> self
58
+ def load_balance_dc_aware: (String) -> self
59
+ def token_aware_routing: (bool) -> self
60
+ def latency_aware_routing: (bool) -> self
61
+ def use_schema: (bool) -> self
33
62
  end
34
63
 
35
64
  class Session
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ilios
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 1.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Watson
@@ -58,7 +58,7 @@ metadata:
58
58
  homepage_uri: https://github.com/Watson1978/ilios
59
59
  source_code_uri: https://github.com/Watson1978/ilios
60
60
  bug_tracker_uri: https://github.com/Watson1978/ilios/issues
61
- documentation_uri: https://www.rubydoc.info/gems/ilios/1.1.1
61
+ documentation_uri: https://www.rubydoc.info/gems/ilios/1.1.2
62
62
  rubygems_mfa_required: 'true'
63
63
  rdoc_options: []
64
64
  require_paths: