mudis 0.7.0 → 0.7.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.
- checksums.yaml +4 -4
 - data/README.md +861 -695
 - data/lib/mudis/version.rb +3 -3
 - data/lib/mudis.rb +521 -521
 - data/lib/mudis_client.rb +65 -68
 - data/lib/mudis_config.rb +25 -25
 - data/lib/mudis_proxy.rb +33 -0
 - data/lib/mudis_server.rb +81 -79
 - data/sig/mudis.rbs +56 -56
 - data/sig/mudis_client.rbs +23 -0
 - data/sig/mudis_config.rbs +10 -10
 - data/sig/mudis_server.rbs +7 -0
 - data/spec/eviction_spec.rb +29 -29
 - data/spec/guardrails_spec.rb +138 -138
 - data/spec/memory_guard_spec.rb +33 -33
 - data/spec/metrics_spec.rb +34 -34
 - data/spec/mudis_client_spec.rb +137 -0
 - data/spec/mudis_server_spec.rb +90 -0
 - data/spec/mudis_spec.rb +183 -183
 - data/spec/namespace_spec.rb +69 -69
 - data/spec/reset_spec.rb +31 -31
 - metadata +16 -4
 - data/lib/example_mudis_server_config.md +0 -39
 
    
        data/lib/mudis_client.rb
    CHANGED
    
    | 
         @@ -1,68 +1,65 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            # frozen_string_literal: true
         
     | 
| 
       2 
     | 
    
         
            -
             
     | 
| 
       3 
     | 
    
         
            -
            require " 
     | 
| 
       4 
     | 
    
         
            -
             
     | 
| 
       5 
     | 
    
         
            -
             
     | 
| 
       6 
     | 
    
         
            -
             
     | 
| 
       7 
     | 
    
         
            -
             
     | 
| 
       8 
     | 
    
         
            -
               
     | 
| 
       9 
     | 
    
         
            -
             
     | 
| 
       10 
     | 
    
         
            -
               
     | 
| 
       11 
     | 
    
         
            -
             
     | 
| 
       12 
     | 
    
         
            -
               
     | 
| 
       13 
     | 
    
         
            -
             
     | 
| 
       14 
     | 
    
         
            -
             
     | 
| 
       15 
     | 
    
         
            -
             
     | 
| 
       16 
     | 
    
         
            -
             
     | 
| 
       17 
     | 
    
         
            -
                     
     | 
| 
       18 
     | 
    
         
            -
                     
     | 
| 
       19 
     | 
    
         
            -
                    res 
     | 
| 
       20 
     | 
    
         
            -
                    
         
     | 
| 
       21 
     | 
    
         
            -
             
     | 
| 
       22 
     | 
    
         
            -
             
     | 
| 
       23 
     | 
    
         
            -
                rescue Errno::ENOENT
         
     | 
| 
       24 
     | 
    
         
            -
                  warn "[MudisClient] Socket missing; master likely not running MudisServer"
         
     | 
| 
       25 
     | 
    
         
            -
                  nil
         
     | 
| 
       26 
     | 
    
         
            -
             
     | 
| 
       27 
     | 
    
         
            -
             
     | 
| 
       28 
     | 
    
         
            -
             
     | 
| 
       29 
     | 
    
         
            -
               
     | 
| 
       30 
     | 
    
         
            -
             
     | 
| 
       31 
     | 
    
         
            -
               
     | 
| 
       32 
     | 
    
         
            -
             
     | 
| 
       33 
     | 
    
         
            -
               
     | 
| 
       34 
     | 
    
         
            -
             
     | 
| 
       35 
     | 
    
         
            -
               
     | 
| 
       36 
     | 
    
         
            -
             
     | 
| 
       37 
     | 
    
         
            -
               
     | 
| 
       38 
     | 
    
         
            -
             
     | 
| 
       39 
     | 
    
         
            -
               
     | 
| 
       40 
     | 
    
         
            -
             
     | 
| 
       41 
     | 
    
         
            -
               
     | 
| 
       42 
     | 
    
         
            -
             
     | 
| 
       43 
     | 
    
         
            -
               
     | 
| 
       44 
     | 
    
         
            -
             
     | 
| 
       45 
     | 
    
         
            -
               
     | 
| 
       46 
     | 
    
         
            -
             
     | 
| 
       47 
     | 
    
         
            -
             
     | 
| 
       48 
     | 
    
         
            -
             
     | 
| 
       49 
     | 
    
         
            -
                 
     | 
| 
       50 
     | 
    
         
            -
             
     | 
| 
       51 
     | 
    
         
            -
                new_val 
     | 
| 
       52 
     | 
    
         
            -
             
     | 
| 
       53 
     | 
    
         
            -
             
     | 
| 
       54 
     | 
    
         
            -
               
     | 
| 
       55 
     | 
    
         
            -
             
     | 
| 
       56 
     | 
    
         
            -
               
     | 
| 
       57 
     | 
    
         
            -
             
     | 
| 
       58 
     | 
    
         
            -
               
     | 
| 
       59 
     | 
    
         
            -
             
     | 
| 
       60 
     | 
    
         
            -
               
     | 
| 
       61 
     | 
    
         
            -
             
     | 
| 
       62 
     | 
    
         
            -
               
     | 
| 
       63 
     | 
    
         
            -
             
     | 
| 
       64 
     | 
    
         
            -
               
     | 
| 
       65 
     | 
    
         
            -
             
     | 
| 
       66 
     | 
    
         
            -
              end
         
     | 
| 
       67 
     | 
    
         
            -
             
     | 
| 
       68 
     | 
    
         
            -
            end
         
     | 
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require "socket"
         
     | 
| 
      
 4 
     | 
    
         
            +
            require "json"
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            # thread-safe client for communicating with the MudisServer via UNIX socket.
         
     | 
