semian 0.8.9 → 0.9.0
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.
- checksums.yaml +4 -4
- data/ext/semian/resource.c +6 -0
- data/ext/semian/resource.h +4 -0
- data/ext/semian/semian.c +1 -0
- data/lib/semian.rb +33 -4
- data/lib/semian/circuit_breaker.rb +5 -0
- data/lib/semian/instrumentable.rb +6 -0
- data/lib/semian/lru_hash.rb +160 -0
- data/lib/semian/protected_resource.rb +6 -0
- data/lib/semian/resource.rb +5 -1
- data/lib/semian/unprotected_resource.rb +6 -0
- data/lib/semian/version.rb +1 -1
- metadata +31 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 426aa6c4392e70e79cbf478827dcdccbc1a468f0f19b2a5e42b885584a389b00
         | 
| 4 | 
            +
              data.tar.gz: df5c7c0ca8e80879c1d1d1a5cc46400c2de841e5809ea2396cc91941f222fa05
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: da18cc34e1c36d18904cbb584a9c7a03fd32afa976f27a6ed227b62e4ebb64065ea38a36ac951d0cade1c72b115143c3b24d6299b28de0927c78e52379205f17
         | 
| 7 | 
            +
              data.tar.gz: 1e4dd82c16f7717022e65d4c722a28b4bd06490cbf347dfbe0f9fafd5652b615162f884a6dea04ad5ec5314986f7b0aad3abda8205cf2cc34b434416b001d0ed
         | 
    
        data/ext/semian/resource.c
    CHANGED
    
    
    
        data/ext/semian/resource.h
    CHANGED
    
    
    
        data/ext/semian/semian.c
    CHANGED
    
    | @@ -52,6 +52,7 @@ void Init_semian() | |
| 52 52 | 
             
              rb_define_method(cResource, "destroy", semian_resource_destroy, 0);
         | 
| 53 53 | 
             
              rb_define_method(cResource, "reset_registered_workers!", semian_resource_reset_workers, 0);
         | 
| 54 54 | 
             
              rb_define_method(cResource, "unregister_worker", semian_resource_unregister_worker, 0);
         | 
| 55 | 
            +
              rb_define_method(cResource, "in_use?", semian_resource_in_use, 0);
         | 
| 55 56 |  | 
| 56 57 | 
             
              id_wait_time = rb_intern("wait_time");
         | 
| 57 58 | 
             
              id_timeout = rb_intern("timeout");
         | 
    
        data/lib/semian.rb
    CHANGED
    
    | @@ -13,6 +13,7 @@ require 'semian/unprotected_resource' | |
| 13 13 | 
             
            require 'semian/simple_sliding_window'
         | 
| 14 14 | 
             
            require 'semian/simple_integer'
         | 
| 15 15 | 
             
            require 'semian/simple_state'
         | 
| 16 | 
            +
            require 'semian/lru_hash'
         | 
| 16 17 |  | 
| 17 18 | 
             
            #
         | 
| 18 19 | 
             
            # === Overview
         | 
| @@ -89,6 +90,10 @@ module Semian | |
| 89 90 | 
             
              InternalError = Class.new(BaseError)
         | 
| 90 91 | 
             
              OpenCircuitError = Class.new(BaseError)
         | 
| 91 92 |  | 
| 93 | 
            +
              attr_accessor :maximum_lru_size, :minimum_lru_time
         | 
| 94 | 
            +
              self.maximum_lru_size = 500
         | 
| 95 | 
            +
              self.minimum_lru_time = 300
         | 
| 96 | 
            +
             | 
| 92 97 | 
             
              def issue_disabled_semaphores_warning
         | 
| 93 98 | 
             
                return if defined?(@warning_issued)
         | 
| 94 99 | 
             
                @warning_issued = true
         | 
| @@ -216,7 +221,7 @@ module Semian | |
| 216 221 |  | 
| 217 222 | 
             
              # Retrieves a hash of all registered resources.
         | 
| 218 223 | 
             
              def resources
         | 
| 219 | 
            -
                @resources ||=  | 
