Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nesting Bug When DynamoDB is Mid Provisioning Capacity #1406

Closed
cameck opened this issue Jan 31, 2017 · 24 comments
Closed

Nesting Bug When DynamoDB is Mid Provisioning Capacity #1406

cameck opened this issue Jan 31, 2017 · 24 comments

Comments

@cameck
Copy link

cameck commented Jan 31, 2017

I'm using this code to do bulk writes to DynamoDB:

@dynamodb ||= Aws::DynamoDB::Client.new

 def build_put_request(data)
    while data.any?
      # DynamoDB supports batchwrites of up to 25 at a time
      batch = data.slice!(0, 25)
      items = put_request_items(batch)
      write_to_dynamo(ENV['DYNAMO_TABLE'] => items)
    end
  end

  def put_request_items(batch)
    Array.new(batch.length) do |i|
      { put_request: item(batch[i]) }
    end
  end

  def item(batch)
    { item: {
      user: batch['user'],
      pic: batch['pic'],
      bio: batch['bio'],
      fav_movies: batch['movies'],
      fav_tv_shows: batch['fav_tv_shows'],
      age: batch['age'],
      premium_user: true,
      referral_source: batch['source']
    } }
  end

  def write_to_dynamo(items)
    tries ||= 5
    resp = @dynamodb.batch_write_item(request_items: items)
    handle_unprocessed_items(resp.unprocessed_items)
  rescue StandardError => e
    @logger.error("#{e}\nError on: #{{ request_items: items }}")
    sleep(2)
    retry unless (tries -= 1).zero?
    @logger.fatal('Fatal Dynamo Errors, Program Aborting')
    abort
  end

  def handle_unprocessed_items(unprocessed_items)
    if unprocessed_items.count > 0
      log_failure(unprocessed_items[ENV['DYNAMO_TABLE']]) if (@tries + 1) > 1
      sleep(@tries * 0.75)
      write_to_dynamo(unprocessed_items)
    else
      reset_tries
      print_sent_message
    end
  end

I get this error when I run out of provisioned capacity:
ERROR -- DynamoDB: The level of configured provisioned throughput for one or more global secondary indexes of the table was exceeded. Consider increasing your provisioning level for the under-provisioned global secondary indexes with the UpdateTable API

So now, on the retry (Another app updates the provisioned writes dynamically), I get this error:
ERROR -- DynamoDB: The provided key element does not match the schema

When I look at the data suddenly I have this odd nestings that appear:

