prosody 0.5.1 → 0.6.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.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/.config/rail.toml +26 -0
  3. data/.release-please-manifest.json +1 -1
  4. data/.ruby-version +1 -1
  5. data/.taplo.toml +1 -1
  6. data/AGENTS.md +29 -15
  7. data/CHANGELOG.md +7 -0
  8. data/CONFIGURATION.md +19 -16
  9. data/Cargo.lock +254 -235
  10. data/Cargo.toml +11 -8
  11. data/README.md +100 -23
  12. data/examples/keyed_state.rb +10 -2
  13. data/examples/keyed_state.rbs +1 -0
  14. data/ext/prosody/Cargo.toml +1 -1
  15. data/ext/prosody/src/admin.rs +36 -36
  16. data/ext/prosody/src/bridge/mod.rs +5 -10
  17. data/ext/prosody/src/client/config/connections.rs +170 -0
  18. data/ext/prosody/src/client/config/middleware.rs +184 -0
  19. data/ext/prosody/src/client/config/mod.rs +396 -0
  20. data/ext/prosody/src/client/config/state.rs +323 -0
  21. data/ext/prosody/src/client/mod.rs +31 -117
  22. data/ext/prosody/src/client/readers.rs +106 -0
  23. data/ext/prosody/src/client/request.rs +2 -2
  24. data/ext/prosody/src/client/support.rs +13 -32
  25. data/ext/prosody/src/gvl.rs +8 -6
  26. data/ext/prosody/src/handler/{context.rs → context/mod.rs} +36 -150
  27. data/ext/prosody/src/handler/context/vending.rs +138 -0
  28. data/ext/prosody/src/handler/message.rs +36 -0
  29. data/ext/prosody/src/handler/mod.rs +18 -8
  30. data/ext/prosody/src/handler/state/deque.rs +144 -0
  31. data/ext/prosody/src/handler/state/mod.rs +163 -268
  32. data/ext/prosody/src/handler/state/query.rs +264 -0
  33. data/ext/prosody/src/handler/state/registration.rs +15 -86
  34. data/ext/prosody/src/handler/state/scan.rs +33 -141
  35. data/ext/prosody/src/handler/state/set.rs +98 -0
  36. data/ext/prosody/src/lib.rs +54 -36
  37. data/ext/prosody/src/logging.rs +6 -6
  38. data/ext/prosody/src/published.rs +171 -152
  39. data/ext/prosody/src/scheduler/result.rs +2 -1
  40. data/ext/prosody/src/util.rs +65 -3
  41. data/lib/prosody/client.rb +32 -0
  42. data/lib/prosody/configuration.rb +20 -12
  43. data/lib/prosody/demand.rb +27 -0
  44. data/lib/prosody/native_stubs/client.rb +157 -0
  45. data/lib/prosody/native_stubs/context.rb +178 -0
  46. data/lib/prosody/native_stubs/message.rb +133 -0
  47. data/lib/prosody/native_stubs.rb +11 -956
  48. data/lib/prosody/state/deque.rb +253 -0
  49. data/lib/prosody/state/map.rb +313 -0
  50. data/lib/prosody/state/set.rb +121 -0
  51. data/lib/prosody/state/value.rb +58 -0
  52. data/lib/prosody/state.rb +157 -677
  53. data/lib/prosody/version.rb +1 -1
  54. data/lib/prosody.rb +2 -1
  55. data/sig/configuration.rbs +19 -10
  56. data/sig/prosody.rbs +37 -4
  57. data/sig/published.rbs +102 -0
  58. data/sig/state.rbs +109 -122
  59. data/typecheck/payload_types.rb +12 -0
  60. data/typecheck/payload_types.rbs +1 -0
  61. metadata +30 -11
  62. data/ext/prosody/src/client/config.rs +0 -1300
@@ -6,5 +6,5 @@ module Prosody
6
6
  # This version number follows semantic versioning and is used by the
7
7
  # gem system to identify the library version. It should be updated
8
8
  # according to semver guidelines when making releases.
9
- VERSION = "0.5.1"
9
+ VERSION = "0.6.0"
10
10
  end
data/lib/prosody.rb CHANGED
@@ -16,10 +16,10 @@ require "async"
16
16
  require "logger"