| 224 | 
            +
                @resources ||= LRUHash.new
         | 
| 220 225 | 
             
              end
         | 
| 221 226 |  | 
| 222 227 | 
             
              # Retrieves a hash of all registered resource consumers.
         | 
| @@ -226,7 +231,16 @@ module Semian | |
| 226 231 |  | 
| 227 232 | 
             
              def reset!
         | 
| 228 233 | 
             
                @consumers = {}
         | 
| 229 | 
            -
                @resources =  | 
| 234 | 
            +
                @resources = LRUHash.new
         | 
| 235 | 
            +
              end
         | 
| 236 | 
            +
             | 
| 237 | 
            +
              def thread_safe?
         | 
| 238 | 
            +
                return @thread_safe if defined?(@thread_safe)
         | 
| 239 | 
            +
                @thread_safe = true
         | 
| 240 | 
            +
              end
         | 
| 241 | 
            +
             | 
| 242 | 
            +
              def thread_safe=(thread_safe)
         | 
| 243 | 
            +
                @thread_safe = thread_safe
         | 
| 230 244 | 
             
              end
         | 
| 231 245 |  | 
| 232 246 | 
             
              private
         | 
| @@ -235,7 +249,6 @@ module Semian | |
| 235 249 | 
             
                circuit_breaker = options.fetch(:circuit_breaker, true)
         | 
| 236 250 | 
             
                return unless circuit_breaker
         | 
| 237 251 | 
             
                require_keys!([:success_threshold, :error_threshold, :error_timeout], options)
         | 
| 238 | 
            -
                implementation = options[:thread_safety_disabled] ? ::Semian::Simple : ::Semian::ThreadSafe
         | 
| 239 252 |  | 
| 240 253 | 
             
                exceptions = options[:exceptions] || []
         | 
| 241 254 | 
             
                CircuitBreaker.new(
         | 
| @@ -245,10 +258,26 @@ module Semian | |
| 245 258 | 
             
                  error_timeout: options[:error_timeout],
         | 
| 246 259 | 
             
                  exceptions: Array(exceptions) + [::Semian::BaseError],
         | 
| 247 260 | 
             
                  half_open_resource_timeout: options[:half_open_resource_timeout],
         | 
| 248 | 
            -
                  implementation: implementation,
         | 
| 261 | 
            +
                  implementation: implementation(**options),
         | 
| 249 262 | 
             
                )
         | 
| 250 263 | 
             
              end
         | 
| 251 264 |  | 
| 265 | 
            +
              def implementation(**options)
         | 
| 266 | 
            +
                # thread_safety_disabled will be replaced by a global setting
         | 
| 267 | 
            +
                # Semian is thread safe by default. It is possible
         | 
| 268 | 
            +
                # to modify the value by using Semian.thread_safe=
         | 
| 269 | 
            +
                unless options[:thread_safety_disabled].nil?
         | 
| 270 | 
            +
                  logger.info(
         | 
| 271 | 
            +
                    "NOTE: thread_safety_disabled will be replaced by a global setting" \
         | 
| 272 | 
            +
                    "Semian is thread safe by default. It is possible" \
         | 
| 273 | 
            +
                    "to modify the value by using Semian.thread_safe=",
         | 
| 274 | 
            +
                  )
         | 
| 275 | 
            +
                end
         | 
| 276 | 
            +
             | 
| 277 | 
            +
                thread_safe = options[:thread_safety_disabled].nil? ? Semian.thread_safe? : !options[:thread_safety_disabled]
         | 
| 278 | 
            +
                thread_safe ? ::Semian::ThreadSafe : ::Semian::Simple
         | 
| 279 | 
            +
              end
         | 
| 280 | 
            +
             | 
| 252 281 | 
             
              def create_bulkhead(name, **options)
         | 
| 253 282 | 
             
                bulkhead = options.fetch(:bulkhead, true)
         | 
| 254 283 | 
             
                return unless bulkhead
         | 
| @@ -9,6 +9,12 @@ module Semian | |
| 9 9 | 
             
                  subscribers.delete(name)
         | 
| 10 10 | 
             
                end
         | 
| 11 11 |  | 
| 12 | 
            +
                # Args:
         | 
| 13 | 
            +
                #   event (string)
         | 
| 14 | 
            +
                #   resource (Object)
         | 
| 15 | 
            +
                #   scope (string)
         | 
| 16 | 
            +
                #   adapter (string)
         | 
| 17 | 
            +
                #   payload (optional)
         | 
| 12 18 | 
             
                def notify(*args)
         | 
| 13 19 | 
             
                  subscribers.values.each { |subscriber| subscriber.call(*args) }
         | 
| 14 20 | 
             
                end
         | 
| @@ -0,0 +1,160 @@ | |
| 1 | 
            +
            class LRUHash
         | 
| 2 | 
            +
              # This LRU (Least Recently Used) hash will allow
         | 
| 3 | 
            +
              # the cleaning of resources as time goes on.
         | 
| 4 | 
            +
              # The goal is to remove the least recently used resources
         | 
| 5 | 
            +
              # everytime we set a new resource. A default window of
         | 
| 6 | 
            +
              # 5 minutes will allow empty item to stay in the hash
         | 
| 7 | 
            +
              # for a maximum of 5 minutes
         | 
| 8 | 
            +
              extend Forwardable
         | 
| 9 | 
            +
              def_delegators :@table, :size, :count, :empty?, :values
         | 
| 10 | 
            +
              attr_reader :table
         | 
| 11 | 
            +
             | 
| 12 | 
            +
              class NoopMutex
         | 
| 13 | 
            +
                def synchronize(*)
         | 
| 14 | 
            +
                  yield
         | 
| 15 | 
            +
                end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                def try_lock
         | 
| 18 | 
            +
                  true
         | 
| 19 | 
            +
                end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                def unlock
         | 
| 22 | 
            +
                  true
         | 
| 23 | 
            +
                end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                def locked?
         | 
| 26 | 
            +
                  true
         | 
| 27 | 
            +
                end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                def owned?
         | 
| 30 | 
            +
                  true
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
              end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
              def keys
         | 
| 35 | 
            +
                @lock.synchronize { @table.keys }
         | 
| 36 | 
            +
              end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
              def clear
         | 
| 39 | 
            +
                @lock.synchronize { @table.clear }
         | 
| 40 | 
            +
              end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
              # Create an LRU hash
         | 
| 43 | 
            +
              #
         | 
| 44 | 
            +
              # Arguments:
         | 
| 45 | 
            +
              #   +max_size+ The maximum size of the table
         | 
| 46 | 
            +
              #   +min_time+ The minimum time a resource can live in the cache
         | 
| 47 | 
            +
              #
         | 
| 48 | 
            +
              # Note:
         | 
| 49 | 
            +
              #   The +min_time+ is a stronger guarantee than +max_size+. That is, if there are
         | 
| 50 | 
            +
              #   more than +max_size+ entries in the cache, but they've all been updated more
         | 
| 51 | 
            +
              #   recently than +min_time+, the garbage collection will not remove them and the
         | 
| 52 | 
            +
              #   cache can grow without bound. This usually means that you have many active
         | 
| 53 | 
            +
              #   circuits to disparate endpoints (or your circuit names are bad).
         | 
| 54 | 
            +
              #   If the max_size is 0, the garbage collection will be very aggressive and
         | 
| 55 | 
            +
              #   potentially computationally expensive.
         | 
| 56 | 
            +
              def initialize(max_size: Semian.maximum_lru_size, min_time: Semian.minimum_lru_time)
         | 
| 57 | 
            +
                @max_size = max_size
         | 
| 58 | 
            +
                @min_time = min_time
         | 
| 59 | 
            +
                @table = {}
         | 
| 60 | 
            +
                @lock =
         | 
| 61 | 
            +
                  if Semian.thread_safe?
         | 
| 62 | 
            +
                    Mutex.new
         | 
| 63 | 
            +
                  else
         | 
| 64 | 
            +
                    NoopMutex.new
         | 
| 65 | 
            +
                  end
         | 
| 66 | 
            +
              end
         | 
| 67 | 
            +
             | 
