dalli 2.7.6 → 2.7.7
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.
Potentially problematic release.
This version of dalli might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/History.md +2 -0
- data/README.md +13 -3
- data/lib/action_dispatch/middleware/session/dalli_store.rb +1 -0
- data/lib/active_support/cache/dalli_store.rb +47 -21
- data/lib/dalli.rb +3 -0
- data/lib/dalli/cas/client.rb +1 -0
- data/lib/dalli/client.rb +1 -0
- data/lib/dalli/compressor.rb +2 -1
- data/lib/dalli/options.rb +1 -0
- data/lib/dalli/railtie.rb +1 -0
- data/lib/dalli/ring.rb +3 -3
- data/lib/dalli/server.rb +18 -14
- data/lib/dalli/socket.rb +13 -5
- data/lib/dalli/version.rb +2 -1
- data/lib/rack/session/dalli.rb +143 -37
- metadata +3 -3
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 | 
            -
             | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 2 | 
            +
            SHA256:
         | 
| 3 | 
            +
              metadata.gz: be20ed3535eb5ddc41d8d53e596adc7fe7e7d4241ff345e79f29fb1c6b732a01
         | 
| 4 | 
            +
              data.tar.gz: 39e7c9ea59332d26a2b8ca7276664f3028bbe0b3db3481aa24bc478b5019f3aa
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 9ca82a471a6ae90c123c1888117e87fbc6e348a3401b3db77f5982574d5dc46e0765681df22815773c27d204fa9023841b0012db7a37ec72569eeeddc5e53a3b
         | 
| 7 | 
            +
              data.tar.gz: 93cef3703a4b7c48e1127228b11d03565feba4523dfc1057f6689f50627b1bbb29c0fcea2d703e8500bfdb5fc70da2b975736032452610550f6acf7dace76025
         | 
    
        data/History.md
    CHANGED
    
    
    
        data/README.md
    CHANGED
    
    | @@ -1,4 +1,4 @@ | |