{:request_items=>{"users"=>[#<struct Aws::DynamoDB::Types::WriteRequest put_request=#<struct Aws::DynamoDB::Types::PutRequest item={"age"=>{:m=>{"n"=>{:s=>"34"}}}, "user"=>{:m=>{"s"=>{:s=>"Uncle Bill"}}}, "pic"=>{:m=>{"s"=>{:s=>"https://s3.aws.sdfa3jlfsd.com"}}}, "bio"=>{:m=>{"s"=>{:s=>"lorem ipsum...."}}}, "premium_user"=>{:m=>{"bool"=>{:bool=>true}}} # etc...

The request should look like this as far as I understand:

{:request_items=>{"users"=>[#<struct Aws::DynamoDB::Types::WriteRequest put_request=#<struct Aws::DynamoDB::Types::PutRequest item={"age"=>{:n=>"34"}, "user"=>{:s=>"Uncle Bill"}, "pic"=>{:s=>"https://s3.aws.sdfa3jlfsd.com"}, "bio"=>{:s=>"lorem ipsum...."}, "premium_user"=>{:bool=>true} # etc...

This only seems to happen when I'm mid provisioning more capacity or I did something wrong?

AWS Gem Versions:

aws-sdk (2.6.44)
  aws-sdk-resources (= 2.6.44)
aws-sdk-core (2.6.44)
  aws-sigv4 (~> 1.0)
  jmespath (~> 1.0)
aws-sdk-resources (2.6.44)
  aws-sdk-core (= 2.6.44)
aws-sigv4 (1.0.0)
@awood45
Copy link
Member

awood45 commented Jan 31, 2017

I'll take a look to see if I can try and reproduce. Are you seeing this on the SDK's automatic retries, or on retries within your code?

If the latter, you should look for anything where you might be mutating your items hash, as that seems the most likely culprit. I'll investigate just in case the SDK is doing something odd on retries, but this is unlikely.

@awood45 awood45 added service-api General API label for AWS Services. Version 2 labels Jan 31, 2017
@cameck
Copy link
Author

cameck commented Feb 1, 2017

It's the second time the unprocessed items array hits the write_to_dynamo method.
The code I'm using is as above, I've stared long and hard at it, but I'm never surprised to find something I overlooked.
Thank you for your time 😃

@awood45
Copy link
Member

awood45 commented Feb 1, 2017

Actually, I think I see it now. It looks like you're taking the raw unprocessed items hash, and plumbing it right back in, and the simple attributes helper it making it a map one level deeper.

I'll work on an example of how you can avoid this later, but you can also avoid this by turning off simple attributes at the client level.

@cameck
Copy link
Author

cameck commented Feb 1, 2017

Hmm interesting I actually started off with this example by trevorrowe:

items = (1..25).map do |n|
  {
    put_request: {
      item: {
        "id" => "value#{n}"
      }
    }
  }
end

unprocessed_items = nil
100.times do
  # the target table I created with 1 write capacity units to ensure I will be throttled on batch write
  r = dynamodb.batch_write_item(request_items: { "aws-sdk-slow": items })
  if r.unprocessed_items.count > 0
    unprocessed_items = r.unprocessed_items
    break
  end
end

dynamodb.batch_write_item(request_items: unprocessed_items)

So, I was under the impression I could just throw it back in without disabling simple_atributes, but it should be more like this:

@dynamodb ||= Aws::DynamoDB::Client.new(simple_attributes: false)

def item(batch)
  { item: {
    user: { s: batch['user'] },
    pic: { s: batch['pic'] },
    bio: { s: batch['bio'] },
    fav_movie: { s: batch['movie'] },
    fav_tv_show: { s: batch['fav_tv_shows'] },
    age: { n: batch['age'] },
    premium_user: { bool: true },
    referral_source: { s: batch['source'] }
  } }
end

@awood45
Copy link
Member

awood45 commented Feb 2, 2017

I'm taking a look at that now, because you should be right about that.

@awood45
Copy link
Member

awood45 commented Feb 2, 2017

Okay, I ran code substantially similar to your example above, and had no issues - the items were put in the format you would expect (with simple attributes ON). Looking now for differences in behavior.

@awood45
Copy link
Member

awood45 commented Feb 2, 2017

Question: Are you able to reproduce this on demand? If so, it would be interesting to see the response shape you're getting back from unprocessed items. For example, if you captured it in byebug to see if it matches what you would expect.

@cameck
Copy link
Author

cameck commented Feb 2, 2017

Ok, so I reproduced it again and added some more logging. I was also a bit suspicious of the handle_unprocessed_items method being inside the rescue loop but that didn't make a difference either way.

Here's the updated code with all kinds of logging:

  def write_to_dynamo(items)
    begin
      tries ||= 5
      resp = @dynamodb.batch_write_item(request_items: items)
      @logger.info("Dynamoe Respones: #{resp}")
    rescue StandardError => e
      @logger.error("#{e}\nError on: #{{ request_items: items }}")
      @logger.error("Dynamo Response: #{resp}")
      sleep(2)
      retry unless (tries -= 1).zero?
      @logger.fatal('Fatal Dynamo Errors, Programming Aborting')
      abort
    end
    handle_unprocessed_items(resp.unprocessed_items)
  end

  def handle_unprocessed_items(unprocessed_items)
    if unprocessed_items.count > 0
      (@tries + 1)
      log_failure(unprocessed_items[ENV['DYNAMO_TABLE']])
      @logger.info('Printing unprocessed_items')
      p unprocessed_items
      sleep(@tries * 0.75)
      write_to_dynamo(unprocessed_items)
    else
      reset_tries
      print_sent_message
    end
  end

Logs:

I, [2017-02-02T22:19:39.682227 #1]  INFO -- DynamoDB: 2 failed to make it resending...
I, [2017-02-02T22:19:39.682265 #1]  INFO -- DynamoDB: Printing unprocessed_items
{"user-table"=>[#<struct Aws::DynamoDB::Types::WriteRequest put_request=#<struct Aws::DynamoDB::Types::PutRequest item={"age"=>#<BigDecimal:5640b85b8868,'0.1486073977E10',18(27)>, "user"=>"jim", "pic"=>"https://s3.afsldjfl", "bio"=>"unknown", "premium_user"=>true, "referral_source"=>"google", "sign_up_date"=>#<BigDecimal:5640b85a7068,'0.1536E5',9(18)>, "fav_movie"=>"gossip girl"}>, delete_request=nil>, #<struct Aws::DynamoDB::Types::WriteRequest put_request=#<struct Aws::DynamoDB::Types::PutRequest item={"age"=>#<BigDecimal:5640b85a62f8,'0.1486073977E10',18(27)>, "user"=>"pete", "pic"=>"https://s3.sdf8yr4wjla", "bio"=>"unknown", "premium_user"=>true, "referral_source"=>"bing", "sign_up_date"=>#<BigDecimal:5640b85a4db8,'0.42E2',9(18)>, "fav_movie"=>"alien"}>, delete_request=nil>]}
I, [2017-02-02T22:19:39.808993 #1]  INFO -- DynamoDB: Dynamoe Respones: #<struct Aws::DynamoDB::Types::BatchWriteItemOutput unprocessed_items={"user-table"=>[#<struct Aws::DynamoDB::Types::WriteRequest put_request=#<struct Aws::DynamoDB::Types::PutRequest item={"age"=>#<BigDecimal:5640b84b5cb8,'0.1486073977E10',18(27)>, "user"=>"pete", "pic"=>"https://s3.sdf8yr4wjla", "bio"=>"unknown", "premium_user"=>true, "referral_source"=>"bing", "sign_up_date"=>#<BigDecimal:5640b84b42f0,'0.42E2',9(18)>, "fav_movie"=>"alien"}>, delete_request=nil>]}, item_collection_metrics=nil, consumed_capacity=nil>
I, [2017-02-02T22:19:39.809222 #1]  INFO -- DynamoDB: 1 failed to make it resending...
I, [2017-02-02T22:19:39.809296 #1]  INFO -- DynamoDB: Printing unprocessed_items
{"user-table"=>[#<struct Aws::DynamoDB::Types::WriteRequest put_request=#<struct Aws::DynamoDB::Types::PutRequest item={"age"=>#<BigDecimal:5640b84b5cb8,'0.1486073977E10',18(27)>, "user"=>"pete", "pic"=>"https://s3.sdf8yr4wjla", "bio"=>"unknown", "premium_user"=>true, "referral_source"=>"bing", "sign_up_date"=>#<BigDecimal:5640b84b42f0,'0.42E2',9(18)>, "fav_movie"=>"alien"}>, delete_request=nil>]}
E, [2017-02-02T22:20:07.597396 #1] ERROR -- DynamoDB: The level of configured provisioned throughput for the table was exceeded. Consider increasing your provisioning level with the UpdateTable API.
Error on: {:request_items=>{"user-table"=>[#<struct Aws::DynamoDB::Types::WriteRequest put_request=#<struct Aws::DynamoDB::Types::PutRequest item={"age"=>{:n=>"0.1486073977E10"}, "user"=>{:s=>"pete"}, "pic"=>{:s=>"https://s3.sdf8yr4wjla"}, "bio"=>{:s=>"unknown"}, "premium_user"=>{:bool=>true}, "referral_source"=>{:s=>"bing"}, "sign_up_date"=>{:n=>"0.42E2"}, "fav_movie"=>{:s=>"alien"}}>, delete_request=nil>]}}
E, [2017-02-02T22:20:07.597621 #1] ERROR -- DynamoDB: Dynamo Response: 
E, [2017-02-02T22:20:09.853818 #1] ERROR -- DynamoDB: The provided key element does not match the schema
Error on: {:request_items=>{"user-table"=>[#<struct Aws::DynamoDB::Types::WriteRequest put_request=#<struct Aws::DynamoDB::Types::PutRequest item={"age"=>{:m=>{"n"=>{:s=>"0.1486073977E10"}}}, "user"=>{:m=>{"s"=>{:s=>"pete"}}}, "pic"=>{:m=>{"s"=>{:s=>"https://s3.sdf8yr4wjla"}}}, "bio"=>{:m=>{"s"=>{:s=>"unknown"}}}, "premium_user"=>{:m=>{"bool"=>{:bool=>true}}}, "referral_source"=>{:m=>{"s"=>{:s=>"bing"}}}, "sign_up_date"=>{:m=>{"n"=>{:s=>"0.42E2"}}}, "fav_movie"=>{:m=>{"s"=>{:s=>"alien"}}}}>, delete_request=nil>]}}
E, [2017-02-02T22:20:09.854041 #1] ERROR -- DynamoDB: Dynamo Response: 
E, [2017-02-02T22:20:12.166887 #1] ERROR -- DynamoDB: The provided key element does not match the schema
Error on: {:request_items=>{"user-table"=>[#<struct Aws::DynamoDB::Types::WriteRequest put_request=#<struct Aws::DynamoDB::Types::PutRequest item={"age"=>{:m=>{"m"=>{:m=>{"n"=>{:m=>{"s"=>{:s=>"0.1486073977E10"}}}}}}}, "user"=>{:m=>{"m"=>{:m=>{"s"=>{:m=>{"s"=>{:s=>"pete"}}}}}}}, "pic"=>{:m=>{"m"=>{:m=>{"s"=>{:m=>{"s"=>{:s=>"https://s3.sdf8yr4wjla"}}}}}}}, "bio"=>{:m=>{"m"=>{:m=>{"s"=>{:m=>{"s"=>{:s=>"unknown"}}}}}}}, "premium_user"=>{:m=>{"m"=>{:m=>{"bool"=>{:m=>{"bool"=>{:bool=>true}}}}}}}, "referral_source"=>{:m=>{"m"=>{:m=>{"s"=>{:m=>{"s"=>{:s=>"bing"}}}}}}}, "sign_up_date"=>{:m=>{"m"=>{:m=>{"n"=>{:m=>{"s"=>{:s=>"0.42E2"}}}}}}}, "fav_movie"=>{:m=>{"m"=>{:m=>{"s"=>{:m=>{"s"=>{:s=>"alien"}}}}}}}}>, delete_request=nil>]}}
E, [2017-02-02T22:20:12.167116 #1] ERROR -- DynamoDB: Dynamo Response: 
E, [2017-02-02T22:20:14.342812 #1] ERROR -- DynamoDB: The provided key element does not match the schema
Error on: {:request_items=>{"user-table"=>[#<struct Aws::DynamoDB::Types::WriteRequest put_request=#<struct Aws::DynamoDB::Types::PutRequest item={"age"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"n"=>{:m=>{"m"=>{:m=>{"s"=>{:m=>{"s"=>{:s=>"0.1486073977E10"}}}}}}}}}}}}}}}, "user"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"s"=>{:m=>{"m"=>{:m=>{"s"=>{:m=>{"s"=>{:s=>"pete"}}}}}}}}}}}}}}}, "pic"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"s"=>{:m=>{"m"=>{:m=>{"s"=>{:m=>{"s"=>{:s=>"https://s3.sdf8yr4wjla"}}}}}}}}}}}}}}}, "bio"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"s"=>{:m=>{"m"=>{:m=>{"s"=>{:m=>{"s"=>{:s=>"unknown"}}}}}}}}}}}}}}}, "premium_user"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"bool"=>{:m=>{"m"=>{:m=>{"bool"=>{:m=>{"bool"=>{:bool=>true}}}}}}}}}}}}}}}, "referral_source"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"s"=>{:m=>{"m"=>{:m=>{"s"=>{:m=>{"s"=>{:s=>"bing"}}}}}}}}}}}}}}}, "sign_up_date"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"n"=>{:m=>{"m"=>{:m=>{"s"=>{:m=>{"s"=>{:s=>"0.42E2"}}}}}}}}}}}}}}}, "fav_movie"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"s"=>{:m=>{"m"=>{:m=>{"s"=>{:m=>{"s"=>{:s=>"alien"}}}}}}}}}}}}}}}}>, delete_request=nil>]}}
E, [2017-02-02T22:20:14.343118 #1] ERROR -- DynamoDB: Dynamo Response: 
E, [2017-02-02T22:20:16.570434 #1] ERROR -- DynamoDB: The provided key element does not match the schema
Error on: {:request_items=>{"user-table"=>[#<struct Aws::DynamoDB::Types::WriteRequest put_request=#<struct Aws::DynamoDB::Types::PutRequest item={"age"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"n"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"s"=>{:m=>{"m"=>{:m=>{"s"=>{:m=>{"s"=>{:s=>"0.1486073977E10"}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}, "user"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"s"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"s"=>{:m=>{"m"=>{:m=>{"s"=>{:m=>{"s"=>{:s=>"pete"}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}, "pic"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"s"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"s"=>{:m=>{"m"=>{:m=>{"s"=>{:m=>{"s"=>{:s=>"https://s3.sdf8yr4wjla"}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}, "bio"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"s"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"s"=>{:m=>{"m"=>{:m=>{"s"=>{:m=>{"s"=>{:s=>"unknown"}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}, "premium_user"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"bool"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"bool"=>{:m=>{"m"=>{:m=>{"bool"=>{:m=>{"bool"=>{:bool=>true}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}, "referral_source"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"s"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"s"=>{:m=>{"m"=>{:m=>{"s"=>{:m=>{"s"=>{:s=>"bing"}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}, "sign_up_date"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"n"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"s"=>{:m=>{"m"=>{:m=>{"s"=>{:m=>{"s"=>{:s=>"0.42E2"}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}, "fav_movie"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"s"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"m"=>{:m=>{"s"=>{:m=>{"m"=>{:m=>{"s"=>{:m=>{"s"=>{:s=>"alien"}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}>, delete_request=nil>]}}
E, [2017-02-02T22:20:16.570675 #1] ERROR -- DynamoDB: Dynamo Response: 
F, [2017-02-02T22:20:18.576291 #1] FATAL -- DynamoDB: Fatal Dynamo Errors, Programming Aborting

I hope that helps you as it still seems odd to me 😄

@cameck
Copy link
Author

cameck commented Feb 3, 2017

By the way, I can reproduce this by just setting the writes to one and having two Ruby workers running against the same Dynamo Table.

@cameck
Copy link
Author

cameck commented Feb 3, 2017

Ok, so it looks like when it hits the retry logic, it retries the transmuted array rather than the original array.
And apparently that is semi-expected based on what retry is doing.
screen shot 2017-02-03 at 11 54 44
Source

@awood45
Copy link
Member

awood45 commented Feb 10, 2017

That's interesting, I can try to recreate the error by using the retry keyword.

@awood45
Copy link
Member

awood45 commented Feb 10, 2017

So, I made a simple example to try and recreate this, and I'm not getting the same issue - retries are as expected:

require 'aws-sdk'
require 'securerandom'
require 'byebug'

def writer
  client = Aws::DynamoDB::Client.new
  1000.times do
    batch = []
    25.times do
      batch << {
        put_request: {
          item: {
            "uuid" => SecureRandom.uuid,
            "int" => rand(1000),
            "map" => {
              "m1" => { "a" => rand(25) },
              "m2" => { "b" => rand(25) },
              "m3" => { "c" => rand(25) }
            }
          }
        }
      }
    end
    @retry_flag = false
    begin
      if @retry_flag
        byebug
      end
      resp = client.batch_write_item(
        request_items: {
          "BatchModel" => batch
        }
      )
    rescue StandardError => e
      @retry_flag = true
      retry
    end
  end
end

one = Thread.new { writer }
two = Thread.new { writer }
thr = Thread.new { writer }
one.join
two.join
thr.join

When this eventually fails and I hit the byebug component, batch is as expected and the retried call works.

I can try to dive a bit deeper in to your code, but so far I'm still unable to recreate the issue.

@awood45
Copy link
Member

awood45 commented Feb 10, 2017

We also have a bit of a hunch as to a possible source. Checking that now.

@awood45
Copy link
Member

awood45 commented Feb 10, 2017

Okay, I do see a possible bug source here. The code works under normal circumstances, but it's possible you're getting an unusual error, like perhaps a network error, under production load and our plugin stack is consuming the mutated hash for a built-in retry.

In our Aws::Plugins::DynamoDBSimpleAttributes class, the param hash gets mutated. If we go through the full stack, this mutation happens in both directions and there's no problem, but it looks like in some cases we might mutate it twice.

Still investigating.

@awood45 awood45 added bug and removed information requested service-api General API label for AWS Services. labels Feb 10, 2017
@awood45
Copy link
Member

awood45 commented Feb 20, 2017

Quick note - this is the top priority bug we have remaining, but it is proving rather hard to repro for failing test purposes. Still working on it.

@cameck
Copy link
Author

cameck commented Feb 21, 2017

No worries 😃 .
If there's anything I can help with, let me know. I'm happy to help.

@awood45
Copy link
Member

awood45 commented Feb 21, 2017

If you have a small reproducing example, possibly with wire trace examples, that would help. Still investigating even if that is hard to get.

@cameck
Copy link
Author

cameck commented Feb 23, 2017

Hey @awood45 sorry for the delay. Going out of town, will work to reproduce over the weekend.

@cameck
Copy link
Author

cameck commented Feb 28, 2017

Okay finally made a separate reproducible example (this is all the code needed to reproduce again):

require 'aws-sdk'
require 'byebug'
require 'logger'

@dynamo_table = 'test-throttle'
@dynamodb ||= Aws::DynamoDB::Client.new(profile: 'dev')
@logger = Logger.new(STDOUT).tap { |log| log.progname = 'DynamoDB' }
@logger.level = Logger::INFO
@tries = 0
@https_tries = 0
@spot = 0
@users = *('0'..'50')

def build_put_request(data)
  while data.any?
    batch = data.slice!(0, 25)
    items = put_request_items(batch)
    write_to_dynamo(@dynamo_table => items)
  end
end

def put_request_items(batch)
  Array.new(batch.length) { |_i| { put_request: item } }
end

def item
  @spot < 50 ? @spot += 1 : @spot = 0
  { item: {
    user: @users[@spot],
    pic: 'great pic',
    bio: 'such words',
    fav_movies: ['balto', 'the lion king', 'swingers'],
    fav_tv_shows: ['spongebob', 'dexter', 'stranger things'],
    age: SecureRandom.uuid,
    premium_user: true,
    referral_source: 'Johnson & Jonson'
  } }
end

def write_to_dynamo(items)
  byebug if @https_tries > 1
  resp = @dynamodb.batch_write_item(request_items: items)
  handle_unprocessed_items(resp.unprocessed_items)
rescue StandardError => e
  @logger.error("#{e}\nError on: #{{ request_items: items }}")
  @https_tries += 1
  retry
end

def handle_unprocessed_items(unprocessed_items)
  if unprocessed_items.count > 0
    log_failure(unprocessed_items[@dynamo_table]) if (@tries + 1) > 1
    sleep(@tries * 0.75)
    write_to_dynamo(unprocessed_items)
  else
    @tries = 0
    @logger.info('Sent 25 items')
  end
end

def make_dummies
  build_put_request([*(1..10_000)])
end

make_dummies

Logs

I, [2017-02-27T18:11:56.523010 #25539]  INFO -- DynamoDB: Sent 25 items
I, [2017-02-27T18:12:21.721349 #25539]  INFO -- DynamoDB: Sent 25 items
I, [2017-02-27T18:12:46.775433 #25539]  INFO -- DynamoDB: Sent 25 items
I, [2017-02-27T18:13:11.720228 #25539]  INFO -- DynamoDB: Sent 25 items
E, [2017-02-28T06:13:57.010722 #25539] ERROR -- DynamoDB: Encountered a `SocketError` while attempting to connect to:

  https://dynamodb.us-east-1.amazonaws.com

This is typically the result of an invalid `:region` option or a
poorly formatted `:endpoint` option.

* Avoid configuring the `:endpoint` option directly. Endpoints are constructed
  from the `:region`. The `:endpoint` option is reserved for connecting to
  non-standard test endpoints.

* Not every service is available in every region.

* Never suffix region names with availability zones.
  Use "us-east-1", not "us-east-1a"

Known AWS regions include (not specific to this service):

ap-northeast-1
ap-northeast-2
ap-south-1
ap-southeast-1
ap-southeast-2
ca-central-1
eu-central-1
eu-west-1
eu-west-2
sa-east-1
us-east-1
us-east-2
us-west-1
us-west-2
cn-north-1
us-gov-west-1

Error on: {:request_items=>{"test-throttle"=>[#<struct Aws::DynamoDB::Types::WriteRequest put_request=#<struct Aws::DynamoDB::Types::PutRequest item={"premium_user"=>{:bool=>true}, "fav_tv_shows"=>{:l=>[{:s=>"spongebob"}, {:s=>"dexter"}, {:s=>"stranger things"}]}, "referral_source"=>{:s=>"Johnson & Jonson"}, "fav_movies"=>{:l=>[{:s=>"balto"}, {:s=>"the lion king"}, {:s=>"swingers"}]}, "bio"=>{:s=>"such words"}, "pic"=>{:s=>"great pic"}, "user"=>{:s=>"41"}, "age"=>{:s=>"7e06c5af-6ca2-4cb2-a248-c4521da8ce1a"}}>, delete_request=nil>, #<struct Aws::DynamoDB::Types::WriteRequest put_request=#<struct Aws::DynamoDB::Types::PutRequest item={"premium_user"=>{:bool=>true}, "fav_tv_shows"=>{:l=>[{:s=>"spongebob"}, {:s=>"dexter"}, {:s=>"stranger things"}]}, "referral_source"=>{:s=>"Johnson & Jonson"}, "fav_movies"=>{:l=>[{:s=>"balto"}, {:s=>"the lion king"}, {:s=>"swingers"}]}, "bio"=>{:s=>"such words"}, "pic"=>{:s=>"great pic"}, "user"=>{:s=>"42"}, "age"=>{:s=>"52b6169f-7310-4ae8-bbbd-c51e07828978"}}>, delete_request=nil>, #<struct Aws::DynamoDB::Types::WriteRequest put_request=#<struct Aws::DynamoDB::Types::PutRequest item={"premium_user"=>{:bool=>true}, "fav_tv_shows"=>{:l=>[{:s=>"spongebob"}, {:s=>"dexter"}, {:s=>"stranger things"}]}, "referral_source"=>{:s=>"Johnson & Jonson"}, "fav_movies"=>{:l=>[{:s=>"balto"}, {:s=>"the lion king"}, {:s=>"swingers"}]}, "bio"=>{:s=>"such words"}, "pic"=>{:s=>"great pic"}, "user"=>{:s=>"43"}, "age"=>{:s=>"90482103-ba88-4bde-93d0-dc4f3c01b0d6"}}>, delete_request=nil>, #<struct Aws::DynamoDB::Types::WriteRequest put_request=#<struct Aws::DynamoDB::Types::PutRequest item={"premium_user"=>{:bool=>true}, "fav_tv_shows"=>{:l=>[{:s=>"spongebob"}, {:s=>"dexter"}, {:s=>"stranger things"}]}, "referral_source"=>{:s=>"Johnson & Jonson"}, "fav_movies"=>{:l=>[{:s=>"balto"}, {:s=>"the lion king"}, {:s=>"swingers"}]}, "bio"=>{:s=>"such words"}, "pic"=>{:s=>"great pic"}, "user"=>{:s=>"44"}, "age"=>{:s=>"ea6f2626-f0e5-4d16-a3a9-3e00984ab6e8"}}>, delete_request=nil>, #<struct Aws::DynamoDB::Types::WriteRequest put_request=#<struct Aws::DynamoDB::Types::PutRequest item={"premium_user"=>{:bool=>true}, "fav_tv_shows"=>{:l=>[{:s=>"spongebob"}, {:s=>"dexter"}, {:s=>"stranger things"}]}, "referral_source"=>{:s=>"Johnson & Jonson"}, "fav_movies"=>{:l=>[{:s=>"balto"}, {:s=>"the lion king"}, {:s=>"swingers"}]}, "bio"=>{:s=>"such words"}, "pic"=>{:s=>"great pic"}, "user"=>{:s=>"45"}, "age"=>{:s=>"43c0d7a1-e11c-4214-a2eb-2af9979f65a9"}}>, delete_request=nil>, #<struct Aws::DynamoDB::Types::WriteRequest put_request=#<struct Aws::DynamoDB::Types::PutRequest item={"premium_user"=>{:bool=>true}, "fav_tv_shows"=>{:l=>[{:s=>"spongebob"}, {:s=>"dexter"}, {:s=>"stranger things"}]}, "referral_source"=>{:s=>"Johnson & Jonson"}, "fav_movies"=>{:l=>[{:s=>"balto"}, {:s=>"the lion king"}, {:s=>"swingers"}]}, "bio"=>{:s=>"such words"}, "pic"=>{:s=>"great pic"}, "user"=>{:s=>"46"}, "age"=>{:s=>"95120315-5ad7-46fc-bd8d-3569e3b3ea65"}}>, delete_request=nil>, #<struct Aws::DynamoDB::Types::WriteRequest put_request=#<struct Aws::DynamoDB::Types::PutRequest item={"premium_user"=>{:bool=>true}, "fav_tv_shows"=>{:l=>[{:s=>"spongebob"}, {:s=>"dexter"}, {:s=>"stranger things"}]}, "referral_source"=>{:s=>"Johnson & Jonson"}, "fav_movies"=>{:l=>[{:s=>"balto"}, {:s=>"the lion king"}, {:s=>"swingers"}]}, "bio"=>{:s=>"such words"}, "pic"=>{:s=>"great pic"}, "user"=>{:s=>"47"}, "age"=>{:s=>"ecc48617-d8c8-4332-9bf7-6a540cf0e394"}}>, delete_request=nil>, #<struct Aws::DynamoDB::Types::WriteRequest put_request=#<struct Aws::DynamoDB::Types::PutRequest item={"premium_user"=>{:bool=>true}, "fav_tv_shows"=>{:l=>[{:s=>"spongebob"}, {:s=>"dexter"}, {:s=>"stranger things"}]}, "referral_source"=>{:s=>"Johnson & Jonson"}, "fav_movies"=>{:l=>[{:s=>"balto"}, {:s=>"the lion king"}, {:s=>"swingers"}]}, "bio"=>{:s=>"such words"}, "pic"=>{:s=>"great pic"}, "user"=>{:s=>"48"}, "age"=>{:s=>"fb889f20-c3a0-48e3-80eb-645a7d6e9bc7"}}>, delete_request=nil>, #<struct Aws::DynamoDB::Types::WriteRequest put_request=#<struct Aws::DynamoDB::Types::PutRequest item={"premium_user"=>{:bool=>true}, "fav_tv_shows"=>{:l=>[{:s=>"spongebob"}, {:s=>"dexter"}, {:s=>"stranger things"}]}, "referral_source"=>{:s=>"Johnson & Jonson"}, "fav_movies"=>{:l=>[{:s=>"balto"}, {:s=>"the lion king"}, {:s=>"swingers"}]}, "bio"=>{:s=>"such words"}, "pic"=>{:s=>"great pic"}, "user"=>{:s=>"49"}, "age"=>{:s=>"17af13b7-dbdc-4784-b3fe-9cef7dc31506"}}>, delete_request=nil>, #<struct Aws::DynamoDB::Types::WriteRequest put_request=#<struct Aws::DynamoDB::Types::PutRequest item={"premium_user"=>{:bool=>true}, "fav_tv_shows"=>{:l=>[{:s=>"spongebob"}, {:s=>"dexter"}, {:s=>"stranger things"}]}, "referral_source"=>{:s=>"Johnson & Jonson"}, "fav_movies"=>{:l=>[{:s=>"balto"}, {:s=>"the lion king"}, {:s=>"swingers"}]}, "bio"=>{:s=>"such words"}, "pic"=>{:s=>"great pic"}, "user"=>{:s=>"50"}, "age"=>{:s=>"ed2cfa67-31df-4732-95a4-560aba5af449"}}>, delete_request=nil>]}}
E, [2017-02-28T06:13:57.501219 #25539] ERROR -- DynamoDB: Encountered a `SocketError` while attempting to connect to:

  https://dynamodb.us-east-1.amazonaws.com

This is typically the result of an invalid `:region` option or a
poorly formatted `:endpoint` option.

* Avoid configuring the `:endpoint` option directly. Endpoints are constructed
  from the `:region`. The `:endpoint` option is reserved for connecting to
  non-standard test endpoints.

* Not every service is available in every region.

* Never suffix region names with availability zones.
  Use "us-east-1", not "us-east-1a"

Known AWS regions include (not specific to this service):

ap-northeast-1
ap-northeast-2
ap-south-1
ap-southeast-1
ap-southeast-2
ca-central-1
eu-central-1
eu-west-1
eu-west-2
sa-east-1
us-east-1
us-east-2
us-west-1
us-west-2
cn-north-1
us-gov-west-1

Error on: {:request_items=>{"test-throttle"=>[#<struct Aws::DynamoDB::Types::WriteRequest put_request=#<struct Aws::DynamoDB::Types::PutRequest item={"premium_user"=>{:m=>{"bool"=>{:bool=>true}}}, "fav_tv_shows"=>{:m=>{"l"=>{:l=>[{:m=>{"s"=>{:s=>"spongebob"}}}, {:m=>{"s"=>{:s=>"dexter"}}}, {:m=>{"s"=>{:s=>"stranger things"}}}]}}}, "referral_source"=>{:m=>{"s"=>{:s=>"Johnson & Jonson"}}}, "fav_movies"=>{:m=>{"l"=>{:l=>[{:m=>{"s"=>{:s=>"balto"}}}, {:m=>{"s"=>{:s=>"the lion king"}}}, {:m=>{"s"=>{:s=>"swingers"}}}]}}}, "bio"=>{:m=>{"s"=>{:s=>"such words"}}}, "pic"=>{:m=>{"s"=>{:s=>"great pic"}}}, "user"=>{:m=>{"s"=>{:s=>"41"}}}, "age"=>{:m=>{"s"=>{:s=>"7e06c5af-6ca2-4cb2-a248-c4521da8ce1a"}}}}>, delete_request=nil>, #<struct Aws::DynamoDB::Types::WriteRequest put_request=#<struct Aws::DynamoDB::Types::PutRequest item={"premium_user"=>{:m=>{"bool"=>{:bool=>true}}}, "fav_tv_shows"=>{:m=>{"l"=>{:l=>[{:m=>{"s"=>{:s=>"spongebob"}}}, {:m=>{"s"=>{:s=>"dexter"}}}, {:m=>{"s"=>{:s=>"stranger things"}}}]}}}, "referral_source"=>{:m=>{"s"=>{:s=>"Johnson & Jonson"}}}, "fav_movies"=>{:m=>{"l"=>{:l=>[{:m=>{"s"=>{:s=>"balto"}}}, {:m=>{"s"=>{:s=>"the lion king"}}}, {:m=>{"s"=>{:s=>"swingers"}}}]}}}, "bio"=>{:m=>{"s"=>{:s=>"such words"}}}, "pic"=>{:m=>{"s"=>{:s=>"great pic"}}}, "user"=>{:m=>{"s"=>{:s=>"42"}}}, "age"=>{:m=>{"s"=>{:s=>"52b6169f-7310-4ae8-bbbd-c51e07828978"}}}}>, delete_request=nil>, #<struct Aws::DynamoDB::Types::WriteRequest put_request=#<struct Aws::DynamoDB::Types::PutRequest item={"premium_user"=>{:m=>{"bool"=>{:bool=>true}}}, "fav_tv_shows"=>{:m=>{"l"=>{:l=>[{:m=>{"s"=>{:s=>"spongebob"}}}, {:m=>{"s"=>{:s=>"dexter"}}}, {:m=>{"s"=>{:s=>"stranger things"}}}]}}}, "referral_source"=>{:m=>{"s"=>{:s=>"Johnson & Jonson"}}}, "fav_movies"=>{:m=>{"l"=>{:l=>[{:m=>{"s"=>{:s=>"balto"}}}, {:m=>{"s"=>{:s=>"the lion king"}}}, {:m=>{"s"=>{:s=>"swingers"}}}]}}}, "bio"=>{:m=>{"s"=>{:s=>"such words"}}}, "pic"=>{:m=>{"s"=>{:s=>"great pic"}}}, "user"=>{:m=>{"s"=>{:s=>"43"}}}, "age"=>{:m=>{"s"=>{:s=>"90482103-ba88-4bde-93d0-dc4f3c01b0d6"}}}}>, delete_request=nil>, #<struct Aws::DynamoDB::Types::WriteRequest put_request=#<struct Aws::DynamoDB::Types::PutRequest item={"premium_user"=>{:m=>{"bool"=>{:bool=>true}}}, "fav_tv_shows"=>{:m=>{"l"=>{:l=>[{:m=>{"s"=>{:s=>"spongebob"}}}, {:m=>{"s"=>{:s=>"dexter"}}}, {:m=>{"s"=>{:s=>"stranger things"}}}]}}}, "referral_source"=>{:m=>{"s"=>{:s=>"Johnson & Jonson"}}}, "fav_movies"=>{:m=>{"l"=>{:l=>[{:m=>{"s"=>{:s=>"balto"}}}, {:m=>{"s"=>{:s=>"the lion king"}}}, {:m=>{"s"=>{:s=>"swingers"}}}]}}}, "bio"=>{:m=>{"s"=>{:s=>"such words"}}}, "pic"=>{:m=>{"s"=>{:s=>"great pic"}}}, "user"=>{:m=>{"s"=>{:s=>"44"}}}, "age"=>{:m=>{"s"=>{:s=>"ea6f2626-f0e5-4d16-a3a9-3e00984ab6e8"}}}}>, delete_request=nil>, #<struct Aws::DynamoDB::Types::WriteRequest put_request=#<struct Aws::DynamoDB::Types::PutRequest item={"premium_user"=>{:m=>{"bool"=>{:bool=>true}}}, "fav_tv_shows"=>{:m=>{"l"=>{:l=>[{:m=>{"s"=>{:s=>"spongebob"}}}, {:m=>{"s"=>{:s=>"dexter"}}}, {:m=>{"s"=>{:s=>"stranger things"}}}]}}}, "referral_source"=>{:m=>{"s"=>{:s=>"Johnson & Jonson"}}}, "fav_movies"=>{:m=>{"l"=>{:l=>[{:m=>{"s"=>{:s=>"balto"}}}, {:m=>{"s"=>{:s=>"the lion king"}}}, {:m=>{"s"=>{:s=>"swingers"}}}]}}}, "bio"=>{:m=>{"s"=>{:s=>"such words"}}}, "pic"=>{:m=>{"s"=>{:s=>"great pic"}}}, "user"=>{:m=>{"s"=>{:s=>"45"}}}, "age"=>{:m=>{"s"=>{:s=>"43c0d7a1-e11c-4214-a2eb-2af9979f65a9"}}}}>, delete_request=nil>, #<struct Aws::DynamoDB::Types::WriteRequest put_request=#<struct Aws::DynamoDB::Types::PutRequest item={"premium_user"=>{:m=>{"bool"=>{:bool=>true}}}, "fav_tv_shows"=>{:m=>{"l"=>{:l=>[{:m=>{"s"=>{:s=>"spongebob"}}}, {:m=>{"s"=>{:s=>"dexter"}}}, {:m=>{"s"=>{:s=>"stranger things"}}}]}}}, "referral_source"=>{:m=>{"s"=>{:s=>"Johnson & Jonson"}}}, "fav_movies"=>{:m=>{"l"=>{:l=>[{:m=>{"s"=>{:s=>"balto"}}}, {:m=>{"s"=>{:s=>"the lion king"}}}, {:m=>{"s"=>{:s=>"swingers"}}}]}}}, "bio"=>{:m=>{"s"=>{:s=>"such words"}}}, "pic"=>{:m=>{"s"=>{:s=>"great pic"}}}, "user"=>{:m=>{"s"=>{:s=>"46"}}}, "age"=>{:m=>{"s"=>{:s=>"95120315-5ad7-46fc-bd8d-3569e3b3ea65"}}}}>, delete_request=nil>, #<struct Aws::DynamoDB::Types::WriteRequest put_request=#<struct Aws::DynamoDB::Types::PutRequest item={"premium_user"=>{:m=>{"bool"=>{:bool=>true}}}, "fav_tv_shows"=>{:m=>{"l"=>{:l=>[{:m=>{"s"=>{:s=>"spongebob"}}}, {:m=>{"s"=>{:s=>"dexter"}}}, {:m=>{"s"=>{:s=>"stranger things"}}}]}}}, "referral_source"=>{:m=>{"s"=>{:s=>"Johnson & Jonson"}}}, "fav_movies"=>{:m=>{"l"=>{:l=>[{:m=>{"s"=>{:s=>"balto"}}}, {:m=>{"s"=>{:s=>"the lion king"}}}, {:m=>{"s"=>{:s=>"swingers"}}}]}}}, "bio"=>{:m=>{"s"=>{:s=>"such words"}}}, "pic"=>{:m=>{"s"=>{:s=>"great pic"}}}, "user"=>{:m=>{"s"=>{:s=>"47"}}}, "age"=>{:m=>{"s"=>{:s=>"ecc48617-d8c8-4332-9bf7-6a540cf0e394"}}}}>, delete_request=nil>, #<struct Aws::DynamoDB::Types::WriteRequest put_request=#<struct Aws::DynamoDB::Types::PutRequest item={"premium_user"=>{:m=>{"bool"=>{:bool=>true}}}, "fav_tv_shows"=>{:m=>{"l"=>{:l=>[{:m=>{"s"=>{:s=>"spongebob"}}}, {:m=>{"s"=>{:s=>"dexter"}}}, {:m=>{"s"=>{:s=>"stranger things"}}}]}}}, "referral_source"=>{:m=>{"s"=>{:s=>"Johnson & Jonson"}}}, "fav_movies"=>{:m=>{"l"=>{:l=>[{:m=>{"s"=>{:s=>"balto"}}}, {:m=>{"s"=>{:s=>"the lion king"}}}, {:m=>{"s"=>{:s=>"swingers"}}}]}}}, "bio"=>{:m=>{"s"=>{:s=>"such words"}}}, "pic"=>{:m=>{"s"=>{:s=>"great pic"}}}, "user"=>{:m=>{"s"=>{:s=>"48"}}}, "age"=>{:m=>{"s"=>{:s=>"fb889f20-c3a0-48e3-80eb-645a7d6e9bc7"}}}}>, delete_request=nil>, #<struct Aws::DynamoDB::Types::WriteRequest put_request=#<struct Aws::DynamoDB::Types::PutRequest item={"premium_user"=>{:m=>{"bool"=>{:bool=>true}}}, "fav_tv_shows"=>{:m=>{"l"=>{:l=>[{:m=>{"s"=>{:s=>"spongebob"}}}, {:m=>{"s"=>{:s=>"dexter"}}}, {:m=>{"s"=>{:s=>"stranger things"}}}]}}}, "referral_source"=>{:m=>{"s"=>{:s=>"Johnson & Jonson"}}}, "fav_movies"=>{:m=>{"l"=>{:l=>[{:m=>{"s"=>{:s=>"balto"}}}, {:m=>{"s"=>{:s=>"the lion king"}}}, {:m=>{"s"=>{:s=>"swingers"}}}]}}}, "bio"=>{:m=>{"s"=>{:s=>"such words"}}}, "pic"=>{:m=>{"s"=>{:s=>"great pic"}}}, "user"=>{:m=>{"s"=>{:s=>"49"}}}, "age"=>{:m=>{"s"=>{:s=>"17af13b7-dbdc-4784-b3fe-9cef7dc31506"}}}}>, delete_request=nil>, #<struct Aws::DynamoDB::Types::WriteRequest put_request=#<struct Aws::DynamoDB::Types::PutRequest item={"premium_user"=>{:m=>{"bool"=>{:bool=>true}}}, "fav_tv_shows"=>{:m=>{"l"=>{:l=>[{:m=>{"s"=>{:s=>"spongebob"}}}, {:m=>{"s"=>{:s=>"dexter"}}}, {:m=>{"s"=>{:s=>"stranger things"}}}]}}}, "referral_source"=>{:m=>{"s"=>{:s=>"Johnson & Jonson"}}}, "fav_movies"=>{:m=>{"l"=>{:l=>[{:m=>{"s"=>{:s=>"balto"}}}, {:m=>{"s"=>{:s=>"the lion king"}}}, {:m=>{"s"=>{:s=>"swingers"}}}]}}}, "bio"=>{:m=>{"s"=>{:s=>"such words"}}}, "pic"=>{:m=>{"s"=>{:s=>"great pic"}}}, "user"=>{:m=>{"s"=>{:s=>"50"}}}, "age"=>{:m=>{"s"=>{:s=>"ed2cfa67-31df-4732-95a4-560aba5af449"}}}}>, delete_request=nil>]}}

[37, 46] in ~/dynamo_debugging_for_aws.rb
   37:   } }
   38: end
   39: 
   40: def write_to_dynamo(items)
   41:   byebug if @https_tries > 1
=> 42:   resp = @dynamodb.batch_write_item(request_items: items)
   43:   handle_unprocessed_items(resp.unprocessed_items)
   44: rescue StandardError => e
   45:   @logger.error("#{e}\nError on: #{{ request_items: items }}")
   46:   @https_tries += 1
(byebug) where
--> #0  Object.write_to_dynamo(items#Hash) at ~/dynamo_debugging_for_aws.rb:42
    #1  Object.handle_unprocessed_items(unprocessed_items#Hash) at ~/dynamo_debugging_for_aws.rb:54
    #2  Object.write_to_dynamo(items#Hash) at ~/dynamo_debugging_for_aws.rb:43
    #3  Object.handle_unprocessed_items(unprocessed_items#Hash) at ~/dynamo_debugging_for_aws.rb:54
    #4  Object.write_to_dynamo(items#Hash) at ~/dynamo_debugging_for_aws.rb:43
    #5  Object.handle_unprocessed_items(unprocessed_items#Hash) at ~/dynamo_debugging_for_aws.rb:54
    #6  Object.write_to_dynamo(items#Hash) at ~/dynamo_debugging_for_aws.rb:43
    #7  Object.handle_unprocessed_items(unprocessed_items#Hash) at ~/dynamo_debugging_for_aws.rb:54
    #8  Object.write_to_dynamo(items#Hash) at ~/dynamo_debugging_for_aws.rb:43
    #9  Object.handle_unprocessed_items(unprocessed_items#Hash) at ~/dynamo_debugging_for_aws.rb:54
    #10 Object.write_to_dynamo(items#Hash) at ~/dynamo_debugging_for_aws.rb:43
    #11 Object.handle_unprocessed_items(unprocessed_items#Hash) at ~/dynamo_debugging_for_aws.rb:54
    #12 Object.write_to_dynamo(items#Hash) at ~/dynamo_debugging_for_aws.rb:43
    #13 Object.handle_unprocessed_items(unprocessed_items#Hash) at ~/dynamo_debugging_for_aws.rb:54
    #14 Object.write_to_dynamo(items#Hash) at ~/dynamo_debugging_for_aws.rb:43
    #15 Object.handle_unprocessed_items(unprocessed_items#Hash) at ~/dynamo_debugging_for_aws.rb:54
    #16 Object.write_to_dynamo(items#Hash) at ~/dynamo_debugging_for_aws.rb:43
    #17 Object.handle_unprocessed_items(unprocessed_items#Hash) at ~/dynamo_debugging_for_aws.rb:54
    #18 Object.write_to_dynamo(items#Hash) at ~/dynamo_debugging_for_aws.rb:43
    #19 Object.handle_unprocessed_items(unprocessed_items#Hash) at ~/dynamo_debugging_for_aws.rb:54
    #20 Object.write_to_dynamo(items#Hash) at ~/dynamo_debugging_for_aws.rb:43
    #21 Object.handle_unprocessed_items(unprocessed_items#Hash) at ~/dynamo_debugging_for_aws.rb:54
    #22 Object.write_to_dynamo(items#Hash) at ~/dynamo_debugging_for_aws.rb:43
    #23 Object.handle_unprocessed_items(unprocessed_items#Hash) at ~/dynamo_debugging_for_aws.rb:54
    #24 Object.write_to_dynamo(items#Hash) at ~/dynamo_debugging_for_aws.rb:43
    #25 Object.handle_unprocessed_items(unprocessed_items#Hash) at ~/dynamo_debugging_for_aws.rb:54
    #26 Object.write_to_dynamo(items#Hash) at ~/dynamo_debugging_for_aws.rb:43
    #27 Object.handle_unprocessed_items(unprocessed_items#Hash) at ~/dynamo_debugging_for_aws.rb:54
    #28 Object.write_to_dynamo(items#Hash) at ~/dynamo_debugging_for_aws.rb:43
    #29 Object.build_put_request(data#Array) at ~/dynamo_debugging_for_aws.rb:18
    #30 Object.make_dummies at ~/dynamo_debugging_for_aws.rb:62
    #31 <main> at ~/dynamo_debugging_for_aws.rb:65
(byebug) info locals
*** Unknown command 'info locals'. Try 'help info'
(byebug) locals
*** NameError Exception: undefined local variable or method `locals' for main:Object

nil


(byebug) info program
Program stopped. 
It stopped after stepping, next'ing or initial start.
(byebug) list

[47, 56] in ~/dynamo_debugging_for_aws.rb
   47:   retry
   48: end
   49: 
   50: def handle_unprocessed_items(unprocessed_items)
   51:   if unprocessed_items.count > 0
   52:     log_failure(unprocessed_items[@dynamo_table]) if (@tries + 1) > 1
   53:     sleep(@tries * 0.75)
   54:     write_to_dynamo(unprocessed_items)
   55:   else
   56:     @tries = 0


(byebug) var all
$! = nil
$" = ["enumerator.so", "thread.rb", "rational.so", "complex.so", "~/.rvm/rubies/ruby-2.3.3/lib/ruby/2.3.0/x86_64-darwin15/enc/encdb.bundle",...
$$ = 25539
$& = nil
$' = nil
$* = []
$+ = nil
$, = nil
$-0 = "\n"
$-F = nil
$-I = ["~/.rvm/gems/ruby-2.3.3@global/gems/did_you_mean-1.0.0/lib", "~/.rvm/gems/ruby-2.3.3/gems/jmespath-1.3.1/lib", "/Use...
$-W = 1
$-a = false
$-d = false
$-i = nil
$-l = false
$-p = false
$-v = false
$-w = false
$. = 57
$/ = "\n"
$0 = "dynamo_debugging_for_aws.rb"
$1 = nil
$2 = nil
$3 = nil
$4 = nil
$5 = nil
$6 = nil
$7 = nil
$8 = nil
$9 = nil
$: = ["~/.rvm/gems/ruby-2.3.3@global/gems/did_you_mean-1.0.0/lib", "~/.rvm/gems/ruby-2.3.3/gems/jmespath-1.3.1/lib", "/User...
$; = nil
$< = ARGF
$> = #<IO:<STDOUT>>
$? = nil
$@ = nil
$ARGV = nil
$CGI_ENV = {"NVM_RC_VERSION"=>"", "rvm_bin_path"=>"~/.rvm/bin", "TERM_PROGRAM"=>"Apple_Terminal", "NVM_CD_FLAGS"=>"", "GEM_HOME"=>"/Users/ce...
$DEBUG = false
$FILENAME = "-"
$LOADED_FEATURES = ["enumerator.so", "thread.rb", "rational.so", "complex.so", "~/.rvm/rubies/ruby-2.3.3/lib/ruby/2.3.0/x86_64-darwin15/enc/...
$LOAD_PATH = ["~/.rvm/gems/ruby-2.3.3@global/gems/did_you_mean-1.0.0/lib", "~/.rvm/gems/ruby-2.3.3/gems/jmespath-1.3.1/lib"...
$PROGRAM_NAME = "dynamo_debugging_for_aws.rb"
$SAFE = 0
$VERBOSE = false
$\ = nil
$_ = nil
$` = nil
$fileutils_rb_have_lchmod = nil
$fileutils_rb_have_lchown = nil
$stderr = #<IO:<STDERR>>
$stdin = #<IO:<STDIN>>
$stdout = #<IO:<STDOUT>>
$~ = nil
@dynamo_table = "test-throttle"
@dynamodb = #<Aws::DynamoDB::Client>
@https_tries = 2
@logger = #<Logger:0x007fedf503a6e8 @progname="DynamoDB", @level=1, @default_formatter=#<Logger::Formatter:0x007fedf503a6c0 @datetime_format=nil>, @formatter...
@spot = 50
@tries = 0
@users = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "...
e = Encountered a `SocketError` while attempting to connect to:

  https://dynamodb.us-east-1.amazonaws.com

This is typically the result of an invalid `:reg...
items = {"test-throttle"=>[#<struct Aws::DynamoDB::Types::WriteRequest put_request=#<struct Aws::DynamoDB::Types::PutRequest item={"premium_user"=>{:m...
resp = nil
(byebug) var local
e = Encountered a `SocketError` while attempting to connect to:

  https://dynamodb.us-east-1.amazonaws.com

This is typically the result of an invalid `:reg...
items = {"test-throttle"=>[#<struct Aws::DynamoDB::Types::WriteRequest put_request=#<struct Aws::DynamoDB::Types::PutRequest item={"premium_user"=>{:m...
resp = nil
(byebug) var args
items = {"test-throttle"=>[#<struct Aws::DynamoDB::Types::WriteRequest put_request=#<struct Aws::DynamoDB::Types::PutRequest item={"premium_user"=>{:m...


p resp[:vhash]

I did this on a newly created table.
The Partition Key was user and the GSI was age.
The RCU/WCU for both indexes was set to 1.

I did not hit the error I hit before but instead a SocketError, which made the same nesting bug.

@awood45
Copy link
Member

awood45 commented Apr 3, 2017

Sorry about the delay here - I couldn't repro with that script, but actively picking this up again.

@cameck
Copy link
Author

cameck commented Apr 4, 2017

no worries 😃

awood45 added a commit that referenced this issue Apr 4, 2017
Under a particular set of circumstances, a return value struct can have
SimpleAttributes transformations applied multiple times, which can case
incorrect types and values to be written (or more likely, fail to be
written).

This doesn't happen for hashes, but we hadn't applied the same approach
to structs. This change applies the same approach to type structs,
including a previously failing test case.

Resolves GitHub issue #1406
@awood45
Copy link
Member

awood45 commented Apr 4, 2017

Okay, I think we got it. Going to try and get this out with the next release.

@awood45 awood45 closed this as completed Apr 4, 2017
@awood45
Copy link
Member

awood45 commented Apr 4, 2017

Please feel free to reopen if you see any recurrence of this issue after pulling down version 2.9.3 (yet to be released, but that will be the next version number).

awood45 added a commit that referenced this issue Apr 4, 2017
@cameck
Copy link
Author

cameck commented Apr 4, 2017

Very cool, thanks so much 💯 !

awood45 added a commit that referenced this issue Jun 29, 2017
Under a particular set of circumstances, a return value struct can have
SimpleAttributes transformations applied multiple times, which can case
incorrect types and values to be written (or more likely, fail to be
written).

This doesn't happen for hashes, but we hadn't applied the same approach
to structs. This change applies the same approach to type structs,
including a previously failing test case.

Resolves GitHub issue #1406
awood45 added a commit that referenced this issue Jun 29, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants