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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c19c30d17efd4507beb32278585c05188db05bb0b375c563edf29d24de76cff4
4
- data.tar.gz: 38d1dc9257ac999358813eefcb9995b62da528ca98d0052c59fde8c70893c3f6
3
+ metadata.gz: f51af8dfb50cdab48f5a2a60c0ae761f26ce082ce3be41c538c1642a7147871e
4
+ data.tar.gz: 293c854498dbb467a1ea5aaccb774bc4d4db997e432f03b407cc7b6518de15e7
5
5
  SHA512:
6
- metadata.gz: fddcd444897b7b3ef656d701395d921a47057489ea7e0cd63180fad07e4df5bc94b55386d4b6cc314e4f32653aa85401675d42ced8e3dc7810c3562bf2d97f5e
7
- data.tar.gz: 9068019d952021ce16d10c3eb9c0000f97de3071f922c20ed59a1fccbb93ed20eddc015f7792f076e537cb0dd689dd0e742a29afb3a09da7bc88da4ac3c10ed4
6
+ metadata.gz: 4f7f4a6ec8fb471110213bcf7ba47d38bd06393f0a072afe403fe25a1ed8ee3924444ecd91e26a06207807916c22f4d0c1e803679f9aa75dec80e35f3d64c361
7
+ data.tar.gz: 584bd14a5ea9e2e806434ee06eea31ace62930b53f7e8c926e8536193d6500389b86f40fe4c7702b9fe06913d347c16f82a1e280c7bff381d2c0d46b14fb2d12
data/.config/rail.toml ADDED
@@ -0,0 +1,26 @@
1
+ [plan.work.ci]
2
+ paths = [".config/rail.toml", ".github/**", ".taplo.toml", "Makefile"]
3
+ scope = "repository"
4
+
5
+ [plan.work.ruby]
6
+ paths = [
7
+ ".rspec",
8
+ ".ruby-version",
9
+ ".standard.yml",
10
+ "Gemfile",
11
+ "Gemfile.lock",
12
+ "Rakefile",
13
+ "Steepfile",
14
+ "bin/**",
15
+ "examples/**",
16
+ "ext/prosody/extconf.rb",
17
+ "lib/**",
18
+ "prosody.gemspec",
19
+ "sig-private/**",
20
+ "sig/**",
21
+ "spec/**",
22
+ "steep_expectations.yml",
23
+ "typecheck/**",
24
+ "typecheck_negative/**",
25
+ ]
26
+ scope = "repository"
@@ -1,3 +1,3 @@
1
1
  {
2
- ".": "0.5.1"
2
+ ".": "0.6.0"
3
3
  }
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 3.3.7
1
+ 3.3.12
data/.taplo.toml CHANGED
@@ -1,4 +1,4 @@
1
- include = ["*.toml", ".cargo/*.toml", "ext/**/*.toml"]
1
+ include = ["*.toml", ".cargo/*.toml", ".config/*.toml", "ext/**/*.toml"]
2
2
 
3
3
  [formatting]
4
4
  indent_string = " "
data/AGENTS.md CHANGED
@@ -10,11 +10,11 @@ Ruby/Rust bridge in depth — read it before touching the extension.
10
10
 
11
11
  These come before everything else. Every change is judged against them.
12
12
 
13
- **Write code that is simple, clear, well-factored, elegant, easy to
14
- understand, correct, and idiomatic.** A reader should grasp the intent without
15
- effort. If a change makes the code harder to read, the change is wrong, even
16
- if it is faster or shorter. If two designs are correct, pick the one that is
17
- easier to delete.
13
+ **Write code that is simple, clear, well-factored, DRY, performant, elegant,
14
+ easy to understand, correct, and idiomatic.** A reader should grasp the intent
15
+ without effort. If a change makes the code harder to read, the change is
16
+ wrong, even if it is faster or shorter. If two designs are correct, pick the
17
+ one that is easier to delete.
18
18
 
19
19
  **Make invalid states unrepresentable in the type system.** When a compiler
20
20
  or type checker can prove a contract, no test, comment, or convention has to.
@@ -26,9 +26,10 @@ uncompilable, do that instead of writing a runtime check.
26
26
  **Delete more than you add.** Every change should leave the codebase smaller,
27
27
  simpler, or both. If you must add code, look first for duplication you can
28
28
  fold, abstractions that no longer pay rent, dead branches, and stale comments.