| 68 | 
            +
              def set(key, resource)
         | 
| 69 | 
            +
                @lock.synchronize do
         | 
| 70 | 
            +
                  @table.delete(key)
         | 
| 71 | 
            +
                  @table[key] = resource
         | 
| 72 | 
            +
                  resource.updated_at = Time.now
         | 
| 73 | 
            +
                end
         | 
| 74 | 
            +
                clear_unused_resources if @table.length > @max_size
         | 
| 75 | 
            +
              end
         | 
| 76 | 
            +
             | 
| 77 | 
            +
              # This method uses the property that "Hashes enumerate their values in the
         | 
| 78 | 
            +
              # order that the corresponding keys were inserted." Deleting a key and
         | 
| 79 | 
            +
              # re-inserting it effectively moves it to the front of the cache.
         | 
| 80 | 
            +
              # Update the `updated_at` field so we can use it later do decide if the
         | 
| 81 | 
            +
              # resource is "in use".
         | 
| 82 | 
            +
              def get(key)
         | 
| 83 | 
            +
                @lock.synchronize do
         | 
| 84 | 
            +
                  found = @table.delete(key)
         | 
| 85 | 
            +
                  if found
         | 
| 86 | 
            +
                    @table[key] = found
         | 
| 87 | 
            +
                    found.updated_at = Time.now
         | 
| 88 | 
            +
                  end
         | 
| 89 | 
            +
                  found
         | 
| 90 | 
            +
                end
         | 
| 91 | 
            +
              end
         | 
| 92 | 
            +
             | 
| 93 | 
            +
              def delete(key)
         | 
| 94 | 
            +
                @lock.synchronize do
         | 
| 95 | 
            +
                  @table.delete(key)
         | 
| 96 | 
            +
                end
         | 
| 97 | 
            +
              end
         | 
| 98 | 
            +
             | 
| 99 | 
            +
              def []=(key, resource)
         | 
| 100 | 
            +
                set(key, resource)
         | 
| 101 | 
            +
              end
         | 
| 102 | 
            +
             | 
| 103 | 
            +
              def [](key)
         | 
| 104 | 
            +
                get(key)
         | 
| 105 | 
            +
              end
         | 
| 106 | 
            +
             | 
| 107 | 
            +
              private
         | 
| 108 | 
            +
             | 
| 109 | 
            +
              def clear_unused_resources
         | 
| 110 | 
            +
                payload = {
         | 
| 111 | 
            +
                    size: @table.size,
         | 
| 112 | 
            +
                    examined: 0,
         | 
| 113 | 
            +
                    cleared: 0,
         | 
| 114 | 
            +
                    elapsed: nil,
         | 
| 115 | 
            +
                }
         | 
| 116 | 
            +
                timer_start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
         | 
| 117 | 
            +
             | 
| 118 | 
            +
                ran = try_synchronize do
         | 
| 119 | 
            +
                  # Clears resources that have not been used in the last 5 minutes.
         | 
| 120 | 
            +
             | 
| 121 | 
            +
                  stop_time = Time.now - @min_time # Don't process resources updated after this time
         | 
| 122 | 
            +
                  @table.each do |_, resource|
         | 
| 123 | 
            +
                    payload[:examined] += 1
         | 
| 124 | 
            +
             | 
| 125 | 
            +
                    # The update times of the resources in the LRU are monotonically increasing,
         | 
| 126 | 
            +
                    # time, so we can stop looking once we find the first resource with an
         | 
| 127 | 
            +
                    # update time after the stop_time.
         | 
| 128 | 
            +
                    break if resource.updated_at > stop_time
         | 
| 129 | 
            +
             | 
| 130 | 
            +
                    next if resource.in_use?
         | 
| 131 | 
            +
             | 
| 132 | 
            +
                    resource = @table.delete(resource.name)
         | 
| 133 | 
            +
                    if resource
         | 
| 134 | 
            +
                      payload[:cleared] += 1
         | 
| 135 | 
            +
                      resource.destroy
         | 
| 136 | 
            +
                    end
         | 
| 137 | 
            +
                  end
         | 