| 
      
 7 
     | 
    
         
            +
            class MudisClient
         
     | 
| 
      
 8 
     | 
    
         
            +
              SOCKET_PATH = "/tmp/mudis.sock"
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
              def initialize
         
     | 
| 
      
 11 
     | 
    
         
            +
                @mutex = Mutex.new
         
     | 
| 
      
 12 
     | 
    
         
            +
              end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
              def request(payload) # rubocop:disable Metrics/MethodLength
         
     | 
| 
      
 15 
     | 
    
         
            +
                @mutex.synchronize do
         
     | 
| 
      
 16 
     | 
    
         
            +
                  UNIXSocket.open(SOCKET_PATH) do |sock|
         
     | 
| 
      
 17 
     | 
    
         
            +
                    sock.puts(JSON.dump(payload))
         
     | 
| 
      
 18 
     | 
    
         
            +
                    response = sock.gets
         
     | 
| 
      
 19 
     | 
    
         
            +
                    res = JSON.parse(response, symbolize_names: true)
         
     | 
| 
      
 20 
     | 
    
         
            +
                    raise res[:error] unless res[:ok] # rubocop:disable Layout/EmptyLineAfterGuardClause
         
     | 
| 
      
 21 
     | 
    
         
            +
                    res[:value]
         
     | 
| 
      
 22 
     | 
    
         
            +
                  end
         
     | 
| 
      
 23 
     | 
    
         
            +
                rescue Errno::ENOENT
         
     | 
| 
      
 24 
     | 
    
         
            +
                  warn "[MudisClient] Socket missing; master likely not running MudisServer"
         
     | 
| 
      
 25 
     | 
    
         
            +
                  nil
         
     | 
| 
      
 26 
     | 
    
         
            +
                end
         
     | 
| 
      
 27 
     | 
    
         
            +
              end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
              def read(key, namespace: nil)
         
     | 
| 
      
 30 
     | 
    
         
            +
                request(cmd: "read", key: key, namespace: namespace)
         
     | 
| 
      
 31 
     | 
    
         
            +
              end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
              def write(key, value, expires_in: nil, namespace: nil)
         
     | 
| 
      
 34 
     | 
    
         
            +
                request(cmd: "write", key: key, value: value, ttl: expires_in, namespace: namespace)
         
     | 
| 
      
 35 
     | 
    
         
            +
              end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
              def delete(key, namespace: nil)
         
     | 
| 
      
 38 
     | 
    
         
            +
                request(cmd: "delete", key: key, namespace: namespace)
         
     | 
| 
      
 39 
     | 
    
         
            +
              end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
              def exists?(key, namespace: nil)
         
     | 
| 
      
 42 
     | 
    
         
            +
                request(cmd: "exists", key: key, namespace: namespace)
         
     | 
| 
      
 43 
     | 
    
         
            +
              end
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
              def fetch(key, expires_in: nil, namespace: nil)
         
     | 
| 
      
 46 
     | 
    
         
            +
                val = read(key, namespace: namespace)
         
     | 
| 
      
 47 
     | 
    
         
            +
                return val if val
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                new_val = yield
         
     | 
| 
      
 50 
     | 
    
         
            +
                write(key, new_val, expires_in: expires_in, namespace: namespace)
         
     | 
| 
      
 51 
     | 
    
         
            +
                new_val
         
     | 
| 
      
 52 
     | 
    
         
            +
              end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
              def metrics
         
     | 
| 
      
 55 
     | 
    
         
            +
                request(cmd: "metrics")
         
     | 
| 
      
 56 
     | 
    
         
            +
              end
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
              def reset_metrics!
         
     | 
| 
      
 59 
     | 
    
         
            +
                request(cmd: "reset_metrics")
         
     | 
| 
      
 60 
     | 
    
         
            +
              end
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
              def reset!
         
     | 
| 
      
 63 
     | 
    
         
            +
                request(cmd: "reset")
         
     | 
| 
      
 64 
     | 
    
         
            +
              end
         
     | 
| 
      
 65 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/mudis_config.rb
    CHANGED
    
    | 
         @@ -1,25 +1,25 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            # frozen_string_literal: true
         
     | 
| 
       2 
     | 
    
         
            -
             
     | 
| 
       3 
     | 
    
         
            -
            # MudisConfig holds all configuration values for Mudis,
         
     | 
| 
       4 
     | 
    
         
            -
            # and provides defaults that can be overridden via Mudis.configure.
         
     | 
| 
       5 
     | 
    
         
            -
            class MudisConfig
         
     | 
| 
       6 
     | 
    
         
            -
              attr_accessor :serializer,
         
     | 
| 
       7 
     | 
    
         
            -
                            :compress,
         
     | 
| 
       8 
     | 
    
         
            -
                            :max_value_bytes,
         
     | 
| 
       9 
     | 
    
         
            -
                            :hard_memory_limit,
         
     | 
| 
       10 
     | 
    
         
            -
                            :max_bytes,
         
     | 
| 
       11 
     | 
    
         
            -
                            :buckets,
         
     | 
| 
       12 
     | 
    
         
            -
                            :max_ttl,
         
     | 
| 
       13 
     | 
    
         
            -
                            :default_ttl
         
     | 
| 
       14 
     | 
    
         
            -
             
     | 
| 
       15 
     | 
    
         
            -
              def initialize
         
     | 
| 
       16 
     | 
    
         
            -
                @serializer = JSON                        # Default serialization strategy
         
     | 
| 
       17 
     | 
    
         
            -
                @compress = false                         # Whether to compress values with Zlib
         
     | 
| 
       18 
     | 
    
         
            -
                @max_value_bytes = nil                    # Max size per value (optional)
         
     | 
| 
       19 
     | 
    
         
            -
                @hard_memory_limit = false                # Enforce max_bytes as hard cap
         
     | 