| 1 | 
            -
            Dalli [](http://travis-ci.org/petergoldstein/dalli) [](https://gemnasium.com/petergoldstein/dalli) [](https://codeclimate.com/github/petergoldstein/dalli)
         | 
| 2 2 | 
             
            =====
         | 
| 3 3 |  | 
| 4 4 | 
             
            Dalli is a high performance pure Ruby client for accessing memcached servers.  It works with memcached 1.4+ only as it uses the newer binary protocol.  It should be considered a replacement for the memcache-client gem.
         | 
| @@ -90,10 +90,12 @@ config.cache_store = :dalli_store | |
| 90 90 | 
             
            Here's a more comprehensive example that sets a reasonable default for maximum cache entry lifetime (one day), enables compression for large values and namespaces all entries for this rails app.  Remove the namespace if you have multiple apps which share cached values.
         | 
| 91 91 |  | 
| 92 92 | 
             
            ```ruby
         | 
| 93 | 
            -
            config.cache_store = :dalli_store, 'cache-1.example.com', 'cache-2.example.com',
         | 
| 93 | 
            +
            config.cache_store = :dalli_store, 'cache-1.example.com', 'cache-2.example.com:11211:2',
         | 
| 94 94 | 
             
              { :namespace => NAME_OF_RAILS_APP, :expires_in => 1.day, :compress => true }
         | 
| 95 95 | 
             
            ```
         | 
| 96 96 |  | 
| 97 | 
            +
            You can specify a port and a weight by appending to the server name. You may wish to increase the weight of a server with more memory configured.  (e.g. to specify port 11211 and a weight of 2, append `:11211:2` ) 
         | 
| 98 | 
            +
             | 
| 97 99 | 
             
            If your servers are specified in `ENV["MEMCACHE_SERVERS"]` (e.g. on Heroku when using a third-party hosted addon), simply provide `nil` for the servers:
         | 
| 98 100 |  | 
| 99 101 | 
             
            ```ruby
         | 
| @@ -147,6 +149,8 @@ end | |
| 147 149 | 
             
            Configuration
         | 
| 148 150 | 
             
            ------------------------
         | 
| 149 151 |  | 
| 152 | 
            +
            **servers**: An Array of "host:port:weight" where weight allows you to distribute cache unevenly.
         | 
| 153 | 
            +
             | 
| 150 154 | 
             
            Dalli::Client accepts the following options. All times are in seconds.
         | 
| 151 155 |  | 
| 152 156 | 
             
            **expires_in**: Global default for key TTL.  Default is 0, which means no expiry.
         | 
| @@ -178,10 +182,12 @@ If serving compressed data using nginx's HttpMemcachedModule, set `memcached_gzi | |
| 178 182 |  | 
| 179 183 | 
             
            **socket_failure_delay**: Before retrying a socket operation, the process sleeps for this amount of time. Default is 0.01.  Set to nil for no delay.
         | 
| 180 184 |  | 
| 181 | 
            -
            **down_retry_delay**: When a server has been marked down due to many failures, the server will be checked again for being alive only after this amount of time. Don't set this value too low, otherwise each request which tries the failed server might hang for the maximum **socket_timeout**. Default is  | 
| 185 | 
            +
            **down_retry_delay**: When a server has been marked down due to many failures, the server will be checked again for being alive only after this amount of time. Don't set this value too low, otherwise each request which tries the failed server might hang for the maximum **socket_timeout**. Default is 60 seconds.
         | 
| 182 186 |  | 
| 183 187 | 
             
            **value_max_bytes**: The maximum size of a value in memcached.  Defaults to 1MB, this can be increased with memcached's -I parameter.  You must also configure Dalli to allow the larger size here.
         | 
| 184 188 |  | 
| 189 | 
            +
            **error_when_over_max_size**: Boolean. If true, Dalli will throw a Dalli::ValueOverMaxSize exception when trying to store data larger than **value_max_bytes**. Defaults to false, meaning only a warning is logged.
         | 
| 190 | 
            +
             | 
| 185 191 | 
             
            **username**: The username to use for authenticating this client instance against a SASL-enabled memcached server.  Heroku users should not need to use this normally.
         | 
| 186 192 |  | 
| 187 193 | 
             
            **password**: The password to use for authenticating this client instance against a SASL-enabled memcached server.  Heroku users should not need to use this normally.
         | 
| @@ -192,6 +198,10 @@ If serving compressed data using nginx's HttpMemcachedModule, set `memcached_gzi | |
| 192 198 |  | 
| 193 199 | 
             
            **cache_nils**: Boolean. If true Dalli will not treat cached `nil` values as 'not found' for `#fetch` operations. Default is false.
         | 
| 194 200 |  | 
| 201 | 
            +
            **raise_errors**: Boolean. When true DalliStore will reraise Dalli:DalliError instead swallowing the error. Default is false.
         | 
| 202 | 
            +
             | 
| 203 | 
            +
            **instrument_errors**: Boolean. When true DalliStore will send notification of Dalli::DalliError via a 'cache_error.active_support' event. Default is false.
         | 
| 204 | 
            +
             | 
| 195 205 | 
             
            Features and Changes
         | 
| 196 206 | 
             
            ------------------------
         | 
| 197 207 |  | 
| @@ -1,4 +1,5 @@ | |
| 1 1 | 
             
            # encoding: ascii
         | 
| 2 | 
            +
            # frozen_string_literal: true
         | 
| 2 3 | 
             
            require 'dalli'
         | 
| 3 4 |  | 
| 4 5 | 
             
            module ActiveSupport
         | 
| @@ -98,24 +99,24 @@ module ActiveSupport | |
| 98 99 | 
             
                    if block_given?
         | 
| 99 100 | 
             
                      entry = not_found
         | 
| 100 101 | 
             
                      unless options[:force]
         | 
| 101 | 
            -
                        entry =  | 
| 102 | 
            +
                        entry = instrument_with_log(:read, namespaced_name, options) do |payload|
         | 
| 102 103 | 
             
                          read_entry(namespaced_name, options).tap do |result|
         | 
| 103 104 | 
             
                            if payload
         | 
| 104 105 | 
             
                              payload[:super_operation] = :fetch
         | 
| 105 | 
            -
                              payload[:hit] =  | 
| 106 | 
            +
                              payload[:hit] = not_found != result
         | 
| 106 107 | 
             
                            end
         | 
| 107 108 | 
             
                          end
         | 
| 108 109 | 
             
                        end
         | 
| 109 110 | 
             
                      end
         | 
| 110 111 |  | 
| 111 | 
            -
                      if  | 
| 112 | 
            -
                        result =  | 
| 112 | 
            +
                      if not_found == entry
         | 
| 113 | 
            +
                        result = instrument_with_log(:generate, namespaced_name, options) do |payload|
         | 
| 113 114 | 
             
                          yield
         | 
| 114 115 | 
             
                        end
         | 
| 115 116 | 
             
                        write(name, result, options)
         | 
| 116 117 | 
             
                        result
         | 
| 117 118 | 
             
                      else
         | 
| 118 | 
            -
                         | 
| 119 | 
            +
                        instrument_with_log(:fetch_hit, namespaced_name, options) { |payload| }
         | 
| 119 120 | 
             
                        entry
         | 
| 120 121 | 
             
                      end
         | 
| 121 122 | 
             
                    else
         | 
| @@ -127,7 +128,7 @@ module ActiveSupport | |
| 127 128 | 
             
                    options ||= {}
         | 
| 128 129 | 
             
                    name = namespaced_key(name, options)
         | 
| 129 130 |  | 
| 130 | 
            -
                     | 
| 131 | 
            +
                    instrument_with_log(:read, name, options) do |payload|
         | 
| 131 132 | 
             
                      entry = read_entry(name, options)
         | 
| 132 133 | 
             
                      payload[:hit] = !entry.nil? if payload
         | 
| 133 134 | 
             
                      entry
         | 
| @@ -138,7 +139,7 @@ module ActiveSupport | |
| 138 139 | 
             
                    options ||= {}
         | 
| 139 140 | 
             
                    name = namespaced_key(name, options)
         | 
| 140 141 |  | 
| 141 | 
            -
                     | 
| 142 | 
            +
                    instrument_with_log(:write, name, options) do |payload|
         | 
| 142 143 | 
             
                      with do |connection|
         | 
| 143 144 | 
             
                        options = options.merge(:connection => connection)
         | 
| 144 145 | 
             
                        write_entry(name, value, options)
         | 
| @@ -158,7 +159,7 @@ module ActiveSupport | |
| 158 159 | 
             
                    options ||= {}
         | 
| 159 160 | 
             
                    name = namespaced_key(name, options)
         | 
| 160 161 |  | 
| 161 | 
            -
                     | 
| 162 | 
            +
                    instrument_with_log(:delete, name, options) do |payload|
         | 
| 162 163 | 
             
                      delete_entry(name, options)
         | 
| 163 164 | 
             
                    end
         | 
| 164 165 | 
             
                  end
         | 
| @@ -168,7 +169,7 @@ module ActiveSupport | |
| 168 169 | 
             
                  def read_multi(*names)
         | 
| 169 170 | 
             
                    options  = names.extract_options!
         | 
| 170 171 | 
             
                    mapping = names.inject({}) { |memo, name| memo[namespaced_key(name, options)] = name; memo }
         | 
| 171 | 
            -
                     | 
| 172 | 
            +
                    instrument_with_log(:read_multi, mapping.keys) do
         | 
| 172 173 | 
             
                      results = {}
         | 
| 173 174 | 
             
                      if local_cache
         | 
| 174 175 | 
             
                        mapping.each_key do |key|
         | 
| @@ -199,7 +200,7 @@ module ActiveSupport | |
| 199 200 | 
             
                    options = names.extract_options!
         | 
| 200 201 | 
             
                    mapping = names.inject({}) { |memo, name| memo[namespaced_key(name, options)] = name; memo }
         | 
| 201 202 |  | 
| 202 | 
            -
                     | 
| 203 | 
            +
                    instrument_with_log(:fetch_multi, mapping.keys) do
         | 
| 203 204 | 
             
                      with do |connection|
         | 
| 204 205 | 
             
                        results = connection.get_multi(mapping.keys)
         | 
| 205 206 |  | 
| @@ -230,11 +231,12 @@ module ActiveSupport | |
| 230 231 | 
             
                    name = namespaced_key(name, options)
         | 
| 231 232 | 
             
                    initial = options.has_key?(:initial) ? options[:initial] : amount
         | 
| 232 233 | 
             
                    expires_in = options[:expires_in]
         | 
| 233 | 
            -
                     | 
| 234 | 
            +
                    instrument_with_log(:increment, name, :amount => amount) do
         | 
| 234 235 | 
             
                      with { |c| c.incr(name, amount, expires_in, initial) }
         | 
| 235 236 | 
             
                    end
         | 
| 236 237 | 
             
                  rescue Dalli::DalliError => e
         | 
| 237 | 
            -
                     | 
| 238 | 
            +
                    log_dalli_error(e)
         | 
| 239 | 
            +
                    instrument_error(e) if instrument_errors?
         | 
| 238 240 | 
             
                    raise if raise_errors?
         | 
| 239 241 | 
             
                    nil
         | 
| 240 242 | 
             
                  end
         | 
| @@ -249,11 +251,12 @@ module ActiveSupport | |
| 249 251 | 
             
                    name = namespaced_key(name, options)
         | 
| 250 252 | 
             
                    initial = options.has_key?(:initial) ? options[:initial] : 0
         | 
| 251 253 | 
             
                    expires_in = options[:expires_in]
         | 
| 252 | 
            -
                     | 
| 254 | 
            +
                    instrument_with_log(:decrement, name, :amount => amount) do
         | 
| 253 255 | 
             
                      with { |c| c.decr(name, amount, expires_in, initial) }
         | 
| 254 256 | 
             
                    end
         | 
| 255 257 | 
             
                  rescue Dalli::DalliError => e
         | 
| 256 | 
            -
                     | 
| 258 | 
            +
                    log_dalli_error(e)
         | 
| 259 | 
            +
                    instrument_error(e) if instrument_errors?
         | 
| 257 260 | 
             
                    raise if raise_errors?
         | 
| 258 261 | 
             
                    nil
         | 
| 259 262 | 
             
                  end
         | 
| @@ -261,11 +264,12 @@ module ActiveSupport | |
| 261 264 | 
             
                  # Clear the entire cache on all memcached servers. This method should
         | 
| 262 265 | 
             
                  # be used with care when using a shared cache.
         | 
| 263 266 | 
             
                  def clear(options=nil)
         | 
| 264 | 
            -
                     | 
| 267 | 
            +
                    instrument_with_log(:clear, 'flushing all keys') do
         | 
| 265 268 | 
             
                      with { |c| c.flush_all }
         | 
| 266 269 | 
             
                    end
         | 
| 267 270 | 
             
                  rescue Dalli::DalliError => e
         | 
| 268 | 
            -
                     | 
| 271 | 
            +
                    log_dalli_error(e)
         | 
| 272 | 
            +
                    instrument_error(e) if instrument_errors?
         | 
| 269 273 | 
             
                    raise if raise_errors?
         | 
| 270 274 | 
             
                    nil
         | 
| 271 275 | 
             
                  end
         | 
| @@ -299,7 +303,8 @@ module ActiveSupport | |
| 299 303 | 
             
                    # NB Backwards data compatibility, to be removed at some point
         | 
| 300 304 | 
             
                    entry.is_a?(ActiveSupport::Cache::Entry) ? entry.value : entry
         | 
| 301 305 | 
             
                  rescue Dalli::DalliError => e
         | 
| 302 | 
            -
                     | 
| 306 | 
            +
                    log_dalli_error(e)
         | 
| 307 | 
            +
                    instrument_error(e) if instrument_errors?
         | 
| 303 308 | 
             
                    raise if raise_errors?
         | 
| 304 309 | 
             
                    nil
         | 
| 305 310 | 
             
                  end
         | 
| @@ -313,7 +318,8 @@ module ActiveSupport | |
| 313 318 | 
             
                    connection = options.delete(:connection)
         | 
| 314 319 | 
             
                    connection.send(method, key, value, expires_in, options)
         | 
| 315 320 | 
             
                  rescue Dalli::DalliError => e
         | 
| 316 | 
            -
                     | 
| 321 | 
            +
                    log_dalli_error(e)
         | 
| 322 | 
            +
                    instrument_error(e) if instrument_errors?
         | 
| 317 323 | 
             
                    raise if raise_errors?
         | 
| 318 324 | 
             
                    false
         | 
| 319 325 | 
             
                  end
         | 
| @@ -322,7 +328,8 @@ module ActiveSupport | |
| 322 328 | 
             
                  def delete_entry(key, options) # :nodoc:
         | 
| 323 329 | 
             
                    with { |c| c.delete(key) }
         | 
| 324 330 | 
             
                  rescue Dalli::DalliError => e
         | 
| 325 | 
            -
                     | 
| 331 | 
            +
                    log_dalli_error(e)
         | 
| 332 | 
            +
                    instrument_error(e) if instrument_errors?
         | 
| 326 333 | 
             
                    raise if raise_errors?
         | 
| 327 334 | 
             
                    false
         | 
| 328 335 | 
             
                  end
         | 
| @@ -334,6 +341,7 @@ module ActiveSupport | |
| 334 341 | 
             
                    namespace = options[:namespace] if options
         | 
| 335 342 | 
             
                    prefix = namespace.is_a?(Proc) ? namespace.call : namespace
         | 
| 336 343 | 
             
                    key = "#{prefix}:#{key}" if prefix
         | 
| 344 | 
            +
                    key = "#{key[0, 213]}:md5:#{Digest::MD5.hexdigest(key)}" if key && key.size > 250
         | 
| 337 345 | 
             
                    key
         | 
| 338 346 | 
             
                  end
         | 
| 339 347 | 
             
                  alias :normalize_key :namespaced_key
         | 
| @@ -363,12 +371,26 @@ module ActiveSupport | |
| 363 371 | 
             
                    key
         | 
| 364 372 | 
             
                  end
         | 
| 365 373 |  | 
| 366 | 
            -
                  def  | 
| 374 | 
            +
                  def log_dalli_error(error)
         | 
| 375 | 
            +
                    logger.error("DalliError: #{error.message}") if logger
         | 
| 376 | 
            +
                  end
         | 
| 377 | 
            +
             | 
| 378 | 
            +
                  def instrument_with_log(operation, key, options=nil)
         | 
| 367 379 | 
             
                    log(operation, key, options)
         | 
| 368 380 |  | 
| 369 381 | 
             
                    payload = { :key => key }
         | 
| 370 382 | 
             
                    payload.merge!(options) if options.is_a?(Hash)
         | 
| 371 | 
            -
                     | 
| 383 | 
            +
                    instrument(operation, payload) { |p| yield(p) }
         | 
| 384 | 
            +
                  end
         | 
| 385 | 
            +
             | 
| 386 | 
            +
                  def instrument_error(error)
         | 
| 387 | 
            +
                    instrument(:error, { :key => 'DalliError', :message => error.message })
         | 
| 388 | 
            +
                  end
         | 
| 389 | 
            +
             | 
| 390 | 
            +
                  def instrument(operation, payload)
         | 
| 391 | 
            +
                    ActiveSupport::Notifications.instrument("cache_#{operation}.active_support", payload) do
         | 
| 392 | 
            +
                      yield(payload) if block_given?
         | 
| 393 | 
            +
                    end
         | 
| 372 394 | 
             
                  end
         | 
| 373 395 |  | 
| 374 396 | 
             
                  def log(operation, key, options=nil)
         | 
| @@ -380,6 +402,10 @@ module ActiveSupport | |
| 380 402 | 
             
                    !!@options[:raise_errors]
         | 
| 381 403 | 
             
                  end
         | 
| 382 404 |  | 
| 405 | 
            +
                  def instrument_errors?
         | 
| 406 | 
            +
                    !!@options[:instrument_errors]
         | 
| 407 | 
            +
                  end
         | 
| 408 | 
            +
             | 
| 383 409 | 
             
                  # Make sure LocalCache is giving raw values, not `Entry`s, and
         | 
| 384 410 | 
             
                  # respect `raw` option.
         | 
| 385 411 | 
             
                  module LocalCacheEntryUnwrapAndRaw # :nodoc:
         | 
    
        data/lib/dalli.rb
    CHANGED
    
    | @@ -1,3 +1,4 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 1 2 | 
             
            require 'dalli/compressor'
         | 
| 2 3 | 
             
            require 'dalli/client'
         | 
| 3 4 | 
             
            require 'dalli/ring'
         | 
| @@ -18,6 +19,8 @@ module Dalli | |
| 18 19 | 
             
              class MarshalError < DalliError; end
         | 
| 19 20 | 
             
              # application error in marshalling deserialization or decompression
         | 
| 20 21 | 
             
              class UnmarshalError < DalliError; end
         | 
| 22 | 
            +
              # payload too big for memcached
         | 
| 23 | 
            +
              class ValueOverMaxSize < RuntimeError; end
         | 
| 21 24 |  | 
| 22 25 | 
             
              def self.logger
         | 
| 23 26 | 
             
                @logger ||= (rails_logger || default_logger)
         | 
    
        data/lib/dalli/cas/client.rb
    CHANGED
    
    
    
        data/lib/dalli/client.rb
    CHANGED
    
    
    
        data/lib/dalli/compressor.rb
    CHANGED
    
    | @@ -1,3 +1,4 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 1 2 | 
             
            require 'zlib'
         | 
| 2 3 | 
             
            require 'stringio'
         | 
| 3 4 |  | 
| @@ -14,7 +15,7 @@ module Dalli | |
| 14 15 |  | 
| 15 16 | 
             
              class GzipCompressor
         | 
| 16 17 | 
             
                def self.compress(data)
         | 
| 17 | 
            -
                  io = StringIO.new("w")
         | 
| 18 | 
            +
                  io = StringIO.new(String.new(""), "w")
         | 
| 18 19 | 
             
                  gz = Zlib::GzipWriter.new(io)
         | 
| 19 20 | 
             
                  gz.write(data)
         | 
| 20 21 | 
             
                  gz.close
         | 
    
        data/lib/dalli/options.rb
    CHANGED
    
    
    
        data/lib/dalli/railtie.rb
    CHANGED
    
    
    
        data/lib/dalli/ring.rb
    CHANGED
    
    | @@ -1,3 +1,4 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 1 2 | 
             
            require 'digest/sha1'
         | 
| 2 3 | 
             
            require 'zlib'
         | 
| 3 4 |  | 
| @@ -20,7 +21,7 @@ module Dalli | |
| 20 21 | 
             
                        continuum << Dalli::Ring::Entry.new(value, server)
         | 
| 21 22 | 
             
                      end
         | 
| 22 23 | 
             
                    end
         | 
| 23 | 
            -
                    @continuum = continuum. | 
| 24 | 
            +
                    @continuum = continuum.sort_by(&:value)
         | 
| 24 25 | 
             
                  end
         | 
| 25 26 |  | 
| 26 27 | 
             
                  threadsafe! unless options[:threadsafe] == false
         | 
| @@ -110,7 +111,6 @@ module Dalli | |
| 110 111 | 
             
                  def binary_search(ary, value)
         | 
| 111 112 | 
             
                    upper = ary.size - 1
         | 
| 112 113 | 
             
                    lower = 0
         | 
| 113 | 
            -
                    idx = 0
         | 
| 114 114 |  | 
| 115 115 | 
             
                    while (lower <= upper) do
         | 
| 116 116 | 
             
                      idx = (lower + upper) / 2
         | 
| @@ -124,7 +124,7 @@ module Dalli | |
| 124 124 | 
             
                        lower = idx + 1
         | 
| 125 125 | 
             
                      end
         | 
| 126 126 | 
             
                    end
         | 
| 127 | 
            -
                     | 
| 127 | 
            +
                    upper
         | 
| 128 128 | 
             
                  end
         | 
| 129 129 | 
             
                end
         | 
| 130 130 |  | 
    
        data/lib/dalli/server.rb
    CHANGED
    
    | @@ -1,3 +1,4 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 1 2 | 
             
            require 'socket'
         | 
| 2 3 | 
             
            require 'timeout'
         | 
| 3 4 |  | 
| @@ -14,7 +15,7 @@ module Dalli | |
| 14 15 | 
             
                DEFAULT_WEIGHT = 1
         | 
| 15 16 | 
             
                DEFAULTS = {
         | 
| 16 17 | 
             
                  # seconds between trying to contact a remote server
         | 
| 17 | 
            -
                  :down_retry_delay =>  | 
| 18 | 
            +
                  :down_retry_delay => 60,
         | 
| 18 19 | 
             
                  # connect/read/write timeout for socket operations
         | 
| 19 20 | 
             
                  :socket_timeout => 0.5,
         | 
| 20 21 | 
             
                  # times a socket operation may fail before considering the server dead
         | 
| @@ -23,6 +24,8 @@ module Dalli | |
| 23 24 | 
             
                  :socket_failure_delay => 0.01,
         | 
| 24 25 | 
             
                  # max size of value in bytes (default is 1 MB, can be overriden with "memcached -I <size>")
         | 
| 25 26 | 
             
                  :value_max_bytes => 1024 * 1024,
         | 
| 27 | 
            +
                  # surpassing value_max_bytes either warns (false) or throws (true)
         | 
| 28 | 
            +
                  :error_when_over_max_size => false,
         | 
| 26 29 | 
             
                  :compressor => Compressor,
         | 
| 27 30 | 
             
                  # min byte size to attempt compression
         | 
| 28 31 | 
             
                  :compression_min_size => 1024,
         | 
| @@ -65,16 +68,12 @@ module Dalli | |
| 65 68 | 
             
                  raise Dalli::NetworkError, "#{name} is down: #{@error} #{@msg}. If you are sure it is running, ensure memcached version is > 1.4." unless alive?
         | 
| 66 69 | 
             
                  begin
         | 
| 67 70 | 
             
                    send(op, *args)
         | 
| 68 | 
            -
                  rescue Dalli::NetworkError
         | 
| 69 | 
            -
                    raise
         | 
| 70 71 | 
             
                  rescue Dalli::MarshalError => ex
         | 
| 71 72 | 
             
                    Dalli.logger.error "Marshalling error for key '#{args.first}': #{ex.message}"
         | 
| 72 73 | 
             
                    Dalli.logger.error "You are trying to cache a Ruby object which cannot be serialized to memcached."
         | 
| 73 74 | 
             
                    Dalli.logger.error ex.backtrace.join("\n\t")
         | 
| 74 75 | 
             
                    false
         | 
| 75 | 
            -
                  rescue Dalli::DalliError
         | 
| 76 | 
            -
                    raise
         | 
| 77 | 
            -
                  rescue Timeout::Error
         | 
| 76 | 
            +
                  rescue Dalli::DalliError, Dalli::NetworkError, Dalli::ValueOverMaxSize, Timeout::Error
         | 
| 78 77 | 
             
                    raise
         | 
| 79 78 | 
             
                  rescue => ex
         | 
| 80 79 | 
             
                    Dalli.logger.error "Unexpected exception during Dalli request: #{ex.class.name}: #{ex.message}"
         | 
| @@ -128,7 +127,7 @@ module Dalli | |
| 128 127 | 
             
                def multi_response_start
         | 
| 129 128 | 
             
                  verify_state
         | 
| 130 129 | 
             
                  write_noop
         | 
| 131 | 
            -
                  @multi_buffer = ''
         | 
| 130 | 
            +
                  @multi_buffer = String.new('')
         | 
| 132 131 | 
             
                  @position = 0
         | 
| 133 132 | 
             
                  @inprogress = true
         | 
| 134 133 | 
             
                end
         | 
| @@ -274,7 +273,7 @@ module Dalli | |
| 274 273 | 
             
                end
         | 
| 275 274 |  | 
| 276 275 | 
             
                def send_multiget(keys)
         | 
| 277 | 
            -
                  req = ""
         | 
| 276 | 
            +
                  req = String.new("")
         | 
| 278 277 | 
             
                  keys.each do |key|
         | 
| 279 278 | 
             
                    req << [REQUEST, OPCODES[:getkq], key.bytesize, 0, 0, 0, key.bytesize, 0, 0, key].pack(FORMAT[:getkq])
         | 
| 280 279 | 
             
                  end
         | 
| @@ -475,19 +474,24 @@ module Dalli | |
| 475 474 | 
             
                  if value.bytesize <= @options[:value_max_bytes]
         | 
| 476 475 | 
             
                    yield
         | 
| 477 476 | 
             
                  else
         | 
| 478 | 
            -
                     | 
| 477 | 
            +
                    message = "Value for #{key} over max size: #{@options[:value_max_bytes]} <= #{value.bytesize}"
         | 
| 478 | 
            +
                    raise Dalli::ValueOverMaxSize, message if @options[:error_when_over_max_size]
         | 
| 479 | 
            +
             | 
| 480 | 
            +
                    Dalli.logger.warn message
         | 
| 479 481 | 
             
                    false
         | 
| 480 482 | 
             
                  end
         | 
| 481 483 | 
             
                end
         | 
| 482 484 |  | 
| 483 | 
            -
                # https:// | 
| 485 | 
            +
                # https://github.com/memcached/memcached/blob/master/doc/protocol.txt#L79
         | 
| 484 486 | 
             
                # > An expiration time, in seconds. Can be up to 30 days. After 30 days, is treated as a unix timestamp of an exact date.
         | 
| 485 487 | 
             
                MAX_ACCEPTABLE_EXPIRATION_INTERVAL = 30*24*60*60 # 30 days
         | 
| 486 488 | 
             
                def sanitize_ttl(ttl)
         | 
| 487 489 | 
             
                  ttl_as_i = ttl.to_i
         | 
| 488 490 | 
             
                  return ttl_as_i if ttl_as_i <= MAX_ACCEPTABLE_EXPIRATION_INTERVAL
         | 
| 491 | 
            +
                  now = Time.now.to_i
         | 
| 492 | 
            +
                  return ttl_as_i if ttl_as_i > now # already a timestamp
         | 
| 489 493 | 
             
                  Dalli.logger.debug "Expiration interval (#{ttl_as_i}) too long for Memcached, converting to an expiration timestamp"
         | 
| 490 | 
            -
                   | 
| 494 | 
            +
                  now + ttl_as_i
         | 
| 491 495 | 
             
                end
         | 
| 492 496 |  | 
| 493 497 | 
             
                # Implements the NullObject pattern to store an application-defined value for 'Key not found' responses.
         | 
| @@ -604,7 +608,7 @@ module Dalli | |
| 604 608 | 
             
                RESPONSE = 0x81
         | 
| 605 609 |  | 
| 606 610 | 
             
                # Response codes taken from:
         | 
| 607 | 
            -
                # https:// | 
| 611 | 
            +
                # https://github.com/memcached/memcached/wiki/BinaryProtocolRevamped#response-status
         | 
| 608 612 | 
             
                RESPONSE_CODES = {
         | 
| 609 613 | 
             
                  0 => 'No error',
         | 
| 610 614 | 
             
                  1 => 'Key not found',
         | 
| @@ -698,7 +702,7 @@ module Dalli | |
| 698 702 | 
             
                  req = [REQUEST, OPCODES[:auth_negotiation], 0, 0, 0, 0, 0, 0, 0].pack(FORMAT[:noop])
         | 
| 699 703 | 
             
                  write(req)
         | 
| 700 704 |  | 
| 701 | 
            -
                  (extras,  | 
| 705 | 
            +
                  (extras, _type, status, count) = read_header.unpack(NORMAL_HEADER)
         | 
| 702 706 | 
             
                  raise Dalli::NetworkError, "Unexpected message format: #{extras} #{count}" unless extras == 0 && count > 0
         | 
| 703 707 | 
             
                  content = read(count).gsub(/\u0000/, ' ')
         | 
| 704 708 | 
             
                  return (Dalli.logger.debug("Authentication not required/supported by server")) if status == 0x81
         | 
| @@ -711,7 +715,7 @@ module Dalli | |
| 711 715 | 
             
                  req = [REQUEST, OPCODES[:auth_request], mechanism.bytesize, 0, 0, 0, mechanism.bytesize + msg.bytesize, 0, 0, mechanism, msg].pack(FORMAT[:auth_request])
         | 
| 712 716 | 
             
                  write(req)
         | 
| 713 717 |  | 
| 714 | 
            -
                  (extras,  | 
| 718 | 
            +
                  (extras, _type, status, count) = read_header.unpack(NORMAL_HEADER)
         | 
| 715 719 | 
             
                  raise Dalli::NetworkError, "Unexpected message format: #{extras} #{count}" unless extras == 0 && count > 0
         | 
| 716 720 | 
             
                  content = read(count)
         | 
| 717 721 | 
             
                  return Dalli.logger.info("Dalli/SASL: #{content}") if status == 0
         | 
    
        data/lib/dalli/socket.rb
    CHANGED
    
    | @@ -1,3 +1,4 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 1 2 | 
             
            require 'rbconfig'
         | 
| 2 3 |  | 
| 3 4 | 
             
            module Dalli::Server::TCPSocketOptions
         | 
| @@ -27,7 +28,7 @@ begin | |
| 27 28 | 
             
                alias :write :kgio_write
         | 
| 28 29 |  | 
| 29 30 | 
             
                def readfull(count)
         | 
| 30 | 
            -
                  value = ''
         | 
| 31 | 
            +
                  value = String.new('')
         | 
| 31 32 | 
             
                  while true
         | 
| 32 33 | 
             
                    value << kgio_read!(count - value.bytesize)
         | 
| 33 34 | 
             
                    break if value.bytesize == count
         | 
| @@ -36,7 +37,7 @@ begin | |
| 36 37 | 
             
                end
         | 
| 37 38 |  | 
| 38 39 | 
             
                def read_available
         | 
| 39 | 
            -
                  value = ''
         | 
| 40 | 
            +
                  value = String.new('')
         | 
| 40 41 | 
             
                  while true
         | 
| 41 42 | 
             
                    ret = kgio_tryread(8196)
         | 
| 42 43 | 
             
                    case ret
         | 
| @@ -63,6 +64,9 @@ begin | |
| 63 64 | 
             
                  sock.server = server
         | 
| 64 65 | 
             
                  sock.kgio_wait_writable
         | 
| 65 66 | 
             
                  sock
         | 
| 67 | 
            +
                rescue Timeout::Error
         | 
| 68 | 
            +
                  sock.close if sock
         | 
| 69 | 
            +
                  raise
         | 
| 66 70 | 
             
                end
         | 
| 67 71 | 
             
              end
         | 
| 68 72 |  | 
| @@ -74,6 +78,9 @@ begin | |
| 74 78 | 
             
                  sock.server = server
         | 
| 75 79 | 
             
                  sock.kgio_wait_writable
         | 
| 76 80 | 
             
                  sock
         | 
| 81 | 
            +
                rescue Timeout::Error
         | 
| 82 | 
            +
                  sock.close if sock
         | 
| 83 | 
            +
                  raise
         | 
| 77 84 | 
             
                end
         | 
| 78 85 | 
             
              end
         | 
| 79 86 |  | 
| @@ -88,7 +95,7 @@ rescue LoadError | |
| 88 95 | 
             
              module Dalli::Server::KSocket
         | 
| 89 96 | 
             
                module InstanceMethods
         | 
| 90 97 | 
             
                  def readfull(count)
         | 
| 91 | 
            -
                    value = ''
         | 
| 98 | 
            +
                    value = String.new('')
         | 
| 92 99 | 
             
                    begin
         | 
| 93 100 | 
             
                      while true
         | 
| 94 101 | 
             
                        value << read_nonblock(count - value.bytesize)
         | 
| @@ -98,14 +105,15 @@ rescue LoadError | |
| 98 105 | 
             
                      if IO.select([self], nil, nil, options[:socket_timeout])
         | 
| 99 106 | 
             
                        retry
         | 
| 100 107 | 
             
                      else
         | 
| 101 | 
            -
                         | 
| 108 | 
            +
                        safe_options = options.reject{|k,v| [:username, :password].include? k}
         | 
| 109 | 
            +
                        raise Timeout::Error, "IO timeout: #{safe_options.inspect}"
         | 
| 102 110 | 
             
                      end
         | 
| 103 111 | 
             
                    end
         | 
| 104 112 | 
             
                    value
         | 
| 105 113 | 
             
                  end
         | 
| 106 114 |  | 
| 107 115 | 
             
                  def read_available
         | 
| 108 | 
            -
                    value = ''
         | 
| 116 | 
            +
                    value = String.new('')
         | 
| 109 117 | 
             
                    while true
         | 
| 110 118 | 
             
                      begin
         | 
| 111 119 | 
             
                        value << read_nonblock(8196)
         | 
    
        data/lib/dalli/version.rb
    CHANGED
    
    
    
        data/lib/rack/session/dalli.rb
    CHANGED
    
    | @@ -1,3 +1,4 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 1 2 | 
             
            require 'rack/session/abstract/id'
         | 
| 2 3 | 
             
            require 'dalli'
         | 
| 3 4 |  | 
| @@ -6,17 +7,118 @@ module Rack | |
| 6 7 | 
             
                class Dalli < defined?(Abstract::Persisted) ? Abstract::Persisted : Abstract::ID
         | 
| 7 8 | 
             
                  attr_reader :pool, :mutex
         | 
| 8 9 |  | 
| 9 | 
            -
                   | 
| 10 | 
            +
                  DEFAULT_DALLI_OPTIONS = {
         | 
| 10 11 | 
             
                    :namespace => 'rack:session',
         | 
| 11 12 | 
             
                    :memcache_server => 'localhost:11211'
         | 
| 13 | 
            +
                  }
         | 
| 12 14 |  | 
| 15 | 
            +
                  # Brings in a new Rack::Session::Dalli middleware with the given
         | 
| 16 | 
            +
                  # `:memcache_server`. The server is either a hostname, or a
         | 
| 17 | 
            +
                  # host-with-port string in the form of "host_name:port", or an array of
         | 
| 18 | 
            +
                  # such strings. For example:
         | 
| 19 | 
            +
                  #
         | 
| 20 | 
            +
                  #   use Rack::Session::Dalli,
         | 
| 21 | 
            +
                  #     :memcache_server => "mc.example.com:1234"
         | 
| 22 | 
            +
                  #
         | 
| 23 | 
            +
                  # If no `:memcache_server` option is specified, Rack::Session::Dalli will
         | 
| 24 | 
            +
                  # connect to localhost, port 11211 (the default memcached port). If
         | 
| 25 | 
            +
                  # `:memcache_server` is set to nil, Dalli::Client will look for
         | 
| 26 | 
            +
                  # ENV['MEMCACHE_SERVERS'] and use that value if it is available, or fall
         | 
| 27 | 
            +
                  # back to the same default behavior described above.
         | 
| 28 | 
            +
                  #
         | 
| 29 | 
            +
                  # Rack::Session::Dalli is intended to be a drop-in replacement for
         | 
| 30 | 
            +
                  # Rack::Session::Memcache. It accepts additional options that control the
         | 
| 31 | 
            +
                  # behavior of Rack::Session, Dalli::Client, and an optional
         | 
| 32 | 
            +
                  # ConnectionPool. First and foremost, if you wish to instantiate your own
         | 
| 33 | 
            +
                  # Dalli::Client (or ConnectionPool) and use that instead of letting
         | 
| 34 | 
            +
                  # Rack::Session::Dalli instantiate it on your behalf, simply pass it in
         | 
| 35 | 
            +
                  # as the `:cache` option. Please note that you will be responsible for
         | 
| 36 | 
            +
                  # setting the namespace and any other options on Dalli::Client.
         | 
| 37 | 
            +
                  #
         | 
| 38 | 
            +
                  # Secondly, if you're not using the `:cache` option, Rack::Session::Dalli
         | 
| 39 | 
            +
                  # accepts the same options as Dalli::Client, so it's worth reviewing its
         | 
| 40 | 
            +
                  # documentation. Perhaps most importantly, if you don't specify a
         | 
| 41 | 
            +
                  # `:namespace` option, Rack::Session::Dalli will default to using
         | 
| 42 | 
            +
                  # "rack:session".
         | 
| 43 | 
            +
                  #
         | 
| 44 | 
            +
                  # Whether you are using the `:cache` option or not, it is not recommend
         | 
| 45 | 
            +
                  # to set `:expires_in`. Instead, use `:expire_after`, which will control
         | 
| 46 | 
            +
                  # both the expiration of the client cookie as well as the expiration of
         | 
| 47 | 
            +
                  # the corresponding entry in memcached.
         | 
| 48 | 
            +
                  #
         | 
| 49 | 
            +
                  # Rack::Session::Dalli also accepts a host of options that control how
         | 
| 50 | 
            +
                  # the sessions and session cookies are managed, including the
         | 
| 51 | 
            +
                  # aforementioned `:expire_after` option. Please see the documentation for
         | 
| 52 | 
            +
                  # Rack::Session::Abstract::Persisted for a detailed explanation of these
         | 
| 53 | 
            +
                  # options and their default values.
         | 
| 54 | 
            +
                  #
         | 
| 55 | 
            +
                  # Finally, if your web application is multithreaded, the
         | 
| 56 | 
            +
                  # Rack::Session::Dalli middleware can become a source of contention. You
         | 
| 57 | 
            +
                  # can use a connection pool of Dalli clients by passing in the
         | 
| 58 | 
            +
                  # `:pool_size` and/or `:pool_timeout` options. For example:
         | 
| 59 | 
            +
                  #
         | 
| 60 | 
            +
                  #   use Rack::Session::Dalli,
         | 
| 61 | 
            +
                  #     :memcache_server => "mc.example.com:1234",
         | 
| 62 | 
            +
                  #     :pool_size => 10
         | 
| 63 | 
            +
                  #
         | 
| 64 | 
            +
                  # You must include the `connection_pool` gem in your project if you wish
         | 
| 65 | 
            +
                  # to use pool support. Please see the documentation for ConnectionPool
         | 
| 66 | 
            +
                  # for more information about it and its default options (which would only
         | 
| 67 | 
            +
                  # be applicable if you supplied one of the two options, but not both).
         | 
| 68 | 
            +
                  #
         | 
| 13 69 | 
             
                  def initialize(app, options={})
         | 
| 70 | 
            +
                    # Parent uses DEFAULT_OPTIONS to build @default_options for Rack::Session
         | 
| 14 71 | 
             
                    super
         | 
| 15 | 
            -
             | 
| 16 | 
            -
                     | 
| 17 | 
            -
                     | 
| 18 | 
            -
             | 
| 19 | 
            -
                     | 
| 72 | 
            +
             | 
| 73 | 
            +
                    # Determine the default TTL for newly-created sessions
         | 
| 74 | 
            +
                    @default_ttl = ttl @default_options[:expire_after]
         | 
| 75 | 
            +
             | 
| 76 | 
            +
                    # Normalize and validate passed options
         | 
| 77 | 
            +
                    cache, mserv, mopts, popts = extract_dalli_options options
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                    @pool =
         | 
| 80 | 
            +
                      if cache # caller passed a Dalli::Client or ConnectionPool instance
         | 
| 81 | 
            +
                        cache
         | 
| 82 | 
            +
                      elsif popts # caller passed ConnectionPool options
         | 
| 83 | 
            +
                        ConnectionPool.new(popts) { ::Dalli::Client.new(mserv, mopts) }
         | 
| 84 | 
            +
                      else
         | 
| 85 | 
            +
                        ::Dalli::Client.new(mserv, mopts)
         | 
| 86 | 
            +
                      end
         | 
| 87 | 
            +
             | 
| 88 | 
            +
                    if @pool.respond_to?(:alive!) # is a Dalli::Client
         | 
| 89 | 
            +
                      @mutex = Mutex.new
         | 
| 90 | 
            +
             | 
| 91 | 
            +
                      @pool.alive!
         | 
| 92 | 
            +
                    end
         | 
| 93 | 
            +
                  end
         | 
| 94 | 
            +
             | 
| 95 | 
            +
                  def get_session(env, sid)
         | 
| 96 | 
            +
                    with_block(env, [nil, {}]) do |dc|
         | 
| 97 | 
            +
                      unless sid and !sid.empty? and session = dc.get(sid)
         | 
| 98 | 
            +
                        old_sid, sid, session = sid, generate_sid_with(dc), {}
         | 
| 99 | 
            +
                        unless dc.add(sid, session, @default_ttl)
         | 
| 100 | 
            +
                          sid = old_sid
         | 
| 101 | 
            +
                          redo # generate a new sid and try again
         | 
| 102 | 
            +
                        end
         | 
| 103 | 
            +
                      end
         | 
| 104 | 
            +
                      [sid, session]
         | 
| 105 | 
            +
                    end
         | 
| 106 | 
            +
                  end
         | 
| 107 | 
            +
             | 
| 108 | 
            +
                  def set_session(env, session_id, new_session, options)
         | 
| 109 | 
            +
                    return false unless session_id
         | 
| 110 | 
            +
             | 
| 111 | 
            +
                    with_block(env, false) do |dc|
         | 
| 112 | 
            +
                      dc.set(session_id, new_session, ttl(options[:expire_after]))
         | 
| 113 | 
            +
                      session_id
         | 
| 114 | 
            +
                    end
         | 
| 115 | 
            +
                  end
         | 
| 116 | 
            +
             | 
| 117 | 
            +
                  def destroy_session(env, session_id, options)
         | 
| 118 | 
            +
                    with_block(env) do |dc|
         | 
| 119 | 
            +
                      dc.delete(session_id)
         | 
| 120 | 
            +
                      generate_sid_with(dc) unless options[:drop]
         | 
| 121 | 
            +
                    end
         | 
| 20 122 | 
             
                  end
         | 
| 21 123 |  | 
| 22 124 | 
             
                  if defined?(Abstract::Persisted)
         | 
| @@ -33,46 +135,47 @@ module Rack | |
| 33 135 | 
             
                    end
         | 
| 34 136 | 
             
                  end
         | 
| 35 137 |  | 
| 36 | 
            -
                   | 
| 37 | 
            -
                    while true
         | 
| 38 | 
            -
                      sid = super
         | 
| 39 | 
            -
                      break sid unless @pool.get(sid)
         | 
| 40 | 
            -
                    end
         | 
| 41 | 
            -
                  end
         | 
| 138 | 
            +
                  private
         | 
| 42 139 |  | 
| 43 | 
            -
                  def  | 
| 44 | 
            -
                     | 
| 45 | 
            -
             | 
| 46 | 
            -
             | 
| 47 | 
            -
             | 
| 48 | 
            -
             | 
| 49 | 
            -
             | 
| 50 | 
            -
             | 
| 51 | 
            -
             | 
| 140 | 
            +
                  def extract_dalli_options(options)
         | 
| 141 | 
            +
                    return [options[:cache]] if options[:cache]
         | 
| 142 | 
            +
             | 
| 143 | 
            +
                    # Filter out Rack::Session-specific options and apply our defaults
         | 
| 144 | 
            +
                    mopts = DEFAULT_DALLI_OPTIONS.merge \
         | 
| 145 | 
            +
                      options.reject {|k, _| DEFAULT_OPTIONS.key? k }
         | 
| 146 | 
            +
                    mserv = mopts.delete :memcache_server
         | 
| 147 | 
            +
             | 
| 148 | 
            +
                    if mopts[:pool_size] || mopts[:pool_timeout]
         | 
| 149 | 
            +
                      popts = {}
         | 
| 150 | 
            +
                      popts[:size] = mopts.delete :pool_size if mopts[:pool_size]
         | 
| 151 | 
            +
                      popts[:timeout] = mopts.delete :pool_timeout if mopts[:pool_timeout]
         | 
| 152 | 
            +
             | 
| 153 | 
            +
                      # For a connection pool, locking is handled at the pool level
         | 
| 154 | 
            +
                      mopts[:threadsafe] = false unless mopts.key? :threadsafe
         | 
| 52 155 | 
             
                    end
         | 
| 156 | 
            +
             | 
| 157 | 
            +
                    [nil, mserv, mopts, popts]
         | 
| 53 158 | 
             
                  end
         | 
| 54 159 |  | 
| 55 | 
            -
                   | 
| 56 | 
            -
             | 
| 57 | 
            -
                    expiry = options[:expire_after]
         | 
| 58 | 
            -
                    expiry = expiry.nil? ? 0 : expiry + 1
         | 
| 160 | 
            +
                  # Capture generate_sid's super so we can call it from generate_sid_with
         | 
| 161 | 
            +
                  alias_method :generate_sid_super, :generate_sid
         | 
| 59 162 |  | 
| 60 | 
            -
             | 
| 61 | 
            -
             | 
| 62 | 
            -
             | 
| 63 | 
            -
                     | 
| 163 | 
            +
                  def generate_sid
         | 
| 164 | 
            +
                    # no way to check env['rack.multithread'] here so fall back on
         | 
| 165 | 
            +
                    # Dalli::Client or ConnectionPool's internal mutex cf. our own
         | 
| 166 | 
            +
                    @pool.with {|dc| generate_sid_with(dc) }
         | 
| 64 167 | 
             
                  end
         | 
| 65 168 |  | 
| 66 | 
            -
                  def  | 
| 67 | 
            -
                     | 
| 68 | 
            -
                       | 
| 69 | 
            -
                       | 
| 169 | 
            +
                  def generate_sid_with(dc)
         | 
| 170 | 
            +
                    while true
         | 
| 171 | 
            +
                      sid = generate_sid_super
         | 
| 172 | 
            +
                      break sid unless dc.get(sid)
         | 
| 70 173 | 
             
                    end
         | 
| 71 174 | 
             
                  end
         | 
| 72 175 |  | 
| 73 | 
            -
                  def  | 
| 74 | 
            -
                    @mutex.lock if env['rack.multithread']
         | 
| 75 | 
            -
                     | 
| 176 | 
            +
                  def with_block(env, default=nil, &block)
         | 
| 177 | 
            +
                    @mutex.lock if @mutex and env['rack.multithread']
         | 
| 178 | 
            +
                    @pool.with(&block)
         | 
| 76 179 | 
             
                  rescue ::Dalli::DalliError, Errno::ECONNREFUSED
         | 
| 77 180 | 
             
                    raise if $!.message =~ /undefined class/
         | 
| 78 181 | 
             
                    if $VERBOSE
         | 
| @@ -81,9 +184,12 @@ module Rack | |
| 81 184 | 
             
                    end
         | 
| 82 185 | 
             
                    default
         | 
| 83 186 | 
             
                  ensure
         | 
| 84 | 
            -
                    @mutex.unlock if @mutex.locked?
         | 
| 187 | 
            +
                    @mutex.unlock if @mutex and @mutex.locked?
         | 
| 85 188 | 
             
                  end
         | 
| 86 189 |  | 
| 190 | 
            +
                  def ttl(expire_after)
         | 
| 191 | 
            +
                    expire_after.nil? ? 0 : expire_after + 1
         | 
| 192 | 
            +
                  end
         | 
| 87 193 | 
             
                end
         | 
| 88 194 | 
             
              end
         | 
| 89 195 | 
             
            end
         | 
    
        metadata
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: dalli
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 2.7. | 
| 4 | 
            +
              version: 2.7.7
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Peter M. Goldstein
         | 
| @@ -9,7 +9,7 @@ authors: | |
| 9 9 | 
             
            autorequire: 
         | 
| 10 10 | 
             
            bindir: bin
         | 
| 11 11 | 
             
            cert_chain: []
         | 
| 12 | 
            -
            date:  | 
| 12 | 
            +
            date: 2018-03-15 00:00:00.000000000 Z
         | 
| 13 13 | 
             
            dependencies:
         | 
| 14 14 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 15 15 | 
             
              name: minitest
         | 
| @@ -169,7 +169,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement | |
| 169 169 | 
             
                  version: '0'
         | 
| 170 170 | 
             
            requirements: []
         | 
| 171 171 | 
             
            rubyforge_project: 
         | 
| 172 | 
            -
            rubygems_version: 2. | 
| 172 | 
            +
            rubygems_version: 2.7.5
         | 
| 173 173 | 
             
            signing_key: 
         | 
| 174 174 | 
             
            specification_version: 4
         | 
| 175 175 | 
             
            summary: High performance memcached client for Ruby
         |