right_support 2.8.8 → 2.8.9

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.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 2.8.8
1
+ 2.8.9
@@ -405,27 +405,30 @@ module RightSupport::DB
405
405
  rows
406
406
  end
407
407
 
408
- # This method is an attempt to circumvent the Cassandra gem limitation of returning only 100 columns for wide rows
409
- # This method returns only columns that are within the result set specified by a secondary index equality query
410
- # This method will iterate through chunks of rows of the resultset and it will yield to the caller all of the
411
- # columns in chunks of 1,000 until all of the columns have been retrieved
408
+ # This method is an attempt to circumvent the Cassandra gem limitation of returning only 100 columns for wide rows,
409
+ # and also to help reliably iterate through a column family when the node is busy and experiencing many timeouts.
410
+ #
411
+ # Internally, it uses Cassandra#get_indexed_slices to find rows that match your index constraint; when it finds
412
+ # a wide row (with more than 1,000 columns), it continues to iterate through the columns of that row
412
413
  #
413
414
  # == Parameters:
414
415
  # @param [String] index column name
415
416
  # @param [String] index column value
416
417
  #
417
418
  # == Yields:
418
- # @yield [Array<String, Array<CassandraThrift::ColumnOrSuperColumn>>] irray containing ndex column value passed in and an array of columns matching the index query
419
+ # @yield [Array<String, Array<CassandraThrift::ColumnOrSuperColumn>>] array containing index column value passed in and an array of columns matching the index query
419
420
  def stream_all_indexed_slices(index, key)
420
421
  expr = do_op(:create_idx_expr, index, key, "EQ")
421
-
422
422
  start_row = ''
423
- max_row_count = 100
424
- max_initial_column_count = 1000 # number of columns to retrieve in the initial 2ndary index search
425
- max_additional_column_count = 10000 # Number of columns to retrieve in a batch once we're targetting a single (long) row
426
423
 
427
424
  # Loop over all CF rows, with batches of X
428
425
  while (start_row != nil)
426
+ # Reset these to their initieral values on every iteration thru the loop, in case
427
+ # we backed off due to timeouts (see rescue clauses below)
428
+ max_row_count = 100 # how many rows to grab at once
429
+ max_initial_column_count = 1000 # how much to grab at first for each row
430
+ max_additional_column_count = 1000 # how much to grab in each chunk of a long row
431
+
429
432
  clause = do_op(:create_idx_clause, [expr], start_row, max_row_count)
430
433
 
431
434
  # Now, for each batch of rows, make sure don't ask for "ALL" columns of each row, to avoid hitting rows with a huge amount of columns,
@@ -433,16 +436,14 @@ module RightSupport::DB
433
436
  begin
434
437
  rows = self.conn.get_indexed_slices(column_family, clause, :count => max_initial_column_count)
435
438
  rescue Exception => e
436
- wrapped_timeout = e.is_a?(CassandraThrift::TimedOutException)
437
- unwrapped_timeout = e.is_a?(Thrift::TransportException) && (e.type == Thrift::TransportException::TIMED_OUT)
438
- unwrapped_disconnect = e.is_a?(Thrift::TransportException) && (e.type == Thrift::TransportException::NOT_OPEN)
439
-
440
- if (wrapped_timeout || unwrapped_timeout || unwrapped_disconnect) && (timeout_retries < retry_timeout)
441
- timeout_retries += 1
439
+ if retryable_read_timeout?(e)
440
+ logger.error "CassandraModel#stream_all_indexed_slices retrying get_indexed_slices with fewer rows/cols due to a %s: %s @ %s (cf='%s' start_row='%s' count=%d)" %
441
+ [e.class.name, e.message, e.backtrace.first, column_family, start_row, max_row_count]
442
+ max_row_count /= 10 if max_row_count > 1
443
+ max_initial_column_count /= 10 if max_initial_column_count > 1
442
444
  retry
443
445
  else
444
- timeout_retries = 0
445
- raise e
446
+ raise
446
447
  end
447
448
  end
448
449
 
@@ -467,16 +468,13 @@ module RightSupport::DB
467
468
  :start => last_column_name,
468
469
  :slices_not_hash => true ).first.columns[1..-1]
469
470
  rescue Exception => e