17
17
  require_relative "prosody/version"
18
18
  require_relative "prosody/configuration"
19
+ require_relative "prosody/demand"
19
20
  require_relative "prosody/handler"
20
21
  require_relative "prosody/processor"
21
22
  require_relative "prosody/sentry"
22
- require_relative "prosody/native_stubs" if defined?(Prosody::Client)
23
23
 
24
24
  module Prosody
25
25
  def self.logger
@@ -46,3 +46,4 @@ end
46
46
  # already exist.
47
47
  require_relative "prosody/state"
48
48
  require_relative "prosody/request"
49
+ require_relative "prosody/client"
@@ -63,6 +63,10 @@ module Prosody
63
63
  def commit_interval: () -> Float?
64
64
  def commit_interval=: (Numeric) -> Float
65
65
 
66
+ # Interval between librdkafka statistics reports (seconds)
67
+ def statistics_interval: () -> Float?
68
+ def statistics_interval=: (Numeric) -> Float
69
+
66
70
  # Base delay for retry operations (seconds)
67
71
  def retry_base: () -> Float?
68
72
  def retry_base=: (Numeric) -> Float
@@ -71,7 +75,7 @@ module Prosody
71
75
  def max_retry_delay: () -> Float?
72
76
  def max_retry_delay=: (Numeric) -> Float
73
77
 
74
- # Size of the cache used for message idempotence
78
+ # Capacity of the producer idempotence cache and the consumer deduplication cache
75
79
  def idempotence_cache_size: () -> Integer?
76
80
  def idempotence_cache_size=: (Integer | _ToInt) -> Integer
77
81
 
@@ -256,18 +260,20 @@ module Prosody
256
260
  def timer_spans=: (_ToS) -> String
257
261
 
258
262
  # Keyed-state collections to register before subscribe. Accepts an array of
259
- # definition objects (from Prosody.value/map/deque and their message_*
263
+ # definition objects (from Prosody.value/map/set/deque and their message_*
260
264
  # siblings) or already-serialized registration hashes.
261
265
  def state_collections: () -> Array[untyped]?
262
266
  def state_collections=: (untyped) -> Array[untyped]
263
267
 
264
- # Subsystem under which published JSON collections are advertised.
268
+ # Subsystem under which published JSON and set collections are advertised.
265
269
  # Uses PROSODY_SUBSYSTEM when omitted. Published collections require it.
266
270
  def subsystem: () -> String?
267
271
  def subsystem=: (_ToS) -> String
268
272
 
269
- # Root directory for the local keyed-state cache. Falls back to
270
- # PROSODY_STATE_CACHE_DIR. Must not be an empty string.
273
+ # Directory for the local keyed-state caches. Each consumer uses a new
274
+ # subdirectory, so clients can share it. Falls back to
275
+ # PROSODY_STATE_CACHE_DIR, then to <temp>/prosody/keyed-state. Must not be
276
+ # an empty string.
271
277
  def state_cache_dir: () -> String?
272
278
  def state_cache_dir=: (_ToS) -> String
273
279
 
@@ -277,6 +283,14 @@ module Prosody
277
283
  def state_owned_cache_size: () -> String?
278
284
  def state_owned_cache_size=: (String | _ToS) -> String
279
285
 
286
+ # Bytes of in-memory writes the local keyed-state cache holds for each
287
+ # assigned partition before it flushes them to disk. Accepts a
288
+ # human-readable size. Memory use scales with the number of assigned
289
+ # partitions. Uses PROSODY_STATE_MEMTABLE_SIZE when omitted. Otherwise,
290
+ # the engine uses its default of 64 MiB.
291
+ def state_memtable_size: () -> String?
292
+ def state_memtable_size=: (String | _ToS) -> String
293
+
280
294
  # Capacity of the published-state read cache. Uses
281
295
  # PROSODY_STATE_READ_CACHE_SIZE when omitted. It then uses the owned cache
282
296
  # size when set, or 1 MiB when both sizes are unset.
@@ -288,11 +302,6 @@ module Prosody
288
302
  def state_read_cache: () -> (Float | false)?
289
303
  def state_read_cache=: (Numeric | false) -> (Float | false)
290
304
 
