mudis 0.8.0 → 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.
data/lib/mudis_client.rb CHANGED
@@ -1,65 +1,107 @@
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
1
+ # frozen_string_literal: true
2
+
3
+ require "socket"
4
+ require "json"
5
+ require_relative "mudis_ipc_config"
6
+
7
+ # Thread-safe client for communicating with the MudisServer
8
+ # Automatically uses UNIX sockets on Linux/macOS and TCP on Windows
9
+ class MudisClient
10
+ include MudisIPCConfig
11
+
12
+ def initialize
13
+ @mutex = Mutex.new
14
+ end
15
+
16
+ # Open a connection to the server (TCP or UNIX)
17
+ def open_connection
18
+ if MudisIPCConfig.use_tcp?
19
+ TCPSocket.new(TCP_HOST, TCP_PORT)
20
+ else
21
+ UNIXSocket.open(SOCKET_PATH)
22
+ end
23
+ end
24
+
25
+ # Send a request to the MudisServer and return the response
26
+ # @param payload [Hash] The request payload
27
+ # @return [Object] The response value from the server
28
+ def request(payload) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
29
+ @mutex.synchronize do
30
+ sock = open_connection
31
+ sock.puts(JSON.dump(payload))
32
+ response = sock.gets
33
+ sock.close
34
+
35
+ return nil unless response
36
+
37
+ res = JSON.parse(response, symbolize_names: true)
38
+ raise res[:error] unless res[:ok]
39
+
40
+ res[:value]
41
+ rescue Errno::ENOENT, Errno::ECONNREFUSED
42
+ warn "[MudisClient] Cannot connect to MudisServer. Is it running?"
43
+ nil
44
+ rescue JSON::ParserError
45
+ warn "[MudisClient] Invalid JSON response from server"
46
+ nil
47
+ rescue IOError, SystemCallError => e
48
+ warn "[MudisClient] Connection error: #{e.message}"
49
+ nil
50
+ end
51
+ end
52
+
53
+ # --- Forwarded Mudis methods ---
54
+
55
+ # Read a value from the Mudis server
56
+ def read(key, namespace: nil)
57
+ command("read", key:, namespace:)
58
+ end
59
+
60
+ # Write a value to the Mudis server
61
+ def write(key, value, expires_in: nil, namespace: nil)
62
+ command("write", key:, value:, ttl: expires_in, namespace:)
63
+ end
64
+
65
+ # Delete a value from the Mudis server
66
+ def delete(key, namespace: nil)
67
+ command("delete", key:, namespace:)
68
+ end
69
+
70
+ # Check if a key exists in the Mudis server
71
+ def exists?(key, namespace: nil)
72
+ command("exists", key:, namespace:)
73
+ end
74
+
75
+ # Fetch a value, computing and storing it if not present
76
+ def fetch(key, expires_in: nil, namespace: nil)
77
+ val = read(key, namespace:)
78
+ return val if val
79
+
80
+ new_val = yield
81
+ write(key, new_val, expires_in:, namespace:)
82
+ new_val
83
+ end
84
+
85
+ # Retrieve metrics from the Mudis server
86
+ def metrics
87
+ command("metrics")
88
+ end
89
+
90
+ # Reset metrics on the Mudis server
91
+ def reset_metrics!
92
+ command("reset_metrics")
93
+ end
94
+
95
+ # Reset the Mudis server cache state
96
+ def reset!
97
+ command("reset")
98
+ end
99
+
100
+ private
101
+
102
+ # Helper to send a command with options
103
+ def command(cmd, **opts)
104
+ request({ cmd:, **opts })
105
+ end
106
+
107
+ end
data/lib/mudis_config.rb CHANGED
@@ -1,35 +1,35 @@
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
- # Persistence settings
15
- :persistence_enabled,
16
- :persistence_path,
17
- :persistence_format,
18
- :persistence_safe_write
19
-
20
- def initialize
21
- @serializer = JSON # Default serialization strategy
22
- @compress = false # Whether to compress values with Zlib
23
- @max_value_bytes = nil # Max size per value (optional)
24
- @hard_memory_limit = false # Enforce max_bytes as hard cap
25
- @max_bytes = 1_073_741_824 # 1 GB default max cache size
26
- @buckets = nil # use nil to signal fallback to ENV or default
27
- @max_ttl = nil # Max TTL for cache entries (optional)
28
- @default_ttl = nil # Default TTL for cache entries (optional)
29
- # Persistence settings
30
- @persistence_enabled = false # Whether persistence is enabled
31
- @persistence_path = 'mudis_data' # Default path for persistence files
32
- @persistence_format = :json # Default persistence file format
33
- @persistence_safe_write = true # Whether to use safe write for persistence
34
- end
35
- 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
+ # Persistence settings
15
+ :persistence_enabled,
16
+ :persistence_path,
17
+ :persistence_format,
18
+ :persistence_safe_write
19
+
20
+ def initialize # rubocop:disable Metrics/MethodLength
21
+ @serializer = JSON # Default serialization strategy
22
+ @compress = false # Whether to compress values with Zlib
23
+ @max_value_bytes = nil # Max size per value (optional)
24
+ @hard_memory_limit = false # Enforce max_bytes as hard cap
25
+ @max_bytes = 1_073_741_824 # 1 GB default max cache size
26
+ @buckets = nil # use nil to signal fallback to ENV or default
27
+ @max_ttl = nil # Max TTL for cache entries (optional)
28
+ @default_ttl = nil # Default TTL for cache entries (optional)
29
+ # Persistence settings
30
+ @persistence_enabled = false # Whether persistence is enabled
31
+ @persistence_path = "mudis_data" # Default path for persistence files
32
+ @persistence_format = :json # Default persistence file format
33
+ @persistence_safe_write = true # Whether to use safe write for persistence
34
+ end
35
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Shared configuration for IPC mode (server and client)
4
+ module MudisIPCConfig
5
+ SOCKET_PATH = "/tmp/mudis.sock"
6
+ TCP_HOST = "127.0.0.1"
7
+ TCP_PORT = 9876
8
+
9
+ # Check if TCP mode should be used (Windows or forced via ENV)
10
+ def self.use_tcp?
11
+ ENV["MUDIS_FORCE_TCP"] == "true" || Gem.win_platform?
12
+ end
13
+ end
data/lib/mudis_proxy.rb CHANGED
@@ -1,37 +1,39 @@
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_client"
13
-
14
- # Note that this file must be required after MudisServer
15
- # has been loaded, otherwise the proxy will not be activated.
16
-
17
- unless defined?(MudisClient)
18
- warn "[MudisProxy] MudisClient not loaded: proxy not activated"
19
- return
20
- end
21
-
22
- unless defined?($mudis) && $mudis # rubocop:disable Style/GlobalVars
23
- warn "[MudisProxy] $mudis not set: proxy not activated"
24
- return
25
- end
26
-
27
- class << Mudis
28
- def read(*a, **k) = $mudis.read(*a, **k) # rubocop:disable Naming/MethodParameterName,Style/GlobalVars
29
- def write(*a, **k) = $mudis.write(*a, **k) # rubocop:disable Naming/MethodParameterName,Style/GlobalVars
30
- def delete(*a, **k) = $mudis.delete(*a, **k) # rubocop:disable Naming/MethodParameterName,Style/GlobalVars
31
- def fetch(*a, **k, &b) = $mudis.fetch(*a, **k, &b) # rubocop:disable Naming/MethodParameterName,Style/GlobalVars
32
- def metrics = $mudis.metrics # rubocop:disable Style/GlobalVars
33
- def reset_metrics! = $mudis.reset_metrics! # rubocop:disable Style/GlobalVars
34
- def reset! = $mudis.reset! # rubocop:disable Style/GlobalVars
35
- end
36
-
37
- warn "[MudisProxy] Proxy activated: forwarding calls to $mudis"
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_client"
13
+
14
+ # Note that this file must be required after MudisServer
15
+ # has been loaded, otherwise the proxy will not be activated.
16
+
17
+ unless defined?(MudisClient)
18
+ warn "[MudisProxy] MudisClient not loaded: proxy not activated"
19
+ return
20
+ end
21
+
22
+ unless defined?($mudis) && $mudis # rubocop:disable Style/GlobalVars
23
+ warn "[MudisProxy] $mudis not set: proxy not activated"
24
+ return
25
+ end
26
+
27
+ # --- Proxy method forwarding ---
28
+
29
+ class << Mudis
30
+ def read(*a, **k) = $mudis.read(*a, **k) # rubocop:disable Naming/MethodParameterName,Style/GlobalVars
31
+ def write(*a, **k) = $mudis.write(*a, **k) # rubocop:disable Naming/MethodParameterName,Style/GlobalVars
32
+ def delete(*a, **k) = $mudis.delete(*a, **k) # rubocop:disable Naming/MethodParameterName,Style/GlobalVars
33
+ def fetch(*a, **k, &b) = $mudis.fetch(*a, **k, &b) # rubocop:disable Naming/MethodParameterName,Style/GlobalVars
34
+ def metrics = $mudis.metrics # rubocop:disable Style/GlobalVars
35
+ def reset_metrics! = $mudis.reset_metrics! # rubocop:disable Style/GlobalVars
36
+ def reset! = $mudis.reset! # rubocop:disable Style/GlobalVars
37
+ end
38
+
39
+ warn "[MudisProxy] Proxy activated: forwarding calls to $mudis"
data/lib/mudis_server.rb CHANGED
@@ -1,81 +1,100 @@
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
1
+ # frozen_string_literal: true
2
+
3
+ require "socket"
4
+ require "json"
5
+ require_relative "mudis"
6
+ require_relative "mudis_ipc_config"
7
+
8
+ # Socket server for handling Mudis operations via IPC mode
9
+ # Automatically uses UNIX sockets on Linux/macOS and TCP on Windows
10
+ class MudisServer
11
+ include MudisIPCConfig
12
+
13
+ # Define command handlers mapping
14
+ # Each command maps to a lambda that takes a request hash and performs the corresponding Mudis operation.
15
+ COMMANDS = {
16
+ "read" => ->(r) { Mudis.read(r[:key], namespace: r[:namespace]) },
17
+ "write" => ->(r) { Mudis.write(r[:key], r[:value], expires_in: r[:ttl], namespace: r[:namespace]) },
18
+ "delete" => ->(r) { Mudis.delete(r[:key], namespace: r[:namespace]) },
19
+ "exists" => ->(r) { Mudis.exists?(r[:key], namespace: r[:namespace]) },
20
+ "fetch" => ->(r) { Mudis.fetch(r[:key], expires_in: r[:ttl], namespace: r[:namespace]) { r[:fallback] } },
21
+ "metrics" => ->(_) { Mudis.metrics },
22
+ "reset_metrics" => ->(_) { Mudis.reset_metrics! },
23
+ "reset" => ->(_) { Mudis.reset! }
24
+ }.freeze
25
+
26
+ # Start the MudisServer
27
+ # Automatically selects TCP on Windows, UNIX sockets elsewhere
28
+ # This will run in a separate thread and handle incoming client connections.
29
+ def self.start!
30
+ if MudisIPCConfig.use_tcp?
31
+ start_tcp_server!
32
+ else
33
+ start_unix_server!
34
+ end
35
+ end
36
+
37
+ # Start TCP server (for Windows or development)
38
+ def self.start_tcp_server!
39
+ warn "[MudisServer] Using TCP mode - recommended for development only"
40
+ server = TCPServer.new(TCP_HOST, TCP_PORT)
41
+ puts "[MudisServer] Listening on TCP #{TCP_HOST}:#{TCP_PORT}"
42
+ accept_connections(server)
43
+ end
44
+
45
+ # Start UNIX socket server (production mode for Linux/macOS)
46
+ def self.start_unix_server! # rubocop:disable Metrics/MethodLength
47
+ File.unlink(SOCKET_PATH) if File.exist?(SOCKET_PATH)
48
+ server = UNIXServer.new(SOCKET_PATH)
49
+ server.listen(128)
50
+ puts "[MudisServer] Listening on UNIX socket #{SOCKET_PATH}"
51
+ accept_connections(server)
52
+ end
53
+
54
+ # Accept connections in a loop (works for both TCP and UNIX)
55
+ def self.accept_connections(server)
56
+ Thread.new do
57
+ loop do
58
+ client = server.accept
59
+ Thread.new(client) do |sock|
60
+ handle_client(sock)
61
+ end
62
+ end
63
+ end
64
+ end
65
+
66
+ # Handle a single client connection
67
+ # Reads the request, processes it, and sends back the response
68
+ # @param socket [Socket] The client socket (TCP or UNIX)
69
+ # @return [void]
70
+ def self.handle_client(socket)
71
+ request = JSON.parse(socket.gets, symbolize_names: true)
72
+ return unless request
73
+
74
+ response = process_request(request)
75
+ write_response(socket, ok: true, value: response)
76
+ rescue StandardError => e
77
+ write_response(socket, ok: false, error: e.message)
78
+ ensure
79
+ socket.close
80
+ end
81
+
82
+ # Process a request hash and return the result
83
+ # Raises an error if the command is unknown
84
+ # @param req [Hash] The request hash containing :cmd and other parameters
85
+ # @return [Object] The result of the command execution
86
+ def self.process_request(req)
87
+ handler = COMMANDS[req[:cmd]]
88
+ raise "Unknown command: #{req[:cmd]}" unless handler
89
+
90
+ handler.call(req)
91
+ end
92
+
93
+ # Write a response to the client socket
94
+ # @param socket [Socket] The client socket
95
+ # @param payload [Hash] The response payload
96
+ # @return [void]
97
+ def self.write_response(socket, payload)
98
+ socket.puts(JSON.dump(payload))
99
+ end
100
+ end
data/sig/mudis.rbs CHANGED
@@ -1,56 +1,62 @@
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
+ extend LRU
3
+ extend Persistence
4
+ extend Metrics
5
+ extend Namespace
6
+ extend Expiry
7
+
8
+ # Configuration
9
+ class << self
10
+ attr_accessor serializer : Object
11
+ attr_accessor compress : bool
12
+ attr_accessor hard_memory_limit : bool
13
+ attr_reader max_bytes : Integer
14
+ attr_reader max_value_bytes : Integer?
15
+ attr_accessor max_ttl: Integer?
16
+ attr_accessor default_ttl: Integer?
17
+
18
+ def configure: () { (config: MudisConfig) -> void } -> void
19
+ def config: () -> MudisConfig
20
+ def apply_config!: () -> void
21
+ def validate_config!: () -> void
22
+
23
+ def buckets: () -> Integer
24
+ end
25
+
26
+ # Lifecycle
27
+ def self.start_expiry_thread: (?interval: Integer) -> void
28
+ def self.stop_expiry_thread: () -> void
29
+
30
+ # Core operations
31
+ def self.write: (String, untyped, ?expires_in: Integer, ?namespace: String) -> void
32
+ def self.read: (String, ?namespace: String) -> untyped?
33
+ def self.update: (String, ?namespace: String) { (untyped) -> untyped } -> void
34
+ def self.delete: (String, ?namespace: String) -> void
35
+ def self.exists?: (String, ?namespace: String) -> bool
36
+
37
+ # DSL & Helpers
38
+ def self.fetch: (
39
+ String,
40
+ ?expires_in: Integer,
41
+ ?force: bool,
42
+ ?namespace: String
43
+ ) { () -> untyped } -> untyped
44
+
45
+ def self.clear: (String, ?namespace: String) -> void
46
+ def self.replace: (String, untyped, ?expires_in: Integer, ?namespace: String) -> void
47
+ def self.inspect: (String, ?namespace: String) -> Hash[Symbol, untyped]?
48
+ def self.keys: (?namespace: String) -> Array[String]
49
+ def self.clear_namespace: (?namespace: String) -> void
50
+
51
+ # Introspection & management
52
+ def self.metrics: () -> Hash[Symbol, untyped]
53
+ def self.cleanup_expired!: () -> void
54
+ def self.all_keys: () -> Array[String]
55
+ def self.current_memory_bytes: () -> Integer
56
+ def self.max_memory_bytes: () -> Integer
57
+ def self.least_touched: (?Integer) -> Array[[String, Integer]]
58
+
59
+ # State reset
60
+ def self.reset!: () -> void
61
+ def self.reset_metrics!: () -> void
62
+ end