470
- wrapped_timeout = e.is_a?(CassandraThrift::TimedOutException)
471
- unwrapped_timeout = e.is_a?(Thrift::TransportException) && (e.type == Thrift::TransportException::TIMED_OUT)
472
- unwrapped_disconnect = e.is_a?(Thrift::TransportException) && (e.type == Thrift::TransportException::NOT_OPEN)
473
-
474
- if (wrapped_timeout || unwrapped_timeout || unwrapped_disconnect) && (timeout_retries < retry_timeout)
475
- timeout_retries += 1
471
+ if retryable_read_timeout?(e)
472
+ logger.error "CassandraModel#stream_all_indexed_slices retrying get_range with fewer rows/cols due to a %s: %s @ %s (cf='%s' row='%s' start='%s' count=%d)" %
473
+ [e.class.name, e.message, e.backtrace.first, column_family, row_key, last_column_name, max_additional_column_count]
474
+ max_additional_column_count /= 10 if max_additional_column_count > 1
476
475
  retry
477
476
  else
478
- timeout_retries = 0
479
- raise e
477
+ raise
480
478
  end
481
479
  end
482
480
 
@@ -693,6 +691,29 @@ module RightSupport::DB
693
691
  end
694
692
  end
695
693
 
694
+ # Determine whether an exception can safely be retried, if the operation being performed
695
+ # was a read (i.e. is idempotent).
696
+ # @param [Exception] e the exception in question
697
+ # @return [Boolean] true if yes, false otherwise
698
+ def retryable_read_timeout?(e)
699
+ # Thrift RPC timeout that has been "improved" by cassandra gem's moronic exception-wrapping
700
+ wrapped_timeout = e.is_a?(CassandraThrift::TimedOutException)
701
+
702
+ # I have no idea what these are, but they show up when we have a local socket-read timeout after 10s
703
+ # Probably due to this line from the thrift gem (in lib/thrift/transport/socket.rb)
704
+ # raise TransportException.new(TransportException::TIMED_OUT, "Socket: Timed out reading #{sz} bytes from #{@desc}")
705
+ # The TransportException gets wrapped into a CassandraThrift::Cassandra::Client::TransportException and in the process
706
+ # the type is lost and the message is promoted to "type"
707
+ bogus_timeout = e.is_a?(Thrift::TransportException) && e.type.is_a?(String) && e.type =~ /Timed out/i
708
+
709
+ # Thrift RPC timeout
710
+ unwrapped_timeout = e.is_a?(Thrift::TransportException) && (e.type == Thrift::TransportException::TIMED_OUT)
711
+
712
+ # Thrift socket disconnect
713
+ unwrapped_disconnect = e.is_a?(Thrift::TransportException) && (e.type == Thrift::TransportException::NOT_OPEN)
714
+
715
+ wrapped_timeout || bogus_timeout || unwrapped_timeout || unwrapped_disconnect
716
+ end
696
717
  end # self
697
718
 
698
719
  attr_accessor :key, :attributes
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "right_support"
8
- s.version = "2.8.8"
8
+ s.version = "2.8.9"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Tony Spataro", "Sergey Sergyenko", "Ryan Williamson", "Lee Kirchhoff", "Alexey Karpik", "Scott Messier"]
12
- s.date = "2014-01-09"
12
+ s.date = "2014-01-13"
13
13
  s.description = "A toolkit of useful, reusable foundation code created by RightScale."
14
14
  s.email = "support@rightscale.com"
15
15
  s.extra_rdoc_files = [
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: right_support
3
3
  version: !ruby/object:Gem::Version
4
- hash: 63
4
+ hash: 61
5
5
  prerelease:
6
6
  segments:
7
7
  - 2
8
8
  - 8
9
- - 8
10
- version: 2.8.8
9
+ - 9
10
+ version: 2.8.9
11
11
  platform: ruby
12
12
  authors:
13
13
  - Tony Spataro
@@ -20,7 +20,7 @@ autorequire:
20
20
  bindir: bin
21
21
  cert_chain: []
22
22
 
23
- date: 2014-01-09 00:00:00 Z
23
+ date: 2014-01-13 00:00:00 Z
24
24
  dependencies:
25
25
  - !ruby/object:Gem::Dependency
26
26
  version_requirements: &id001 !ruby/object:Gem::Requirement