super_queue 0.2.1 → 0.3.1
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/lib/super_queue.rb +155 -85
- metadata +5 -5
    
        data/lib/super_queue.rb
    CHANGED
    
    | @@ -1,19 +1,32 @@ | |
| 1 1 | 
             
            require 'aws-sdk'
         | 
| 2 2 | 
             
            require 'base64'
         | 
| 3 | 
            -
            require 'socket'
         | 
| 4 3 | 
             
            require 'digest/md5'
         | 
| 5 | 
            -
            require 'zlib'
         | 
| 6 4 |  | 
| 7 5 | 
             
            class SuperQueue
         | 
| 8 6 |  | 
| 7 | 
            +
              class S3Pointer < Hash
         | 
| 8 | 
            +
                def initialize(key)
         | 
| 9 | 
            +
                  super
         | 
| 10 | 
            +
                  self.merge!(:s3_key => key)
         | 
| 11 | 
            +
                end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                def s3_key
         | 
| 14 | 
            +
                  self[:s3_key]
         | 
| 15 | 
            +
                end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                def s3_key=(value)
         | 
| 18 | 
            +
                  self[:s3_key] = value
         | 
| 19 | 
            +
                end
         | 
| 20 | 
            +
              end
         | 
| 21 | 
            +
             | 
| 9 22 | 
             
              def initialize(opts)
         | 
| 10 23 | 
             
                AWS.eager_autoload! # for thread safety
         | 
| 11 24 | 
             
                check_opts(opts)
         | 
| 12 | 
            -
                @should_poll_sqs = opts[:should_poll_sqs]
         | 
| 13 25 | 
             
                @buffer_size = opts[:buffer_size] || 100
         | 
| 26 | 
            +
                @use_s3 = opts[:use_s3]
         | 
| 14 27 | 
             
                @queue_name = generate_queue_name(opts)
         | 
| 15 28 | 
             
                @request_count = 0
         | 
| 16 | 
            -
                 | 
| 29 | 
            +
                initialize_aws(opts)
         | 
| 17 30 |  | 
| 18 31 | 
             
                @waiting = []
         | 
| 19 32 | 
             
                @waiting.taint
         | 
| @@ -21,16 +34,20 @@ class SuperQueue | |
| 21 34 | 
             
                @mutex = Mutex.new
         | 
| 22 35 | 
             
                @in_buffer = []
         | 
| 23 36 | 
             
                @out_buffer = []
         | 
| 37 | 
            +
                @deletion_buffer = []
         | 
| 24 38 | 
             
                @deletion_queue = []
         | 
| 25 39 |  | 
| 26 | 
            -
                @ | 
| 27 | 
            -
             | 
| 28 | 
            -
             | 
| 29 | 
            -
             | 
| 30 | 
            -
             | 
| 40 | 
            +
                @gc = Thread.new do 
         | 
| 41 | 
            +
                  begin
         | 
| 42 | 
            +
                    collect_garbage
         | 
| 43 | 
            +
                  rescue Exception => @gc_error
         | 
| 44 | 
            +
                    raise @gc_error
         | 
| 45 | 
            +
                  end
         | 
| 46 | 
            +
                end
         | 
| 31 47 | 
             
              end
         | 
| 32 48 |  | 
| 33 49 | 
             
              def push(p)
         | 
| 50 | 
            +
                check_for_errors
         | 
| 34 51 | 
             
                @mutex.synchronize {
         | 
| 35 52 | 
             
                  @in_buffer.push p
         | 
| 36 53 | 
             
                  clear_in_buffer if @in_buffer.size >= @buffer_size
         | 
| @@ -44,8 +61,9 @@ class SuperQueue | |
| 44 61 | 
             
              end
         | 
| 45 62 |  | 
| 46 63 | 
             
              def pop(non_block=false)
         | 
| 64 | 
            +
                check_for_errors
         | 
| 47 65 | 
             
                @mutex.synchronize {
         | 
| 48 | 
            -
                   | 
| 66 | 
            +
                  loop do
         | 
| 49 67 | 
             
                    if @out_buffer.empty?
         | 
| 50 68 | 
             
                      if fill_out_buffer_from_sqs_queue || fill_out_buffer_from_in_buffer
         | 
| 51 69 | 
             
                        return pop_out_buffer
         | 
| @@ -62,19 +80,22 @@ class SuperQueue | |
| 62 80 | 
             
              end
         | 
| 63 81 |  | 
| 64 82 | 
             
              def length
         | 
| 83 | 
            +
                check_for_errors
         | 
| 65 84 | 
             
                @mutex.synchronize {
         | 
| 66 | 
            -
                   | 
| 85 | 
            +
                  sqsl = sqs_length
         | 
| 86 | 
            +
                  return sqsl + @in_buffer.size + @out_buffer.size
         | 
| 67 87 | 
             
                }
         | 
| 68 88 | 
             
              end
         | 
| 69 89 |  | 
| 70 | 
            -
              def empty?
         | 
| 71 | 
            -
                self.length == 0
         | 
| 72 | 
            -
              end
         | 
| 73 | 
            -
             | 
| 74 90 | 
             
              def num_waiting
         | 
| 91 | 
            +
                check_for_errors
         | 
| 75 92 | 
             
                @waiting.size
         | 
| 76 93 | 
             
              end
         | 
| 77 94 |  | 
| 95 | 
            +
              def empty?
         | 
| 96 | 
            +
                self.length == 0
         | 
| 97 | 
            +
              end
         | 
| 98 | 
            +
             | 
| 78 99 | 
             
              def clear
         | 
| 79 100 | 
             
                begin
         | 
| 80 101 | 
             
                  self.pop(true)
         | 
| @@ -84,19 +105,21 @@ class SuperQueue | |
| 84 105 | 
             
              end
         | 
| 85 106 |  | 
| 86 107 | 
             
              def shutdown
         | 
| 87 | 
            -
                @sqs_tracker.terminate if @should_poll_sqs
         | 
| 88 108 | 
             
                @mutex.synchronize { clear_in_buffer }
         | 
| 89 109 | 
             
                @gc.terminate
         | 
| 90 | 
            -
                @mutex.synchronize {  | 
| 110 | 
            +
                @mutex.synchronize { fill_deletion_queue_from_buffer } if @deletion_buffer.any?
         | 
| 111 | 
            +
                @mutex.synchronize { clear_deletion_queue } if @deletion_queue.any?
         | 
| 112 | 
            +
                @done = true
         | 
| 91 113 | 
             
              end
         | 
| 92 114 |  | 
| 93 115 | 
             
              def destroy
         | 
| 94 | 
            -
                @sqs_tracker.terminate if @should_poll_sqs
         | 
| 95 116 | 
             
                @gc.terminate
         | 
| 96 | 
            -
                 | 
| 117 | 
            +
                delete_aws_resources
         | 
| 118 | 
            +
                @done = true
         | 
| 97 119 | 
             
              end
         | 
| 98 120 |  | 
| 99 121 | 
             
              def sqs_requests
         | 
| 122 | 
            +
                check_for_errors
         | 
| 100 123 | 
             
                @request_count
         | 
| 101 124 | 
             
              end
         | 
| 102 125 |  | 
| @@ -119,10 +142,15 @@ class SuperQueue | |
| 119 142 | 
             
              private
         | 
| 120 143 |  | 
| 121 144 | 
             
              #
         | 
| 122 | 
            -
              # Amazon  | 
| 145 | 
            +
              # Amazon AWS methods
         | 
| 123 146 | 
             
              #
         | 
| 124 | 
            -
              def  | 
| 125 | 
            -
                 | 
| 147 | 
            +
              def initialize_aws(opts)
         | 
| 148 | 
            +
                aws_options = {
         | 
| 149 | 
            +
                  :access_key_id => opts[:aws_access_key_id], 
         | 
| 150 | 
            +
                  :secret_access_key => opts[:aws_secret_access_key]
         | 
| 151 | 
            +
                }
         | 
| 152 | 
            +
                @sqs = AWS::SQS.new(aws_options)
         | 
| 153 | 
            +
                @s3 = AWS::S3.new(aws_options)
         | 
| 126 154 | 
             
                create_sqs_queue(opts)
         | 
| 127 155 | 
             
                if opts[:replace_existing_queue] && (sqs_length > 0)
         | 
| 128 156 | 
             
                  delete_queue
         | 
| @@ -130,30 +158,16 @@ class SuperQueue | |
| 130 158 | 
             
                  sleep 62 # You must wait 60s after deleting a q to create one with the same name
         | 
| 131 159 | 
             
                  create_sqs_queue(opts)
         | 
| 132 160 | 
             
                end
         | 
| 161 | 
            +
                @bucket = open_s3_bucket
         | 
| 133 162 | 
             
              end
         | 
| 134 163 |  | 
| 135 | 
            -
              def  | 
| 136 | 
            -
                 | 
| 137 | 
            -
             | 
| 138 | 
            -
                  :secret_access_key => opts[:aws_secret_access_key]
         | 
| 139 | 
            -
                }
         | 
| 140 | 
            -
                begin
         | 
| 141 | 
            -
                  @sqs = AWS::SQS.new(aws_options)
         | 
| 142 | 
            -
                rescue Exception => e
         | 
| 143 | 
            -
                  raise e
         | 
| 144 | 
            -
                end
         | 
| 164 | 
            +
              def create_sqs_queue(opts)
         | 
| 165 | 
            +
                @sqs_queue = find_queue_by_name || new_sqs_queue(opts)
         | 
| 166 | 
            +
                check_for_queue_creation_success
         | 
| 145 167 | 
             
              end
         | 
| 146 168 |  | 
| 147 | 
            -
              def  | 
| 148 | 
            -
                 | 
| 149 | 
            -
                begin
         | 
| 150 | 
            -
                  @sqs_queue = find_queue_by_name || new_sqs_queue(opts)
         | 
| 151 | 
            -
                  check_for_queue_creation_success
         | 
| 152 | 
            -
                rescue RuntimeError => e
         | 
| 153 | 
            -
                  retries += 1
         | 
| 154 | 
            -
                  sleep 1
         | 
| 155 | 
            -
                  (retries >= 20) ? retry : raise(e)
         | 
| 156 | 
            -
                end
         | 
| 169 | 
            +
              def open_s3_bucket
         | 
| 170 | 
            +
                @s3.buckets[queue_name].exists? ? @s3.buckets[queue_name] : @s3.buckets.create(queue_name)
         | 
| 157 171 | 
             
              end
         | 
| 158 172 |  | 
| 159 173 | 
             
              def find_queue_by_name
         | 
| @@ -189,17 +203,20 @@ class SuperQueue | |
| 189 203 | 
             
                number_of_batches.times do
         | 
| 190 204 | 
             
                  batch = []
         | 
| 191 205 | 
             
                  10.times do
         | 
| 206 | 
            +
                    next if @in_buffer.empty?
         | 
| 192 207 | 
             
                    p = @in_buffer.shift
         | 
| 193 | 
            -
                     | 
| 194 | 
            -
             | 
| 208 | 
            +
                    unless should_send_to_s3?(p)
         | 
| 209 | 
            +
                      batch << encode(p)
         | 
| 210 | 
            +
                    else
         | 
| 211 | 
            +
                      batch << encode(send_payload_to_s3(p))
         | 
| 212 | 
            +
                    end
         | 
| 195 213 | 
             
                  end
         | 
| 196 214 | 
             
                  batches << batch
         | 
| 197 215 | 
             
                end
         | 
| 198 216 |  | 
| 199 | 
            -
                #Ugliness! But I'm not sure how else to tackle this at the moment
         | 
| 200 217 | 
             
                batches.each do |b|
         | 
| 201 218 | 
             
                  @request_count += 1
         | 
| 202 | 
            -
                  @sqs_queue.batch_send(b)
         | 
| 219 | 
            +
                  @sqs_queue.batch_send(b) if b.any?
         | 
| 203 220 | 
             
                end
         | 
| 204 221 | 
             
              end
         | 
| 205 222 |  | 
| @@ -207,34 +224,72 @@ class SuperQueue | |
| 207 224 | 
             
                messages = []
         | 
| 208 225 | 
             
                number_of_batches = number_of_messages_to_receive / 10
         | 
| 209 226 | 
             
                number_of_batches += 1 if number_of_messages_to_receive % 10
         | 
| 210 | 
            -
                number_of_batches.times do | 
| 211 | 
            -
                  batch = @sqs_queue.receive_messages(:limit => 10)
         | 
| 212 | 
            -
                  batch.each do | | 
| 213 | 
            -
                     | 
| 214 | 
            -
             | 
| 227 | 
            +
                number_of_batches.times do
         | 
| 228 | 
            +
                  batch = @sqs_queue.receive_messages(:limit => 10).compact
         | 
| 229 | 
            +
                  batch.each do |message|
         | 
| 230 | 
            +
                    obj = decode(message.body)
         | 
| 231 | 
            +
                    unless obj.is_a?(SuperQueue::S3Pointer)
         | 
| 232 | 
            +
                      messages << {
         | 
| 233 | 
            +
                        :payload    => obj,
         | 
| 234 | 
            +
                        :sqs_handle => message
         | 
| 235 | 
            +
                      }
         | 
| 215 236 | 
             
                    else
         | 
| 216 | 
            -
                       | 
| 237 | 
            +
                      p = fetch_payload_from_s3(obj)
         | 
| 238 | 
            +
                      messages << {
         | 
| 239 | 
            +
                        :payload    => p,
         | 
| 240 | 
            +
                        :sqs_handle => message,
         | 
| 241 | 
            +
                        :s3_key     => obj.s3_key } if p
         | 
| 217 242 | 
             
                    end
         | 
| 218 | 
            -
                    messages << obj
         | 
| 219 243 | 
             
                  end
         | 
| 220 244 | 
             
                  @request_count += 1
         | 
| 221 245 | 
             
                end
         | 
| 222 246 | 
             
                messages
         | 
| 223 247 | 
             
              end
         | 
| 224 248 |  | 
| 249 | 
            +
              def send_payload_to_s3(p)
         | 
| 250 | 
            +
                dump = Marshal.dump(p)
         | 
| 251 | 
            +
                digest = Digest::MD5.hexdigest(dump)
         | 
| 252 | 
            +
                return digest if @bucket.objects[digest].exists?
         | 
| 253 | 
            +
                retryable(:tries => 5) { @bucket.objects[digest].write(dump) }
         | 
| 254 | 
            +
                S3Pointer.new(digest)
         | 
| 255 | 
            +
              end
         | 
| 256 | 
            +
             | 
| 257 | 
            +
              def fetch_payload_from_s3(pointer)
         | 
| 258 | 
            +
                payload = nil
         | 
| 259 | 
            +
                retries = 0
         | 
| 260 | 
            +
                begin
         | 
| 261 | 
            +
                  payload = Marshal.load(@bucket.objects[pointer.s3_key].read)
         | 
| 262 | 
            +
                rescue AWS::S3::Errors::NoSuchKey
         | 
| 263 | 
            +
                  return nil
         | 
| 264 | 
            +
                rescue
         | 
| 265 | 
            +
                  retries +=1
         | 
| 266 | 
            +
                  retry if retries < 5
         | 
| 267 | 
            +
                end
         | 
| 268 | 
            +
                payload
         | 
| 269 | 
            +
              end
         | 
| 270 | 
            +
             | 
| 271 | 
            +
              def should_send_to_s3?(p)
         | 
| 272 | 
            +
                @use_s3
         | 
| 273 | 
            +
              end
         | 
| 274 | 
            +
             | 
| 225 275 | 
             
              def sqs_length
         | 
| 226 276 | 
             
                n = @sqs_queue.approximate_number_of_messages
         | 
| 227 277 | 
             
                return n.is_a?(Integer) ? n : 0
         | 
| 228 278 | 
             
              end
         | 
| 229 279 |  | 
| 230 | 
            -
              def  | 
| 280 | 
            +
              def delete_aws_resources
         | 
| 231 281 | 
             
                @request_count += 1
         | 
| 232 282 | 
             
                @sqs_queue.delete
         | 
| 283 | 
            +
                @bucket.delete!
         | 
| 233 284 | 
             
              end
         | 
| 234 285 |  | 
| 235 286 | 
             
              def clear_deletion_queue
         | 
| 236 287 | 
             
                while !@deletion_queue.empty?
         | 
| 237 | 
            -
                  @ | 
| 288 | 
            +
                  sqs_handles = @deletion_queue[0..9].map { |m| m[:sqs_handle] }.compact
         | 
| 289 | 
            +
                  s3_keys = @deletion_queue[0..9].map { |m| m[:s3_key] }.compact
         | 
| 290 | 
            +
                  10.times { @deletion_queue.shift }
         | 
| 291 | 
            +
                  @sqs_queue.batch_delete(sqs_handles) if sqs_handles.any?
         | 
| 292 | 
            +
                  s3_keys.each { |key| @bucket.objects[key].delete }
         | 
| 238 293 | 
             
                  @request_count += 1
         | 
| 239 294 | 
             
                end
         | 
| 240 295 | 
             
              end
         | 
| @@ -244,10 +299,15 @@ class SuperQueue | |
| 244 299 | 
             
              #
         | 
| 245 300 | 
             
              def fill_out_buffer_from_sqs_queue
         | 
| 246 301 | 
             
                return false if sqs_length == 0
         | 
| 247 | 
            -
                 | 
| 248 | 
            -
                while (@out_buffer.size < @buffer_size)
         | 
| 302 | 
            +
                nil_count = 0
         | 
| 303 | 
            +
                while (@out_buffer.size < @buffer_size) && (nil_count < 2)
         | 
| 249 304 | 
             
                  messages = get_messages_from_queue(@buffer_size - @out_buffer.size)
         | 
| 250 | 
            -
                  messages. | 
| 305 | 
            +
                  if messages.empty?
         | 
| 306 | 
            +
                    nil_count += 1
         | 
| 307 | 
            +
                  else
         | 
| 308 | 
            +
                    messages.each { |m| @out_buffer.push(m) }
         | 
| 309 | 
            +
                    nil_count = 0
         | 
| 310 | 
            +
                  end
         | 
| 251 311 | 
             
                end
         | 
| 252 312 | 
             
                !@out_buffer.empty?
         | 
| 253 313 | 
             
              end
         | 
| @@ -260,9 +320,14 @@ class SuperQueue | |
| 260 320 | 
             
                !@out_buffer.empty?
         | 
| 261 321 | 
             
              end
         | 
| 262 322 |  | 
| 323 | 
            +
              def fill_deletion_queue_from_buffer
         | 
| 324 | 
            +
                @deletion_queue += @deletion_buffer
         | 
| 325 | 
            +
                @deletion_buffer = []
         | 
| 326 | 
            +
              end
         | 
| 327 | 
            +
             | 
| 263 328 | 
             
              def pop_out_buffer
         | 
| 264 329 | 
             
                m = @out_buffer.shift
         | 
| 265 | 
            -
                @ | 
| 330 | 
            +
                @deletion_buffer << { :sqs_handle => m[:sqs_handle], :s3_key => m[:s3_key] }
         | 
| 266 331 | 
             
                m[:payload]
         | 
| 267 332 | 
             
              end
         | 
| 268 333 |  | 
| @@ -272,6 +337,9 @@ class SuperQueue | |
| 272 337 | 
             
                end
         | 
| 273 338 | 
             
              end
         | 
| 274 339 |  | 
| 340 | 
            +
              #
         | 
| 341 | 
            +
              # Misc helper methods
         | 
| 342 | 
            +
              #
         | 
| 275 343 | 
             
              def check_opts(opts)
         | 
| 276 344 | 
             
                raise "Options can't be nil!" if opts.nil?
         | 
| 277 345 | 
             
                raise "Minimun :buffer_size is 5." if opts[:buffer_size] && (opts[:buffer_size] < 5)
         | 
| @@ -279,30 +347,37 @@ class SuperQueue | |
| 279 347 | 
             
                raise "Visbility timeout must be an integer (in seconds)!" if opts[:visibility_timeout] && !opts[:visibility_timeout].is_a?(Integer)
         | 
| 280 348 | 
             
              end
         | 
| 281 349 |  | 
| 282 | 
            -
               | 
| 283 | 
            -
             | 
| 284 | 
            -
             | 
| 350 | 
            +
              def check_for_errors
         | 
| 351 | 
            +
                raise @gc_error if @gc_error
         | 
| 352 | 
            +
                raise @sqs_error if @sqs_error
         | 
| 353 | 
            +
                raise "Queue is no longer available!" if @done == true
         | 
| 354 | 
            +
              end
         | 
| 355 | 
            +
             | 
| 285 356 | 
             
              def encode(p)
         | 
| 286 357 | 
             
                Base64.urlsafe_encode64(Marshal.dump(p))
         | 
| 287 358 | 
             
              end
         | 
| 288 359 |  | 
| 289 360 | 
             
              def decode(ser_obj)
         | 
| 290 | 
            -
                 | 
| 291 | 
            -
                obj = Base64.urlsafe_decode64(ser_obj)
         | 
| 292 | 
            -
                #puts "Decode: Object decoded as #{obj}. Doing Marshal load..."
         | 
| 293 | 
            -
                ret = Marshal.load(obj)
         | 
| 294 | 
            -
                #puts "Decode: Marshal loaded as #{ret}"
         | 
| 295 | 
            -
                ret
         | 
| 296 | 
            -
              end
         | 
| 297 | 
            -
             | 
| 298 | 
            -
              def is_a_link?(s)
         | 
| 299 | 
            -
                return false unless s.is_a? String
         | 
| 300 | 
            -
                (s[0..6] == "http://") || (s[0..7] == "https://")
         | 
| 361 | 
            +
                Marshal.load(Base64.urlsafe_decode64(ser_obj))
         | 
| 301 362 | 
             
              end
         | 
| 302 363 |  | 
| 303 364 | 
             
              def generate_queue_name(opts)
         | 
| 304 365 | 
             
                q_name = opts[:name] || random_name
         | 
| 305 | 
            -
                return opts[:namespace] ? " | 
| 366 | 
            +
                return opts[:namespace] ? "queue-#{opts[:namespace]}-#{q_name}" : "queue-#{q_name}"
         | 
| 367 | 
            +
              end
         | 
| 368 | 
            +
             | 
| 369 | 
            +
              def retryable(options = {}, &block)
         | 
| 370 | 
            +
                opts = { :tries => 1, :on => Exception }.merge(options)
         | 
| 371 | 
            +
             | 
| 372 | 
            +
                retry_exception, retries = opts[:on], opts[:tries]
         | 
| 373 | 
            +
             | 
| 374 | 
            +
                begin
         | 
| 375 | 
            +
                  return yield
         | 
| 376 | 
            +
                rescue retry_exception
         | 
| 377 | 
            +
                  retry if (retries -= 1) > 0
         | 
| 378 | 
            +
                end
         | 
| 379 | 
            +
             | 
| 380 | 
            +
                yield
         | 
| 306 381 | 
             
              end
         | 
| 307 382 |  | 
| 308 383 | 
             
              #
         | 
| @@ -315,8 +390,9 @@ class SuperQueue | |
| 315 390 | 
             
              end
         | 
| 316 391 |  | 
| 317 392 | 
             
              def random_name
         | 
| 318 | 
            -
                o =  [('a'..'z'),(' | 
| 319 | 
            -
                (0... | 
| 393 | 
            +
                o =  [('a'..'z'),('1'..'9')].map{|i| i.to_a}.flatten
         | 
| 394 | 
            +
                random_element = (0...25).map{ o[rand(o.length)] }.join
         | 
| 395 | 
            +
                "temp-name-#{random_element}"
         | 
| 320 396 | 
             
              end
         | 
| 321 397 |  | 
| 322 398 | 
             
              def queue_name
         | 
| @@ -326,19 +402,13 @@ class SuperQueue | |
| 326 402 | 
             
              #
         | 
| 327 403 | 
             
              # Maintence thread-related methods
         | 
| 328 404 | 
             
              #
         | 
| 329 | 
            -
              def poll_sqs
         | 
| 330 | 
            -
                loop do
         | 
| 331 | 
            -
                  @mutex.synchronize { fill_out_buffer_from_sqs_queue || fill_out_buffer_from_in_buffer } if @out_buffer.empty?
         | 
| 332 | 
            -
                  @mutex.synchronize { clear_in_buffer } if !@in_buffer.empty? && (@in_buffer.size > @buffer_size)
         | 
| 333 | 
            -
                  Thread.pass
         | 
| 334 | 
            -
                end
         | 
| 335 | 
            -
              end
         | 
| 336 405 |  | 
| 337 406 | 
             
              def collect_garbage
         | 
| 338 407 | 
             
                loop do
         | 
| 339 408 | 
             
                  #This also needs a condition to clear the del queue if there are any handles where the invisibility is about to expire
         | 
| 340 | 
            -
                  @mutex.synchronize {  | 
| 341 | 
            -
                   | 
| 409 | 
            +
                  @mutex.synchronize { fill_deletion_queue_from_buffer } if @deletion_buffer.any?
         | 
| 410 | 
            +
                  Thread.pass
         | 
| 411 | 
            +
                  @mutex.synchronize { clear_deletion_queue } if @deletion_queue.any?
         | 
| 342 412 | 
             
                end
         | 
| 343 413 | 
             
              end
         | 
| 344 414 | 
             
            end
         | 
    
        metadata
    CHANGED
    
    | @@ -1,15 +1,15 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: super_queue
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0.2.1
         | 
| 5 4 | 
             
              prerelease:
         | 
| 5 | 
            +
              version: 0.3.1
         | 
| 6 6 | 
             
            platform: ruby
         | 
| 7 7 | 
             
            authors:
         | 
| 8 8 | 
             
            - Jon Stokes
         | 
| 9 9 | 
             
            autorequire:
         | 
| 10 10 | 
             
            bindir: bin
         | 
| 11 11 | 
             
            cert_chain: []
         | 
| 12 | 
            -
            date: 2013-02- | 
| 12 | 
            +
            date: 2013-02-16 00:00:00.000000000 Z
         | 
| 13 13 | 
             
            dependencies:
         | 
| 14 14 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 15 15 | 
             
              name: aws-sdk
         | 
| @@ -27,7 +27,7 @@ dependencies: | |
| 27 27 | 
             
                none: false
         | 
| 28 28 | 
             
              prerelease: false
         | 
| 29 29 | 
             
              type: :runtime
         | 
| 30 | 
            -
            description: A  | 
| 30 | 
            +
            description: A thread-safe SQS- and S3-backed queue.
         | 
| 31 31 | 
             
            email: jon@jonstokes.com
         | 
| 32 32 | 
             
            executables: []
         | 
| 33 33 | 
             
            extensions: []
         | 
| @@ -56,8 +56,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement | |
| 56 56 | 
             
              none: false
         | 
| 57 57 | 
             
            requirements: []
         | 
| 58 58 | 
             
            rubyforge_project:
         | 
| 59 | 
            -
            rubygems_version: 1.8. | 
| 59 | 
            +
            rubygems_version: 1.8.24
         | 
| 60 60 | 
             
            signing_key:
         | 
| 61 61 | 
             
            specification_version: 3
         | 
| 62 | 
            -
            summary:  | 
| 62 | 
            +
            summary: A thread-safe, SQS- and S3-backed queue structure for ruby that works just like a normal queue, except it's essentially infinite and can use very little memory.
         | 
| 63 63 | 
             
            test_files: []
         |