mudis 0.7.2 → 0.7.3

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,65 @@
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
+
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 CHANGED
@@ -1,32 +1,37 @@
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
- # Note that this file must be required *after* MudisClient and MudisServer
12
- # have been loaded, otherwise the proxy will not be activated.
13
-
14
- if defined?(MudisServer)
15
- # In the master process no proxy needed.
16
- return
17
- end
18
-
19
- unless defined?(MudisClient)
20
- warn "[MudisProxy] MudisClient not loaded — proxy not activated"
21
- return
22
- end
23
-
24
- class << Mudis
25
- def read(*a, **k) = $mudis.read(*a, **k) # rubocop:disable Naming/MethodParameterName,Style/GlobalVars
26
- def write(*a, **k) = $mudis.write(*a, **k) # rubocop:disable Naming/MethodParameterName,Style/GlobalVars
27
- def delete(*a, **k) = $mudis.delete(*a, **k) # rubocop:disable Naming/MethodParameterName,Style/GlobalVars
28
- def fetch(*a, **k, &b) = $mudis.fetch(*a, **k, &b) # rubocop:disable Naming/MethodParameterName,Style/GlobalVars
29
- def metrics = $mudis.metrics # rubocop:disable Style/GlobalVars
30
- def reset_metrics! = $mudis.reset_metrics! # rubocop:disable Style/GlobalVars
31
- def reset! = $mudis.reset! # rubocop:disable Style/GlobalVars
32
- end
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
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"
data/lib/mudis_server.rb CHANGED
@@ -1,81 +1,81 @@
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
+
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
data/sig/mudis_client.rbs CHANGED
@@ -1,23 +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
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
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/sig/mudis_server.rbs CHANGED
@@ -1,7 +1,7 @@
1
- class MudisServer
2
- SOCKET_PATH: String
3
-
4
- def self.start!: () -> void
5
-
6
- def self.handle_client: (sock: UNIXSocket) -> void
1
+ class MudisServer
2
+ SOCKET_PATH: String
3
+
4
+ def self.start!: () -> void
5
+
6
+ def self.handle_client: (sock: UNIXSocket) -> void
7
7
  end