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.
- checksums.yaml +4 -4
- data/.config/rail.toml +26 -0
- data/.release-please-manifest.json +1 -1
- data/.ruby-version +1 -1
- data/.taplo.toml +1 -1
- data/AGENTS.md +29 -15
- data/CHANGELOG.md +7 -0
- data/CONFIGURATION.md +19 -16
- data/Cargo.lock +254 -235
- data/Cargo.toml +11 -8
- data/README.md +100 -23
- data/examples/keyed_state.rb +10 -2
- data/examples/keyed_state.rbs +1 -0
- data/ext/prosody/Cargo.toml +1 -1
- data/ext/prosody/src/admin.rs +36 -36
- data/ext/prosody/src/bridge/mod.rs +5 -10
- data/ext/prosody/src/client/config/connections.rs +170 -0
- data/ext/prosody/src/client/config/middleware.rs +184 -0
- data/ext/prosody/src/client/config/mod.rs +396 -0
- data/ext/prosody/src/client/config/state.rs +323 -0
- data/ext/prosody/src/client/mod.rs +31 -117
- data/ext/prosody/src/client/readers.rs +106 -0
- data/ext/prosody/src/client/request.rs +2 -2
- data/ext/prosody/src/client/support.rs +13 -32
- data/ext/prosody/src/gvl.rs +8 -6
- data/ext/prosody/src/handler/{context.rs → context/mod.rs} +36 -150
- data/ext/prosody/src/handler/context/vending.rs +138 -0
- data/ext/prosody/src/handler/message.rs +36 -0
- data/ext/prosody/src/handler/mod.rs +18 -8
- data/ext/prosody/src/handler/state/deque.rs +144 -0
- data/ext/prosody/src/handler/state/mod.rs +163 -268
- data/ext/prosody/src/handler/state/query.rs +264 -0
- data/ext/prosody/src/handler/state/registration.rs +15 -86
- data/ext/prosody/src/handler/state/scan.rs +33 -141
- data/ext/prosody/src/handler/state/set.rs +98 -0
- data/ext/prosody/src/lib.rs +54 -36
- data/ext/prosody/src/logging.rs +6 -6
- data/ext/prosody/src/published.rs +171 -152
- data/ext/prosody/src/scheduler/result.rs +2 -1
- data/ext/prosody/src/util.rs +65 -3
- data/lib/prosody/client.rb +32 -0
- data/lib/prosody/configuration.rb +20 -12
- data/lib/prosody/demand.rb +27 -0
- data/lib/prosody/native_stubs/client.rb +157 -0
- data/lib/prosody/native_stubs/context.rb +178 -0
- data/lib/prosody/native_stubs/message.rb +133 -0
- data/lib/prosody/native_stubs.rb +11 -956
- data/lib/prosody/state/deque.rb +253 -0
- data/lib/prosody/state/map.rb +313 -0
- data/lib/prosody/state/set.rb +121 -0
- data/lib/prosody/state/value.rb +58 -0
- data/lib/prosody/state.rb +157 -677
- data/lib/prosody/version.rb +1 -1
- data/lib/prosody.rb +2 -1
- data/sig/configuration.rbs +19 -10
- data/sig/prosody.rbs +37 -4
- data/sig/published.rbs +102 -0
- data/sig/state.rbs +109 -122
- data/typecheck/payload_types.rb +12 -0
- data/typecheck/payload_types.rbs +1 -0
- metadata +30 -11
- data/ext/prosody/src/client/config.rs +0 -1300
data/lib/prosody/version.rb
CHANGED
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"
|
data/sig/configuration.rbs
CHANGED
|
@@ -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
|
-
#
|
|
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
|
-
#
|
|
270
|
-
#
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|