| 
       20 
     | 
    
         
            -
                @max_bytes = 1_073_741_824                # 1 GB default max cache size
         
     | 
| 
       21 
     | 
    
         
            -
                @buckets = nil                            # use nil to signal fallback to ENV or default
         
     | 
| 
       22 
     | 
    
         
            -
                @max_ttl = nil                            # Max TTL for cache entries (optional)
         
     | 
| 
       23 
     | 
    
         
            -
                @default_ttl = nil                        # Default TTL for cache entries (optional)
         
     | 
| 
       24 
     | 
    
         
            -
              end
         
     | 
| 
       25 
     | 
    
         
            -
            end
         
     | 
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            # MudisConfig holds all configuration values for Mudis,
         
     | 
| 
      
 4 
     | 
    
         
            +
            # and provides defaults that can be overridden via Mudis.configure.
         
     | 
| 
      
 5 
     | 
    
         
            +
            class MudisConfig
         
     | 
| 
      
 6 
     | 
    
         
            +
              attr_accessor :serializer,
         
     | 
| 
      
 7 
     | 
    
         
            +
                            :compress,
         
     | 
| 
      
 8 
     | 
    
         
            +
                            :max_value_bytes,
         
     | 
| 
      
 9 
     | 
    
         
            +
                            :hard_memory_limit,
         
     | 
| 
      
 10 
     | 
    
         
            +
                            :max_bytes,
         
     | 
| 
      
 11 
     | 
    
         
            +
                            :buckets,
         
     | 
| 
      
 12 
     | 
    
         
            +
                            :max_ttl,
         
     | 
| 
      
 13 
     | 
    
         
            +
                            :default_ttl
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
              def initialize
         
     | 
| 
      
 16 
     | 
    
         
            +
                @serializer = JSON                        # Default serialization strategy
         
     | 
| 
      
 17 
     | 
    
         
            +
                @compress = false                         # Whether to compress values with Zlib
         
     | 
| 
      
 18 
     | 
    
         
            +
                @max_value_bytes = nil                    # Max size per value (optional)
         
     | 
| 
      
 19 
     | 
    
         
            +
                @hard_memory_limit = false                # Enforce max_bytes as hard cap
         
     | 
| 
      
 20 
     | 
    
         
            +
                @max_bytes = 1_073_741_824                # 1 GB default max cache size
         
     | 
| 
      
 21 
     | 
    
         
            +
                @buckets = nil                            # use nil to signal fallback to ENV or default
         
     | 
| 
      
 22 
     | 
    
         
            +
                @max_ttl = nil                            # Max TTL for cache entries (optional)
         
     | 
| 
      
 23 
     | 
    
         
            +
                @default_ttl = nil                        # Default TTL for cache entries (optional)
         
     | 
| 
      
 24 
     | 
    
         
            +
              end
         
     | 
| 
      
 25 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/mudis_proxy.rb
    ADDED
    
    | 
         @@ -0,0 +1,33 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            # Optional Mudis proxy layer for IPC mode.
         
     | 
| 
      
 4 
     | 
    
         
            +
            #
         
     | 
| 
      
 5 
     | 
    
         
            +
            # To enable:
         
     | 
| 
      
 6 
     | 
    
         
            +
            #   require "mudis_proxy"
         
     | 
| 
      
 7 
     | 
    
         
            +
            #
         
     | 
| 
      
 8 
     | 
    
         
            +
            # The proxy will forward calls to `$mudis` (an instance of MudisClient)
         
     | 
| 
      
 9 
     | 
    
         
            +
            # if it is defined, otherwise fallback to standard in-process behaviour.
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
            require_relative "mudis"
         
     | 
| 
      
 12 
     | 
    
         
            +
            require_relative "mudis_server"
         
     | 
| 
      
 13 
     | 
    
         
            +
            require_relative "mudis_client"
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
            if defined?(MudisServer)
         
     | 
| 
      
 16 
     | 
    
         
            +
              # In the master process — no proxy needed.
         
     | 
| 
      
 17 
     | 
    
         
            +
              return
         
     | 
| 
      
 18 
     | 
    
         
            +
            end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
            unless defined?(MudisClient)
         
     | 
| 
      
 21 
     | 
    
         
            +
              warn "[MudisProxy] MudisClient not loaded — proxy not activated"
         
     | 
| 
      
 22 
     | 
    
         
            +
              return
         
     | 
| 
      
 23 
     | 
    
         
            +
            end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
            class << Mudis
         
     | 
| 
      
 26 
     | 
    
         
            +
              def read(*a, **k) = $mudis.read(*a, **k) # rubocop:disable Naming/MethodParameterName,Style/GlobalVars
         
     | 
| 
      
 27 
     | 
    
         
            +
              def write(*a, **k) = $mudis.write(*a, **k) # rubocop:disable Naming/MethodParameterName,Style/GlobalVars
         
     | 
| 
      
 28 
     | 
    
         
            +
              def delete(*a, **k) = $mudis.delete(*a, **k) # rubocop:disable Naming/MethodParameterName,Style/GlobalVars
         
     | 
| 
      
 29 
     | 
    
         
            +
              def fetch(*a, **k, &b) = $mudis.fetch(*a, **k, &b) # rubocop:disable Naming/MethodParameterName,Style/GlobalVars
         
     | 
| 
      
 30 
     | 
    
         
            +
              def metrics = $mudis.metrics # rubocop:disable Style/GlobalVars
         
     | 
| 
      
 31 
     | 
    
         
            +
              def reset_metrics! = $mudis.reset_metrics! # rubocop:disable Style/GlobalVars
         
     | 
| 
      
 32 
     | 
    
         
            +
              def reset! = $mudis.reset! # rubocop:disable Style/GlobalVars
         
     | 