29
- The end-state diff should net negative whenever the task allows. Line count is
30
- not the only axis: plain duplicated arms often read better than generic
31
- machinery.
29
+ The end-state diff should net negative whenever the task allows. Each added
30
+ line must be inherent to the problem, not incidental to the solution. Line
31
+ count is not the only axis: plain duplicated arms often read better than
32
+ generic machinery.
32
33
 
33
34
  **Identify, document, and enforce invariants.** For every load-bearing piece
34
35
  of state: name the invariant, write it down near the type or function that
@@ -40,6 +41,15 @@ not yet understand the code well enough to change it.
40
41
  encouraged when they are scoped to the area you are already touching. Do not
41
42
  sprawl — but do not walk past obvious cleanup either.
42
43
 
44
+ **Do not break users without a reason.** The published package has real
45
+ users. A release can break them only for a clear improvement that the
46
+ owner decided on: a fixed defect, an invalid state that the types now
47
+ prevent, or a need of a new feature. "Nothing in this repo calls it" is
48
+ not a reason to remove, rename, or narrow a public member, an error
49
+ class, a log text, or a dependency floor. When you are not sure, keep
50
+ the old shape. List each break under `## Breaking changes` in the PR
51
+ body.
52
+
43
53
  ## Definition of Done
44
54
 
45
55
  No change is complete until every line below holds. These are acts, not
@@ -60,7 +70,8 @@ aspirations — perform each one; do not merely agree with it:
60
70
  vocabulary (see Redesign hygiene). "The new thing works" is half done.
61
71
  7. Every claim written this session — doc cross-reference, "covered by" note,
62
72
  exemplar path — was verified to resolve, not recalled from memory.
63
- 8. The diff is net-negative, or each addition is individually justified.
73
+ 8. The diff is net-negative, or each addition is individually justified as
74
+ inherent to the problem.
64
75
 
65
76
  ## Development Setup
66
77
 
@@ -216,6 +227,8 @@ half-deleted designs are where bloat and bug re-introduction live:
216
227
 
217
228
  **Style:**
218
229
 
230
+ - Structure function bodies as logical paragraphs. Keep statements for one
231
+ operation together. Add a blank line before a new concept or operation.
219
232
  - Prefer `use` statements over fully qualified prefixes
220
233
  - Methods without `self` should be functions (except `new` and similar)
221
234
  - Ask before large structural changes