291
- # Delay in whole seconds between staging a provisional cell and the recovery
292
- # sweep. Falls back to PROSODY_STATE_RECOVERY_DELAY (read by core).
293
- def state_recovery_delay: () -> Float?
294
- def state_recovery_delay=: (Numeric) -> Float
295
-
296
305
  # Convert configuration to a hash with non-nil values only
297
306
  def to_hash: () -> Hash[Symbol, untyped]
298
307
  end
data/sig/prosody.rbs CHANGED
@@ -23,8 +23,9 @@ module Prosody
23
23
  # Creates an admin client for the specified Kafka bootstrap servers.
24
24
  def self.new: (Array[String]) -> AdminClient
25
25
 
26
- # Creates a topic.
27
- def create_topic: (String, Integer, Integer) -> void
26
+ # Creates a topic. A nil cleanup_policy or retention uses the cluster
27
+ # default. retention is in seconds.
28
+ def create_topic: (String name, Integer partition_count, Integer replication_factor, ?cleanup_policy: String?, ?retention: Numeric?) -> void
28
29
 
29
30
  # Deletes a topic.
30
31
  def delete_topic: (String) -> void
@@ -40,11 +41,31 @@ module Prosody
40
41
  def initialize: (untyped) -> void
41
42
  end
42
43
 
44
+ type demand_kind = :normal | :failure
45
+
46
+ # The demand a handler call serves. kind is :normal for a normal delivery
47
+ # and :failure for a retry. retries is the number of retries: 0 for a
48
+ # normal delivery and 1 on the first retry. The count is an estimate.
49
+ class Demand < Data
50
+ attr_reader kind: demand_kind
51
+ attr_reader retries: Integer
52
+
53
+ def self.new: (demand_kind kind, Integer retries) -> instance
54
+ | (kind: demand_kind, retries: Integer) -> instance
55
+
56
+ def normal?: () -> bool
57
+ def failure?: () -> bool
58
+ end
59
+
43
60
  # Represents the context of a Kafka message.
44
61
  # Provides metadata and control capabilities for message processing.
45
62
  class Context
46
63
  # This class is instantiated internally by the client.
47
64
 
65
+ # Returns whether this call is a normal delivery or a retry.
66
+ # @return [Demand] the demand this call serves
67
+ def demand: () -> Demand
68
+
48
69
  # Checks if cancellation has been requested.
49
70
  # Cancellation includes both message-level cancellation (e.g., timeout)
50
71
  # and partition shutdown.
@@ -111,6 +132,12 @@ module Prosody
111
132
  # Returns the deserialized message payload.
112
133
  # @return [Payload] The message content
113
134
  def payload: () -> Payload
135
+
136
+ # Returns the producer's source system, or nil when the message has none.
137
+ def source_system: () -> String?
138
+
139
+ # Reports whether the producer requested a response to this message.
140
+ def response_requested?: () -> bool
114
141
  end
115
142
 
116
143
  # Represents an excise record with no payload.
@@ -120,6 +147,8 @@ module Prosody
120
147
  def offset: () -> Integer
121
148
  def key: () -> String
122
149
  def timestamp: () -> Time
150
+ def source_system: () -> String?
151
+ def response_requested?: () -> bool
123
152
  end
124
153
 
125
154
  # Represents a timer that was scheduled to fire at a specific time.
@@ -151,6 +180,9 @@ module Prosody
151
180
  def self.new: (Hash[Symbol, untyped] | Configuration config) -> Client
152
181
  | (**untyped options) -> Client
153
182
 
183
+ # Creates a client, yields it, and shuts it down when the block exits.
184
+ def self.open: [T] (Hash[Symbol, untyped] | Configuration config) { (Client client) -> T } -> T
185
+
154
186
  # Returns the current state of the consumer.
155
187
  #
156
188
  # @return [Symbol] The consumer state: :shut_down, :unconfigured, :configured, or :running
@@ -159,7 +191,7 @@ module Prosody
159
191
  # Returns the number of Kafka partitions currently assigned to this consumer.
160
192
  #
161
193
  # @return [Integer] The number of assigned partitions
162
- def assigned_partitions: () -> Integer
194
+ def assigned_partition_count: () -> Integer
163
195
 
164
196
  # Checks if the consumer is stalled.