| 
      
 33 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/mudis_server.rb
    CHANGED
    
    | 
         @@ -1,79 +1,81 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            # frozen_string_literal: true
         
     | 
| 
       2 
     | 
    
         
            -
             
     | 
| 
       3 
     | 
    
         
            -
            require " 
     | 
| 
       4 
     | 
    
         
            -
             
     | 
| 
       5 
     | 
    
         
            -
             
     | 
| 
       6 
     | 
    
         
            -
             
     | 
| 
       7 
     | 
    
         
            -
             
     | 
| 
       8 
     | 
    
         
            -
             
     | 
| 
       9 
     | 
    
         
            -
               
     | 
| 
       10 
     | 
    
         
            -
             
     | 
| 
       11 
     | 
    
         
            -
             
     | 
| 
       12 
     | 
    
         
            -
             
     | 
| 
       13 
     | 
    
         
            -
                 
     | 
| 
       14 
     | 
    
         
            -
             
     | 
| 
       15 
     | 
    
         
            -
                 
     | 
| 
       16 
     | 
    
         
            -
             
     | 
| 
       17 
     | 
    
         
            -
                 
     | 
| 
       18 
     | 
    
         
            -
             
     | 
| 
       19 
     | 
    
         
            -
             
     | 
| 
       20 
     | 
    
         
            -
             
     | 
| 
       21 
     | 
    
         
            -
             
     | 
| 
       22 
     | 
    
         
            -
                     
     | 
| 
       23 
     | 
    
         
            -
             
     | 
| 
       24 
     | 
    
         
            -
             
     | 
| 
       25 
     | 
    
         
            -
             
     | 
| 
       26 
     | 
    
         
            -
             
     | 
| 
       27 
     | 
    
         
            -
               
     | 
| 
       28 
     | 
    
         
            -
             
     | 
| 
       29 
     | 
    
         
            -
             
     | 
| 
       30 
     | 
    
         
            -
             
     | 
| 
       31 
     | 
    
         
            -
                 
     | 
| 
       32 
     | 
    
         
            -
             
     | 
| 
       33 
     | 
    
         
            -
                 
     | 
| 
       34 
     | 
    
         
            -
                 
     | 
| 
       35 
     | 
    
         
            -
                 
     | 
| 
       36 
     | 
    
         
            -
                 
     | 
| 
       37 
     | 
    
         
            -
             
     | 
| 
       38 
     | 
    
         
            -
                 
     | 
| 
       39 
     | 
    
         
            -
             
     | 
| 
       40 
     | 
    
         
            -
             
     | 
| 
       41 
     | 
    
         
            -
             
     | 
| 
       42 
     | 
    
         
            -
             
     | 
| 
       43 
     | 
    
         
            -
             
     | 
| 
       44 
     | 
    
         
            -
             
     | 
| 
       45 
     | 
    
         
            -
             
     | 
| 
       46 
     | 
    
         
            -
             
     | 
| 
       47 
     | 
    
         
            -
             
     | 
| 
       48 
     | 
    
         
            -
             
     | 
| 
       49 
     | 
    
         
            -
             
     | 
| 
       50 
     | 
    
         
            -
             
     | 
| 
       51 
     | 
    
         
            -
             
     | 
| 
       52 
     | 
    
         
            -
             
     | 
| 
       53 
     | 
    
         
            -
             
     | 
| 
       54 
     | 
    
         
            -
             
     | 
| 
       55 
     | 
    
         
            -
             
     | 
| 
       56 
     | 
    
         
            -
             
     | 
| 
       57 
     | 
    
         
            -
             
     | 
| 
       58 
     | 
    
         
            -
             
     | 
| 
       59 
     | 
    
         
            -
             
     | 
| 
       60 
     | 
    
         
            -
             
     | 
| 
       61 
     | 
    
         
            -
             
     | 
| 
       62 
     | 
    
         
            -
             
     | 
| 
       63 
     | 
    
         
            -
             
     | 
| 
       64 
     | 
    
         
            -
             
     | 
| 
       65 
     | 
    
         
            -
                    
         
     | 
| 
       66 
     | 
    
         
            -
             
     | 
| 
       67 
     | 
    
         
            -
             
     | 
| 
       68 
     | 
    
         
            -
             
     | 
| 
       69 
     | 
    
         
            -
             
     | 
| 
       70 
     | 
    
         
            -
             
     | 
| 
       71 
     | 
    
         
            -
             
     | 
| 
       72 
     | 
    
         
            -
                   
     | 
| 
       73 
     | 
    
         
            -
             
     | 
| 
       74 
     | 
    
         
            -
                   
     | 
| 
       75 
     | 
    
         
            -
                 
     | 
| 
       76 
     | 
    
         
            -
                  sock. 
     | 
| 
       77 
     | 
    
         
            -
                 
     | 
| 
       78 
     | 
    
         
            -
             
     | 
| 
       79 
     | 
    
         
            -
            end
         
     | 
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require "socket"
         
     | 
| 
      
 4 
     | 
    
         
            +
            require "json"
         
     | 
| 
      
 5 
     | 
    
         
            +
            require_relative "mudis"
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            # Simple UNIX socket server for handling Mudis operations via IPC mode
         
     | 
| 
      
 8 
     | 
    
         
            +
            class MudisServer
         
     | 
| 
      
 9 
     | 
    
         
            +
              SOCKET_PATH = "/tmp/mudis.sock"
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
              def self.start! # rubocop:disable Metrics/MethodLength
         
     | 
| 
      
 12 
     | 
    
         
            +
                # Clean up old socket if it exists
         
     | 
| 
      
 13 
     | 
    
         
            +
                File.unlink(SOCKET_PATH) if File.exist?(SOCKET_PATH)
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                server = UNIXServer.new(SOCKET_PATH)
         
     | 