| 138 | 
            +
                end
         | 
| 139 | 
            +
             | 
| 140 | 
            +
                if ran
         | 
| 141 | 
            +
                  payload[:elapsed] = Process.clock_gettime(Process::CLOCK_MONOTONIC) - timer_start
         | 
| 142 | 
            +
                  Semian.notify(:lru_hash_gc, self, nil, nil, payload)
         | 
| 143 | 
            +
                end
         | 
| 144 | 
            +
              end
         | 
| 145 | 
            +
             | 
| 146 | 
            +
              EXCEPTION_NEVER = {Exception => :never}.freeze
         | 
| 147 | 
            +
              EXCEPTION_IMMEDIATE = {Exception => :immediate}.freeze
         | 
| 148 | 
            +
              private_constant :EXCEPTION_NEVER
         | 
| 149 | 
            +
              private_constant :EXCEPTION_IMMEDIATE
         | 
| 150 | 
            +
             | 
| 151 | 
            +
              def try_synchronize
         | 
| 152 | 
            +
                Thread.handle_interrupt(EXCEPTION_NEVER) do
         | 
| 153 | 
            +
                  return false unless @lock.try_lock
         | 
| 154 | 
            +
                  Thread.handle_interrupt(EXCEPTION_IMMEDIATE) { yield }
         | 
| 155 | 
            +
                  true
         | 
| 156 | 
            +
                ensure
         | 
| 157 | 
            +
                  @lock.unlock if @lock.owned?
         | 
| 158 | 
            +
                end
         | 
| 159 | 
            +
              end
         | 
| 160 | 
            +
            end
         | 
| @@ -7,11 +7,13 @@ module Semian | |
| 7 7 | 
             
                               :open?, :closed?, :half_open?
         | 
| 8 8 |  | 
| 9 9 | 
             
                attr_reader :bulkhead, :circuit_breaker, :name
         | 
| 10 | 
            +
                attr_accessor :updated_at
         | 
| 10 11 |  | 
| 11 12 | 
             
                def initialize(name, bulkhead, circuit_breaker)
         | 
| 12 13 | 
             
                  @name = name
         | 
| 13 14 | 
             
                  @bulkhead = bulkhead
         | 
| 14 15 | 
             
                  @circuit_breaker = circuit_breaker
         | 
| 16 | 
            +
                  @updated_at = Time.now
         | 
| 15 17 | 
             
                end
         | 
| 16 18 |  | 
| 17 19 | 
             
                def destroy
         | 
| @@ -28,6 +30,10 @@ module Semian | |
| 28 30 | 
             
                  end
         | 
| 29 31 | 
             
                end
         | 
| 30 32 |  | 
| 33 | 
            +
                def in_use?
         | 
| 34 | 
            +
                  circuit_breaker&.in_use? || bulkhead&.in_use?
         | 
| 35 | 
            +
                end
         | 
| 36 | 
            +
             | 
| 31 37 | 
             
                private
         | 
| 32 38 |  | 
| 33 39 | 
             
                def acquire_circuit_breaker(scope, adapter, resource)
         | 
    
        data/lib/semian/resource.rb
    CHANGED
    
    | @@ -5,7 +5,7 @@ module Semian | |
| 5 5 | 
             
                class << Semian::Resource
         | 
| 6 6 | 
             
                  # Ensure that there can only be one resource of a given type
         | 
| 7 7 | 
             
                  def instance(*args)
         | 
| 8 | 
            -
                    Semian.resources[args.first] ||= new(*args)
         | 
| 8 | 
            +
                    Semian.resources[args.first] ||= ProtectedResource.new(args.first, new(*args), nil)
         | 
| 9 9 | 
             
                  end
         | 
| 10 10 | 
             
                end
         | 
| 11 11 |  | 
| @@ -51,5 +51,9 @@ module Semian | |
| 51 51 | 
             
                def key
         | 
| 52 52 | 
             
                  '0x00000000'
         | 
| 53 53 | 
             
                end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                def in_use?
         | 
| 56 | 
            +
                  false
         | 
| 57 | 
            +
                end
         | 
