right_support 2.8.8 → 2.8.9
Sign up to get free protection for your applications and to get access to all the features.
- data/VERSION +1 -1
- data/lib/right_support/db/cassandra_model.rb +46 -25
- data/right_support.gemspec +2 -2
- metadata +4 -4
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.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
|
-
#
|
410
|
-
#
|
411
|
-
#
|
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>>]
|
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
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
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
|
-
|
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
|
-
|
471
|
-
|
472
|
-
|
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
|
-
|
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
|
data/right_support.gemspec
CHANGED
@@ -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
|
+
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-
|
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:
|
4
|
+
hash: 61
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 2
|
8
8
|
- 8
|
9
|
-
-
|
10
|
-
version: 2.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-
|
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
|