| 
      
 16 
     | 
    
         
            +
                server.listen(128)
         
     | 
| 
      
 17 
     | 
    
         
            +
                puts "[MudisServer] Listening on #{SOCKET_PATH}"
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                Thread.new do
         
     | 
| 
      
 20 
     | 
    
         
            +
                  loop do
         
     | 
| 
      
 21 
     | 
    
         
            +
                    client = server.accept
         
     | 
| 
      
 22 
     | 
    
         
            +
                    Thread.new(client) do |sock|
         
     | 
| 
      
 23 
     | 
    
         
            +
                      handle_client(sock)
         
     | 
| 
      
 24 
     | 
    
         
            +
                    end
         
     | 
| 
      
 25 
     | 
    
         
            +
                  end
         
     | 
| 
      
 26 
     | 
    
         
            +
                end
         
     | 
| 
      
 27 
     | 
    
         
            +
              end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
              def self.handle_client(sock) # rubocop:disable Metrics/CyclomaticComplexity,Metrics/AbcSize,Metrics/MethodLength
         
     | 
| 
      
 30 
     | 
    
         
            +
                request_line = sock.gets
         
     | 
| 
      
 31 
     | 
    
         
            +
                return unless request_line
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                req = JSON.parse(request_line, symbolize_names: true)
         
     | 
| 
      
 34 
     | 
    
         
            +
                cmd  = req[:cmd]
         
     | 
| 
      
 35 
     | 
    
         
            +
                key  = req[:key]
         
     | 
| 
      
 36 
     | 
    
         
            +
                ns   = req[:namespace]
         
     | 
| 
      
 37 
     | 
    
         
            +
                val  = req[:value]
         
     | 
| 
      
 38 
     | 
    
         
            +
                ttl  = req[:ttl]
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                begin
         
     | 
| 
      
 41 
     | 
    
         
            +
                  case cmd
         
     | 
| 
      
 42 
     | 
    
         
            +
                  when "read"
         
     | 
| 
      
 43 
     | 
    
         
            +
                    result = Mudis.read(key, namespace: ns)
         
     | 
| 
      
 44 
     | 
    
         
            +
                    sock.puts(JSON.dump({ ok: true, value: result }))
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                  when "write"
         
     | 
| 
      
 47 
     | 
    
         
            +
                    Mudis.write(key, val, expires_in: ttl, namespace: ns)
         
     | 
| 
      
 48 
     | 
    
         
            +
                    sock.puts(JSON.dump({ ok: true }))
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                  when "delete"
         
     | 
| 
      
 51 
     | 
    
         
            +
                    Mudis.delete(key, namespace: ns)
         
     | 
| 
      
 52 
     | 
    
         
            +
                    sock.puts(JSON.dump({ ok: true }))
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                  when "exists"
         
     | 
| 
      
 55 
     | 
    
         
            +
                    sock.puts(JSON.dump({ ok: true, value: Mudis.exists?(key, namespace: ns) }))
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                  when "fetch"
         
     | 
| 
      
 58 
     | 
    
         
            +
                    result = Mudis.fetch(key, expires_in: ttl, namespace: ns) { req[:fallback] }
         
     | 
| 
      
 59 
     | 
    
         
            +
                    sock.puts(JSON.dump({ ok: true, value: result }))
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
                  when "metrics"
         
     | 
| 
      
 62 
     | 
    
         
            +
                    sock.puts(JSON.dump({ ok: true, value: Mudis.metrics }))
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                  when "reset_metrics"
         
     | 
| 
      
 65 
     | 
    
         
            +
                    Mudis.reset_metrics!
         
     | 
| 
      
 66 
     | 
    
         
            +
                    sock.puts(JSON.dump({ ok: true }))
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                  when "reset"
         
     | 
| 
      
 69 
     | 
    
         
            +
                    Mudis.reset!
         
     | 
| 
      
 70 
     | 
    
         
            +
                    sock.puts(JSON.dump({ ok: true }))
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                  else
         
     | 
| 
      
 73 
     | 
    
         
            +
                    sock.puts(JSON.dump({ ok: false, error: "unknown command: #{cmd}" }))
         
     | 
| 
      
 74 
     | 
    
         
            +
                  end
         
     | 
| 
      
 75 
     | 
    
         
            +
                rescue StandardError => e
         
     | 
| 
      
 76 
     | 
    
         
            +
                  sock.puts(JSON.dump({ ok: false, error: e.message }))
         
     | 
| 
      
 77 
     | 
    
         
            +
                ensure
         
     | 
| 
      
 78 
     | 
    
         
            +
                  sock.close
         
     | 
| 
      
 79 
     | 
    
         
            +
                end
         
     | 
| 
      
 80 
     | 
    
         
            +
              end
         
     | 
| 
      
 81 
     | 
    
         
            +
            end
         
     | 
    
        data/sig/mudis.rbs
    CHANGED
    
    | 
         @@ -1,56 +1,56 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            class Mudis
         
     | 
| 
       2 
     | 
    
         
            -
              # Configuration
         
     | 
| 
       3 
     | 
    
         
            -
              class << self
         
     | 
| 
       4 
     | 
    
         
            -
                attr_accessor serializer : Object
         
     | 
| 
       5 
     | 
    
         
            -
                attr_accessor compress : bool
         
     | 
| 
       6 
     | 
    
         
            -
                attr_accessor hard_memory_limit : bool
         
     | 
| 
       7 
     | 
    
         
            -
                attr_reader max_bytes : Integer
         
     | 
| 
       8 
     | 
    
         
            -
                attr_reader max_value_bytes : Integer?
         
     | 
| 
       9 
     | 
    
         
            -
                attr_accessor max_ttl: Integer?
         
     | 
| 
       10 
     | 
    
         
            -
                attr_accessor default_ttl: Integer?
         
     | 