@@ -295,12 +308,13 @@ Constants → Statics → Types → Implementations → Functions → Errors (bo
295
308
 
296
309
  - `sig/` — public RBS signatures; `sig-private/` — internal signatures.
297
310
  Every API change updates the signatures in the same commit.
298
- - `lib/prosody/native_stubs.rb` — documented Ruby stubs for the classes and
299
- methods the Rust extension implements. Editors and documentation tools read
300
- them; the runtime does not (the native extension provides the real
301
- definitions), and Steep ignores the file. When the extension's public
302
- surface changes, update the matching stub and its YARD doc in the same
303
- commit.
311
+ - Public API docs are YARD comments on the public Ruby classes. A public
312
+ class that the Rust extension implements (`Client`, `Context`, `Message`,
313
+ `Timer`) keeps a documented stub in `lib/prosody/native_stubs/`. Do not
314
+ write stubs for internal native classes or private methods; `sig/` and
315
+ `sig-private/` type them. The runtime does not load the stubs, and Steep
316
+ ignores them. When a public native method changes, update its stub in the
317
+ same commit.
304
318
  - Steep targets (`Steepfile`): `lib` checks the implementation;
305
319
  `consumer_types` (`typecheck/`) verifies a payload type flows through the
306
320
  public API; `typed_examples` checks every runnable example;
data/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.6.0](https://github.com/prosody-events/prosody-rb/compare/prosody/v0.5.1...prosody/v0.6.0) (2026-10-02)
4
+
5
+
6
+ ### Features
7
+
8
+ * **deps:** upgrade prosody to 0.7.0 ([#51](https://github.com/prosody-events/prosody-rb/issues/51)) ([c9f265c](https://github.com/prosody-events/prosody-rb/commit/c9f265c7c5353c989886d2333afca2142e06763c))
9
+
3
10
  ## [0.5.1](https://github.com/prosody-events/prosody-rb/compare/prosody/v0.5.0...prosody/v0.5.1) (2026-08-20)
4
11
 
5
12
 
data/CONFIGURATION.md CHANGED
@@ -10,12 +10,14 @@ The Ruby client reports values it cannot convert to Prosody types. Prosody valid
10
10
  |-----------------------------------------|---------------------------------------------------|--------------|
11
11
  | `bootstrap_servers` / `PROSODY_BOOTSTRAP_SERVERS` | Kafka servers to connect to | - |
12
12
  | `group_id` / `PROSODY_GROUP_ID` | Consumer group name | - |
13
- | `subscribed_topics` / `PROSODY_SUBSCRIBED_TOPICS` | Topics to read from | - |
13
+ | `subscribed_topics` / `PROSODY_SUBSCRIBED_TOPICS` | Topics to read from; a client that only reads published state needs none | - |
14
14
  | `allowed_events` / `PROSODY_ALLOWED_EVENTS` | Only process events matching these prefixes | (all) |
15
15
  | `source_system` / `PROSODY_SOURCE_SYSTEM` | Tag for outgoing messages (prevents reprocessing)| `<group_id>` |
16
16
  | `mock` / `PROSODY_MOCK` | Use in-memory Kafka for testing | false |
17
17
  | `mode` / - | Processing mode: `pipeline`, `low_latency`, or `best_effort` | `pipeline` |
18
- | - / `PROSODY_LOG` | Rust log filter, such as `info` or `prosody=debug` | `info` |
18
+ | - / `PROSODY_LOG` | Rust log filter, such as `info` or `prosody=debug` | `info`, with `warn` for `scylla` and `opentelemetry` |
19
+
20
+ `PROSODY_LOG` directives apply on top of the defaults above. A value that names only targets, such as `prosody=debug`, keeps other targets at `info`. Set `PROSODY_LOG=opentelemetry=info` to restore the OpenTelemetry info events.
19
21
 
20
22
  ## Requests
21
23
 
@@ -45,9 +47,9 @@ Set `subsystem` to make this client answer requests. Without it, the client cons
45
47
  | `shutdown_timeout` / `PROSODY_SHUTDOWN_TIMEOUT` | Shutdown budget; handlers run freely until cancellation fires near the end of the timeout | 30s |
46
48
  | `stall_threshold` / `PROSODY_STALL_THRESHOLD` | Report unhealthy if no progress for this long | 5m |
47
49
  | `probe_port` / `PROSODY_PROBE_PORT` | HTTP port for health checks; use `false`, `:disabled`, or the environment value `none` to disable | 8000 |
48
- | - / `PROSODY_STATISTICS_INTERVAL` | How often librdkafka reports client statistics; must be between 1ms and 24h | 5s |
50
+ | `statistics_interval` / `PROSODY_STATISTICS_INTERVAL` | How often librdkafka reports client statistics; must be between 1ms and 24h | 5s |
49
51
  | `failure_topic` / `PROSODY_FAILURE_TOPIC` | Send unprocessable messages here (dead letter queue) | - |
50
- | `idempotence_cache_size` / `PROSODY_IDEMPOTENCE_CACHE_SIZE` | Global shared cache capacity across all partitions for message deduplication. Consumer deduplication is mandatory and cannot be disabled, so this must be at least 1; setting it to 0 in the client configuration is rejected | 8192 |
52
+ | `idempotence_cache_size` / `PROSODY_IDEMPOTENCE_CACHE_SIZE` | Capacity of the producer idempotence cache and of the consumer deduplication cache. Consumer deduplication cannot be turned off, so the value must be at least 1 | 8192 |
51
53
  | `idempotence_version` / `PROSODY_IDEMPOTENCE_VERSION` | Version string for cache-busting dedup hashes | 1 |
52
54
  | `idempotence_ttl` / `PROSODY_IDEMPOTENCE_TTL` | TTL for dedup records in Cassandra | 7d (604800 seconds) |
53
55
  | `slab_size` / `PROSODY_SLAB_SIZE` | Timer storage granularity (rarely needs changing) | 1h |
@@ -141,27 +143,28 @@ Register keyed-state collections before you subscribe. Persistence is backed by
141
143
  | Option / Environment Variable | Description | Default |
142
144
  |-------------------------------|-------------|---------|
143
145
  | `state_collections` / - | Keyed-state collections to register before subscribe (array of definitions or config hashes; duplicate names rejected) | (none) |
144
- | `subsystem` / `PROSODY_SUBSYSTEM` | Subsystem name used to advertise JSON collections whose definitions set `published: true` | (none) |
145
- | `state_cache_dir` / `PROSODY_STATE_CACHE_DIR` | Disk workspace for the local keyed-state cache; each live client needs its own directory. Set a mounted path in production | per-client temp dir |
146
+ | `subsystem` / `PROSODY_SUBSYSTEM` | Subsystem name used to advertise JSON and set collections whose definitions set `published: true` | (none) |
147
+ | `state_cache_dir` / `PROSODY_STATE_CACHE_DIR` | Directory for the local keyed-state caches. Each consumer opens its cache in a new subdirectory and removes it when the consumer stops, so clients can share the directory. Set a mounted path in production | `<temp>/prosody/keyed-state` |
146
148
  | `state_owned_cache_size` / `PROSODY_STATE_OWNED_CACHE_SIZE` | Capacity of the owning keyed-state cache; accepts sizes such as `64 MiB` or `500 MB` | storage-engine default |
149
+ | `state_memtable_size` / `PROSODY_STATE_MEMTABLE_SIZE` | Bytes of in-memory writes the local keyed-state cache holds for each assigned partition before it flushes them to disk; accepts sizes such as `16 MiB`. Memory use scales with the number of assigned partitions | storage-engine default of 64 MiB |
147
150
  | `state_read_cache_size` / `PROSODY_STATE_READ_CACHE_SIZE` | Capacity of the published-state read cache; accepts sizes such as `1 MiB` | `state_owned_cache_size` or `PROSODY_STATE_OWNED_CACHE_SIZE` when set; otherwise 1 MiB |
148
151
  | `state_read_cache` / `PROSODY_STATE_READ_CACHE_TTL` | Default published-read cache TTL. Use `false` or the environment value `none` to bypass the cache | 5s |
149
- | `state_recovery_delay` / `PROSODY_STATE_RECOVERY_DELAY` | Whole-second delay between staging a provisional cell and the recovery sweep; every collection TTL must strictly exceed it | 30s |
150
-
151
- Prefer the definition constructors from the [API reference](README.md#api-reference). They serialize into `state_collections`, so you can reuse the same object with `context.state`. Each entry has these fields:
152
152
 
153
153
  Published collections require `subsystem`. Keep it configured for one deployment after removing `published: true` so readers can observe the collection's retirement.
154
154
 
155
+ Prefer the definition constructors from the [API reference](README.md#api-reference). They serialize into `state_collections`, so you can reuse the same object with `context.state`. Each entry has these fields:
156
+
155
157
  | Field | Description | Default |
156
158
  |-------|-------------|---------|
157
159
  | `name` | Collection name; non-empty and unique within the client | (required) |
158
- | `kind` | `"value"`, `"map"`, or `"deque"` | (required) |
159
- | `payload` | `"json"` (JSON values) or `"message"` (the full Kafka message the handler received) | (required) |
160
- | `ttl_seconds` | Per-write TTL in whole seconds (at least 1; must exceed the recovery delay) | (none) |
160
+ | `kind` | `"value"`, `"map"`, `"set"`, or `"deque"` | (required) |
161
+ | `payload` | `"json"` (JSON values) or `"message"` (the full Kafka message the handler received). Omit it for a set, which stores membership only | (required for a value, map, or deque) |
162
+ | `ttl_seconds` | Per-write TTL in whole seconds (at least 1) | (none) |
161
163
  | `read_uncommitted` | Opt out of transactional staging | false |
162
- | `published` | Allow read-only access from other consumer groups; JSON collections only | false |
163
- | `read_cache` | Published-read cache override: a positive duration, `false`, or inherit when omitted | inherit |
164
- | `keyset_limit` | Map-only; ordered-scan bound in `0..=4096` (`0` disables ordered-scan tracking) | 128 |
164
+ | `published` | Allow read-only access from other consumer groups; JSON and set collections only | false |
165
+ | `keyset_limit` | Map and set only; ordered-scan bound in `0..=4096` (`0` disables ordered-scan tracking) | 128 |
165
166
  | `capacity` | Deque-only window bound (at least 1); keeps at most N slots, enforced lazily on push. Runtime-only and mutable across deploys — not persisted | unbounded |
166
167
 
167
- Constructors set these via keyword arguments (`ttl:`, `keyset_limit:`, `capacity:`, `read_uncommitted:`, `published:`, `read_cache:`). `read_cache` is a positive duration in seconds, `false` to bypass the cache, or `nil` to inherit the client default.
168
+ Constructors set these via keyword arguments (`ttl:`, `keyset_limit:`, `capacity:`, `read_uncommitted:`, `published:`).
169
+
170
+ A JSON or set constructor also takes `read_cache:`. It is not a registration field: it applies only to the readers that `client.state` opens with the definition. `read_cache` is a positive duration in seconds, `false` to bypass the cache, or `nil` to inherit the client default.