| 54 58 | 
             
              end
         | 
| 55 59 | 
             
            end
         | 
| @@ -3,9 +3,11 @@ module Semian | |
| 3 3 | 
             
              # the semian configuration of an `Adapter` is missing or explicitly disabled
         | 
| 4 4 | 
             
              class UnprotectedResource
         | 
| 5 5 | 
             
                attr_reader :name
         | 
| 6 | 
            +
                attr_accessor :updated_at
         | 
| 6 7 |  | 
| 7 8 | 
             
                def initialize(name)
         | 
| 8 9 | 
             
                  @name = name
         | 
| 10 | 
            +
                  @updated_at = Time.now
         | 
| 9 11 | 
             
                end
         | 
| 10 12 |  | 
| 11 13 | 
             
                def registered_workers
         | 
| @@ -63,5 +65,9 @@ module Semian | |
| 63 65 | 
             
                def circuit_breaker
         | 
| 64 66 | 
             
                  nil
         | 
| 65 67 | 
             
                end
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                def in_use?
         | 
| 70 | 
            +
                  true
         | 
| 71 | 
            +
                end
         | 
| 66 72 | 
             
              end
         | 
| 67 73 | 
             
            end
         | 
    
        data/lib/semian/version.rb
    CHANGED
    
    
    
        metadata
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: semian
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0. | 
| 4 | 
            +
              version: 0.9.0
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Scott Francis
         | 
| @@ -10,7 +10,7 @@ authors: | |
| 10 10 | 
             
            autorequire: 
         | 
| 11 11 | 
             
            bindir: bin
         | 
| 12 12 | 
             
            cert_chain: []
         | 
| 13 | 
            -
            date: 2019-09- | 
| 13 | 
            +
            date: 2019-09-04 00:00:00.000000000 Z
         | 
| 14 14 | 
             
            dependencies:
         | 
| 15 15 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 16 16 | 
             
              name: rake-compiler
         | 
| @@ -166,6 +166,34 @@ dependencies: | |
| 166 166 | 
             
                - - ">="
         | 
| 167 167 | 
             
                  - !ruby/object:Gem::Version
         | 
| 168 168 | 
             
                    version: '0'
         | 
| 169 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 170 | 
            +
              name: memory_profiler
         | 
| 171 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 172 | 
            +
                requirements:
         | 
| 173 | 
            +
                - - ">="
         | 
| 174 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 175 | 
            +
                    version: '0'
         | 
| 176 | 
            +
              type: :development
         | 
| 177 | 
            +
              prerelease: false
         | 
| 178 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 179 | 
            +
                requirements:
         | 
| 180 | 
            +
                - - ">="
         | 
| 181 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 182 | 
            +
                    version: '0'
         | 
| 183 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 184 | 
            +
              name: benchmark-memory
         | 
| 185 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 186 | 
            +
                requirements:
         | 
| 187 | 
            +
                - - ">="
         | 
| 188 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 189 | 
            +
                    version: '0'
         | 
| 190 | 
            +
              type: :development
         | 
| 191 | 
            +
              prerelease: false
         | 
| 192 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 193 | 
            +
                requirements:
         | 
| 194 | 
            +
                - - ">="
         | 
| 195 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 196 | 
            +
                    version: '0'
         | 
| 169 197 | 
             
            description: |2
         | 
| 170 198 | 
             
                  A Ruby C extention that is used to control access to shared resources
         | 
| 171 199 | 
             
                  across process boundaries with SysV semaphores.
         | 
| @@ -190,6 +218,7 @@ files: | |
| 190 218 | 
             
            - lib/semian/circuit_breaker.rb
         | 
| 191 219 | 
             
            - lib/semian/grpc.rb
         | 
| 192 220 | 
             
            - lib/semian/instrumentable.rb
         | 
| 221 | 
            +
            - lib/semian/lru_hash.rb
         | 
| 193 222 | 
             
            - lib/semian/mysql2.rb
         | 
| 194 223 | 
             
            - lib/semian/net_http.rb
         | 
| 195 224 | 
             
            - lib/semian/platform.rb
         |