| 
       11 
     | 
    
         
            -
                
         
     | 
| 
       12 
     | 
    
         
            -
                def configure: () { (config: MudisConfig) -> void } -> void
         
     | 
| 
       13 
     | 
    
         
            -
                def config: () -> MudisConfig
         
     | 
| 
       14 
     | 
    
         
            -
                def apply_config!: () -> void
         
     | 
| 
       15 
     | 
    
         
            -
                def validate_config!: () -> void
         
     | 
| 
       16 
     | 
    
         
            -
             
     | 
| 
       17 
     | 
    
         
            -
                def buckets: () -> Integer
         
     | 
| 
       18 
     | 
    
         
            -
              end
         
     | 
| 
       19 
     | 
    
         
            -
             
     | 
| 
       20 
     | 
    
         
            -
              # Lifecycle
         
     | 
| 
       21 
     | 
    
         
            -
              def self.start_expiry_thread: (?interval: Integer) -> void
         
     | 
| 
       22 
     | 
    
         
            -
              def self.stop_expiry_thread: () -> void
         
     | 
| 
       23 
     | 
    
         
            -
             
     | 
| 
       24 
     | 
    
         
            -
              # Core operations
         
     | 
| 
       25 
     | 
    
         
            -
              def self.write: (String, untyped, ?expires_in: Integer, ?namespace: String) -> void
         
     | 
| 
       26 
     | 
    
         
            -
              def self.read: (String, ?namespace: String) -> untyped?
         
     | 
| 
       27 
     | 
    
         
            -
              def self.update: (String, ?namespace: String) { (untyped) -> untyped } -> void
         
     | 
| 
       28 
     | 
    
         
            -
              def self.delete: (String, ?namespace: String) -> void
         
     | 
| 
       29 
     | 
    
         
            -
              def self.exists?: (String, ?namespace: String) -> bool
         
     | 
| 
       30 
     | 
    
         
            -
             
     | 
| 
       31 
     | 
    
         
            -
              # DSL & Helpers
         
     | 
| 
       32 
     | 
    
         
            -
              def self.fetch: (
         
     | 
| 
       33 
     | 
    
         
            -
                String,
         
     | 
| 
       34 
     | 
    
         
            -
                ?expires_in: Integer,
         
     | 
| 
       35 
     | 
    
         
            -
                ?force: bool,
         
     | 
| 
       36 
     | 
    
         
            -
                ?namespace: String
         
     | 
| 
       37 
     | 
    
         
            -
              ) { () -> untyped } -> untyped
         
     | 
| 
       38 
     | 
    
         
            -
             
     | 
| 
       39 
     | 
    
         
            -
              def self.clear: (String, ?namespace: String) -> void
         
     | 
| 
       40 
     | 
    
         
            -
              def self.replace: (String, untyped, ?expires_in: Integer, ?namespace: String) -> void
         
     | 
| 
       41 
     | 
    
         
            -
              def self.inspect: (String, ?namespace: String) -> Hash[Symbol, untyped]?
         
     | 
| 
       42 
     | 
    
         
            -
              def self.keys: (?namespace: String) -> Array[String]
         
     | 
| 
       43 
     | 
    
         
            -
              def self.clear_namespace: (?namespace: String) -> void
         
     | 
| 
       44 
     | 
    
         
            -
             
     | 
| 
       45 
     | 
    
         
            -
              # Introspection & management
         
     | 
| 
       46 
     | 
    
         
            -
              def self.metrics: () -> Hash[Symbol, untyped]
         
     | 
| 
       47 
     | 
    
         
            -
              def self.cleanup_expired!: () -> void
         
     | 
| 
       48 
     | 
    
         
            -
              def self.all_keys: () -> Array[String]
         
     | 
| 
       49 
     | 
    
         
            -
              def self.current_memory_bytes: () -> Integer
         
     | 
| 
       50 
     | 
    
         
            -
              def self.max_memory_bytes: () -> Integer
         
     | 
| 
       51 
     | 
    
         
            -
              def self.least_touched: (?Integer) -> Array[[String, Integer]]
         
     | 
| 
       52 
     | 
    
         
            -
             
     | 
| 
       53 
     | 
    
         
            -
              # State reset
         
     | 
| 
       54 
     | 
    
         
            -
              def self.reset!: () -> void
         
     | 
| 
       55 
     | 
    
         
            -
              def self.reset_metrics!: () -> void
         
     | 
| 
       56 
     | 
    
         
            -
            end
         
     | 
| 
      
 1 
     | 
    
         
            +
            class Mudis
         
     | 
| 
      
 2 
     | 
    
         
            +
              # Configuration
         
     | 
| 
      
 3 
     | 
    
         
            +
              class << self
         
     | 
| 
      
 4 
     | 
    
         
            +
                attr_accessor serializer : Object
         
     | 
| 
      
 5 
     | 
    
         
            +
                attr_accessor compress : bool
         
     | 
| 
      
 6 
     | 
    
         
            +
                attr_accessor hard_memory_limit : bool
         
     | 
| 
      
 7 
     | 
    
         
            +
                attr_reader max_bytes : Integer
         
     | 
| 
      
 8 
     | 
    
         
            +
                attr_reader max_value_bytes : Integer?
         
     | 
| 
      
 9 
     | 
    
         
            +
                attr_accessor max_ttl: Integer?
         
     | 
| 
      
 10 
     | 
    
         
            +
                attr_accessor default_ttl: Integer?
         
     | 
| 
      
 11 
     | 
    
         
            +
                
         
     | 
| 
      
 12 
     | 
    
         
            +
                def configure: () { (config: MudisConfig) -> void } -> void
         
     | 
| 
      
 13 
     | 
    
         
            +
                def config: () -> MudisConfig
         
     | 