165
197
  #
@@ -167,7 +199,7 @@ module Prosody
167
199
  # errors or reaching processing limits.
168
200
  #
169
201
  # @return [Boolean] true if the consumer is stalled, false otherwise
170
- def is_stalled?: () -> bool
202
+ def stalled?: () -> bool
171
203
 
172
204
  # Sends a message to the specified Kafka topic.
173
205
  #
@@ -212,6 +244,7 @@ module Prosody
212
244
 
213
245
  def state: [T] (_ToS subsystem, _ValueDefinition[T] definition) -> PublishedValue[T]
214
246
  | [V] (_ToS subsystem, _MapDefinition[V] definition) -> PublishedMap[V]
247
+ | (_ToS subsystem, _SetDefinition definition) -> PublishedSet
215
248
  | [T] (_ToS subsystem, _DequeDefinition[T] definition) -> PublishedDeque[T]
216
249
  end
217
250
  end
data/sig/published.rbs ADDED
@@ -0,0 +1,102 @@
1
+ module Prosody
2
+ # --- published-state readers, opened by Client#state -------------------
3
+ class NativePublishedValue
4
+ def get: (String key) -> json_value?
5
+ end
6
+
7
+ class NativePublishedMap
8
+ def get: (String key, String map_key) -> json_value?
9
+ def get_many: (String key, Array[String] map_keys) -> Array[json_value?]
10
+ def contains_key: (String key, String map_key) -> bool
11
+ def contains_many: (String key, Array[String] map_keys) -> Array[bool]
12
+ def is_empty: (String key) -> bool
13
+ def scan: (String key, Symbol direction, query_options query) -> NativeJsonMapScan
14
+ def keys: (String key, Symbol direction, query_options query) -> NativeMapKeyScan
15
+ end
16
+
17
+ class NativePublishedSet
18
+ def contains: (String key, String member) -> bool
19
+ def contains_many: (String key, Array[String] members) -> Array[bool]
20
+ def is_empty: (String key) -> bool
21
+ def keys: (String key, Symbol direction, query_options query) -> NativeMapKeyScan
22
+ end
23
+
24
+ class NativePublishedDeque
25
+ def get: (String key, Integer index) -> json_value?
26
+ def length: (String key) -> Integer
27
+ def is_empty: (String key) -> bool
28
+ def peek_front: (String key) -> json_value?
29
+ def peek_back: (String key) -> json_value?
30
+ def scan: (String key, Symbol direction, query_options query) -> NativeJsonDequeScan
31
+ end
32
+
33
+ class PublishedValue[T = json_value]
34
+ def get: (String key) -> T?
35
+
36
+ private
37
+ def initialize: (NativePublishedValue native) -> void
38
+ end
39
+
40
+ class PublishedMap[V = json_value]
41
+ include State::Scanning
42
+
43
+ def get: (String key, String map_key) -> V?
44
+ def get_many: (String key, Array[String] map_keys) -> Array[V?]
45
+ def key?: (String key, String map_key) -> bool
46
+ alias has_key? key?
47
+ alias include? key?
48
+ alias member? key?
49
+ def contains_many: (String key, Array[String] map_keys) -> Array[bool]
50
+ def empty?: (String key) -> bool
51
+ def each_pair: (String key, ?from: String?, ?after: String?, ?to: String?, ?before: String?, ?range: Range[String?]?, ?prefix: String?, ?limit: Integer?) { ([String, V]) -> void } -> void
52
+ | (String key, ?from: String?, ?after: String?, ?to: String?, ?before: String?, ?range: Range[String?]?, ?prefix: String?, ?limit: Integer?) -> Enumerator[[String, V], void]
53
+ def reverse_each_pair: (String key, ?from: String?, ?after: String?, ?to: String?, ?before: String?, ?range: Range[String?]?, ?prefix: String?, ?limit: Integer?) { ([String, V]) -> void } -> void
54
+ | (String key, ?from: String?, ?after: String?, ?to: String?, ?before: String?, ?range: Range[String?]?, ?prefix: String?, ?limit: Integer?) -> Enumerator[[String, V], void]
55
+ def each_key: (String key, ?from: String?, ?after: String?, ?to: String?, ?before: String?, ?range: Range[String?]?, ?prefix: String?, ?limit: Integer?) { (String) -> void } -> void
56
+ | (String key, ?from: String?, ?after: String?, ?to: String?, ?before: String?, ?range: Range[String?]?, ?prefix: String?, ?limit: Integer?) -> Enumerator[String, void]
57
+ def reverse_each_key: (String key, ?from: String?, ?after: String?, ?to: String?, ?before: String?, ?range: Range[String?]?, ?prefix: String?, ?limit: Integer?) { (String) -> void } -> void
58
+ | (String key, ?from: String?, ?after: String?, ?to: String?, ?before: String?, ?range: Range[String?]?, ?prefix: String?, ?limit: Integer?) -> Enumerator[String, void]
59
+ def each_value: (String key, ?from: String?, ?after: String?, ?to: String?, ?before: String?, ?range: Range[String?]?, ?prefix: String?, ?limit: Integer?) { (V) -> void } -> void
60
+ | (String key, ?from: String?, ?after: String?, ?to: String?, ?before: String?, ?range: Range[String?]?, ?prefix: String?, ?limit: Integer?) -> Enumerator[V, void]
61
+ def reverse_each_value: (String key, ?from: String?, ?after: String?, ?to: String?, ?before: String?, ?range: Range[String?]?, ?prefix: String?, ?limit: Integer?) { (V) -> void } -> void
62
+ | (String key, ?from: String?, ?after: String?, ?to: String?, ?before: String?, ?range: Range[String?]?, ?prefix: String?, ?limit: Integer?) -> Enumerator[V, void]
63
+ alias each each_pair
64
+
65
+ private
66
+ def initialize: (NativePublishedMap native) -> void
67
+ end
68
+
69
+ class PublishedSet
70
+ include State::Scanning
71
+
72
+ def include?: (String key, String member) -> bool
73
+ alias member? include?
74
+ def contains_many: (String key, Array[String] members) -> Array[bool]
75
+ def empty?: (String key) -> bool
76
+ def each: (String key, ?from: String?, ?after: String?, ?to: String?, ?before: String?, ?range: Range[String?]?, ?prefix: String?, ?limit: Integer?) { (String) -> void } -> void
77
+ | (String key, ?from: String?, ?after: String?, ?to: String?, ?before: String?, ?range: Range[String?]?, ?prefix: String?, ?limit: Integer?) -> Enumerator[String, void]
78
+ def reverse_each: (String key, ?from: String?, ?after: String?, ?to: String?, ?before: String?, ?range: Range[String?]?, ?prefix: String?, ?limit: Integer?) { (String) -> void } -> void
79
+ | (String key, ?from: String?, ?after: String?, ?to: String?, ?before: String?, ?range: Range[String?]?, ?prefix: String?, ?limit: Integer?) -> Enumerator[String, void]
80
+
81
+ private
82
+ def initialize: (NativePublishedSet native) -> void
83
+ end
84
+
85
+ class PublishedDeque[T = json_value]
86
+ include State::Scanning
87
+
88
+ def get: (String key, Integer index) -> T?
89
+ def length: (String key) -> Integer
90
+ alias size length
91
+ def empty?: (String key) -> bool
92
+ def first: (String key) -> T?
93
+ def last: (String key) -> T?
94
+ def each: (String key, ?from: Integer?, ?after: Integer?, ?to: Integer?, ?before: Integer?, ?range: Range[Integer?]?, ?limit: Integer?) { (T) -> void } -> void
95
+ | (String key, ?from: Integer?, ?after: Integer?, ?to: Integer?, ?before: Integer?, ?range: Range[Integer?]?, ?limit: Integer?) -> Enumerator[T, void]
96
+ def reverse_each: (String key, ?from: Integer?, ?after: Integer?, ?to: Integer?, ?before: Integer?, ?range: Range[Integer?]?, ?limit: Integer?) { (T) -> void } -> void
97
+ | (String key, ?from: Integer?, ?after: Integer?, ?to: Integer?, ?before: Integer?, ?range: Range[Integer?]?, ?limit: Integer?) -> Enumerator[T, void]
98
+
99
+ private
100
+ def initialize: (NativePublishedDeque native) -> void
101
+ end
102
+ end