| 
      
 14 
     | 
    
         
            +
                def apply_config!: () -> void
         
     | 
| 
      
 15 
     | 
    
         
            +
                def validate_config!: () -> void
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                def buckets: () -> Integer
         
     | 
| 
      
 18 
     | 
    
         
            +
              end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
              # Lifecycle
         
     | 
| 
      
 21 
     | 
    
         
            +
              def self.start_expiry_thread: (?interval: Integer) -> void
         
     | 
| 
      
 22 
     | 
    
         
            +
              def self.stop_expiry_thread: () -> void
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
              # Core operations
         
     | 
| 
      
 25 
     | 
    
         
            +
              def self.write: (String, untyped, ?expires_in: Integer, ?namespace: String) -> void
         
     | 
| 
      
 26 
     | 
    
         
            +
              def self.read: (String, ?namespace: String) -> untyped?
         
     | 
| 
      
 27 
     | 
    
         
            +
              def self.update: (String, ?namespace: String) { (untyped) -> untyped } -> void
         
     | 
| 
      
 28 
     | 
    
         
            +
              def self.delete: (String, ?namespace: String) -> void
         
     | 
| 
      
 29 
     | 
    
         
            +
              def self.exists?: (String, ?namespace: String) -> bool
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
              # DSL & Helpers
         
     | 
| 
      
 32 
     | 
    
         
            +
              def self.fetch: (
         
     | 
| 
      
 33 
     | 
    
         
            +
                String,
         
     | 
| 
      
 34 
     | 
    
         
            +
                ?expires_in: Integer,
         
     | 
| 
      
 35 
     | 
    
         
            +
                ?force: bool,
         
     | 
| 
      
 36 
     | 
    
         
            +
                ?namespace: String
         
     | 
| 
      
 37 
     | 
    
         
            +
              ) { () -> untyped } -> untyped
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
              def self.clear: (String, ?namespace: String) -> void
         
     | 
| 
      
 40 
     | 
    
         
            +
              def self.replace: (String, untyped, ?expires_in: Integer, ?namespace: String) -> void
         
     | 
| 
      
 41 
     | 
    
         
            +
              def self.inspect: (String, ?namespace: String) -> Hash[Symbol, untyped]?
         
     | 
| 
      
 42 
     | 
    
         
            +
              def self.keys: (?namespace: String) -> Array[String]
         
     | 
| 
      
 43 
     | 
    
         
            +
              def self.clear_namespace: (?namespace: String) -> void
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
              # Introspection & management
         
     | 
| 
      
 46 
     | 
    
         
            +
              def self.metrics: () -> Hash[Symbol, untyped]
         
     | 
| 
      
 47 
     | 
    
         
            +
              def self.cleanup_expired!: () -> void
         
     | 
| 
      
 48 
     | 
    
         
            +
              def self.all_keys: () -> Array[String]
         
     | 
| 
      
 49 
     | 
    
         
            +
              def self.current_memory_bytes: () -> Integer
         
     | 
| 
      
 50 
     | 
    
         
            +
              def self.max_memory_bytes: () -> Integer
         
     | 
| 
      
 51 
     | 
    
         
            +
              def self.least_touched: (?Integer) -> Array[[String, Integer]]
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
              # State reset
         
     | 
| 
      
 54 
     | 
    
         
            +
              def self.reset!: () -> void
         
     | 
| 
      
 55 
     | 
    
         
            +
              def self.reset_metrics!: () -> void
         
     | 
| 
      
 56 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,23 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            class MudisClient
         
     | 
| 
      
 2 
     | 
    
         
            +
              SOCKET_PATH: String
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
              def initialize: () -> void
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
              def request: (payload: { cmd: String, key?: String, value?: untyped, ttl?: Integer?, namespace?: String? }) -> untyped
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
              def read: (key: String, namespace?: String?) -> untyped
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
              def write: (key: String, value: untyped, expires_in?: Integer?, namespace?: String?) -> untyped
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
              def delete: (key: String, namespace?: String?) -> untyped
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
              def exists?: (key: String, namespace?: String?) -> bool
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
              def fetch: (key: String, expires_in?: Integer?, namespace?: String?, &block: { () -> untyped }) -> untyped
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
              def metrics: () -> { reads: Integer, writes: Integer, deletes: Integer, exists: Integer }
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
              def reset_metrics!: () -> void
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
              def reset!: () -> void
         
     | 
| 
      
 23 
     | 
    
         
            +
            end
         
     | 
    
        data/sig/mudis_config.rbs
    CHANGED
    
    | 
         @@ -1,10 +1,10 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            class MudisConfig
         
     | 
| 
       2 
     | 
    
         
            -
              attr_accessor serializer: Object
         
     | 
| 
       3 
     | 
    
         
            -
              attr_accessor compress: bool
         
     | 
| 
       4 
     | 
    
         
            -
              attr_accessor max_value_bytes: Integer?
         
     | 
| 
       5 
     | 
    
         
            -
              attr_accessor hard_memory_limit: bool
         
     | 
| 
       6 
     | 
    
         
            -
              attr_accessor max_bytes: Integer
         
     | 
| 
       7 
     | 
    
         
            -
              attr_accessor max_ttl: Integer?
         
     | 
| 
       8 
     | 
    
         
            -
              attr_accessor default_ttl: Integer?
         
     | 
| 
       9 
     | 
    
         
            -
              attr_accessor buckets: Integer?
         
     | 
| 
       10 
     | 
    
         
            -
            end
         
     | 
| 
      
 1 
     | 
    
         
            +
            class MudisConfig
         
     | 
| 
      
 2 
     | 
    
         
            +
              attr_accessor serializer: Object
         
     | 
| 
      
 3 
     | 
    
         
            +
              attr_accessor compress: bool
         
     | 
| 
      
 4 
     | 
    
         
            +
              attr_accessor max_value_bytes: Integer?
         
     | 
| 
      
 5 
     | 
    
         
            +
              attr_accessor hard_memory_limit: bool
         
     | 
| 
      
 6 
     | 
    
         
            +
              attr_accessor max_bytes: Integer
         
     | 
| 
      
 7 
     | 
    
         
            +
              attr_accessor max_ttl: Integer?
         
     | 
| 
      
 8 
     | 
    
         
            +
              attr_accessor default_ttl: Integer?
         
     | 
| 
      
 9 
     | 
    
         
            +
              attr_accessor buckets: Integer?
         
     | 
| 
      
 10 
     | 
    
         
            +
            end
         
     | 
    
        data/spec/eviction_spec.rb
    CHANGED
    
    | 
         @@ -1,29 +1,29 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            # frozen_string_literal: true
         
     | 
| 
       2 
     | 
    
         
            -
             
     | 
| 
       3 
     | 
    
         
            -
            require "spec_helper"
         
     | 
| 
       4 
     | 
    
         
            -
             
     | 
| 
       5 
     | 
    
         
            -
            RSpec.describe "Mudis LRU Eviction" do
         
     | 
| 
       6 
     | 
    
         
            -
              before do
         
     | 
| 
       7 
     | 
    
         
            -
                Mudis.reset!
         
     | 
| 
       8 
     | 
    
         
            -
                Mudis.stop_expiry_thread
         
     | 
| 
       9 
     | 
    
         
            -
             
     | 
| 
       10 
     | 
    
         
            -
                Mudis.instance_variable_set(:@buckets, 1)
         
     | 
| 
       11 
     | 
    
         
            -
                Mudis.instance_variable_set(:@stores, [{}])
         
     | 
| 
       12 
     | 
    
         
            -
                Mudis.instance_variable_set(:@mutexes, [Mutex.new])
         
     | 
| 
       13 
     | 
    
         
            -
                Mudis.instance_variable_set(:@lru_heads, [nil])
         
     | 
| 
       14 
     | 
    
         
            -
                Mudis.instance_variable_set(:@lru_tails, [nil])
         
     | 
| 
       15 
     | 
    
         
            -
                Mudis.instance_variable_set(:@lru_nodes, [{}])
         
     | 
| 
       16 
     | 
    
         
            -
                Mudis.instance_variable_set(:@current_bytes, [0])
         
     | 
| 
       17 
     | 
    
         
            -
                Mudis.hard_memory_limit = false
         
     | 
| 
       18 
     | 
    
         
            -
                Mudis.instance_variable_set(:@threshold_bytes, 60)
         
     | 
| 
       19 
     | 
    
         
            -
                Mudis.max_value_bytes = 100
         
     | 
| 
       20 
     | 
    
         
            -
              end
         
     | 
| 
       21 
     | 
    
         
            -
             
     | 
| 
       22 
     | 
    
         
            -
              it "evicts old entries when size limit is reached" do
         
     | 
| 
       23 
     | 
    
         
            -
                Mudis.write("a", "a" * 50)
         
     | 
| 
       24 
     | 
    
         
            -
                Mudis.write("b", "b" * 50)
         
     | 
| 
       25 
     | 
    
         
            -
             
     | 
| 
       26 
     | 
    
         
            -
                expect(Mudis.read("a")).to be_nil
         
     | 
| 
       27 
     | 
    
         
            -
                expect(Mudis.read("b")).not_to be_nil
         
     | 
| 
       28 
     | 
    
         
            -
              end
         
     | 
| 
       29 
     | 
    
         
            -
            end
         
     | 
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require "spec_helper"
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            RSpec.describe "Mudis LRU Eviction" do
         
     | 
| 
      
 6 
     | 
    
         
            +
              before do
         
     | 
| 
      
 7 
     | 
    
         
            +
                Mudis.reset!
         
     | 
| 
      
 8 
     | 
    
         
            +
                Mudis.stop_expiry_thread
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                Mudis.instance_variable_set(:@buckets, 1)
         
     | 
| 
      
 11 
     | 
    
         
            +
                Mudis.instance_variable_set(:@stores, [{}])
         
     | 
| 
      
 12 
     | 
    
         
            +
                Mudis.instance_variable_set(:@mutexes, [Mutex.new])
         
     | 
| 
      
 13 
     | 
    
         
            +
                Mudis.instance_variable_set(:@lru_heads, [nil])
         
     | 
| 
      
 14 
     | 
    
         
            +
                Mudis.instance_variable_set(:@lru_tails, [nil])
         
     | 
| 
      
 15 
     | 
    
         
            +
                Mudis.instance_variable_set(:@lru_nodes, [{}])
         
     | 
| 
      
 16 
     | 
    
         
            +
                Mudis.instance_variable_set(:@current_bytes, [0])
         
     | 
| 
      
 17 
     | 
    
         
            +
                Mudis.hard_memory_limit = false
         
     | 
| 
      
 18 
     | 
    
         
            +
                Mudis.instance_variable_set(:@threshold_bytes, 60)
         
     | 
| 
      
 19 
     | 
    
         
            +
                Mudis.max_value_bytes = 100
         
     | 
| 
      
 20 
     | 
    
         
            +
              end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
              it "evicts old entries when size limit is reached" do
         
     | 
| 
      
 23 
     | 
    
         
            +
                Mudis.write("a", "a" * 50)
         
     | 
| 
      
 24 
     | 
    
         
            +
                Mudis.write("b", "b" * 50)
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                expect(Mudis.read("a")).to be_nil
         
     | 
| 
      
 27 
     | 
    
         
            +
                expect(Mudis.read("b")).not_to be_nil
         
     | 
| 
      
 28 
     | 
    
         
            +
              end
         
     | 
| 
      
 29 
     | 
    
         
            +
            end
         
     |