snerdmq 0.3.2 → 0.3.5

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 (4) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +106 -9
  3. data/snerdmq.gemspec +1 -1
  4. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e1d73035fba1f4d5bd42c382bc5c8798db8597ddbbe2829cf6fd9323747751e7
4
- data.tar.gz: 42b2633c35e968f1640a603b4d4ab59de9cdf7257f9b045c53af7cd97ba68c21
3
+ metadata.gz: 49f0d03ac996ffe2018f862d5a82f0a73fec5281a1d1097727ad369f4a112ab2
4
+ data.tar.gz: 1ac5125ecef4e6d40cbcc4103b96aa18a6d002c1ee7371135720df6fc4370f20
5
5
  SHA512:
6
- metadata.gz: 11a0de6095c802f2a6aa85d0ca5ce4af45b5be5f8763212e10f74608b9ff3a79cdc71929e5937ee8961076f9094124f19321ac9bf9ae78b42133ba86ffbffcbc
7
- data.tar.gz: ba85ccfeff123d455ea3694709ca2a7825ab535fc70b6963d29c6d6486a9126b38f0b2d868afd5d0e05964dc9d143fa7a8b9efb3499f7b79e0df383f3f945b0b
6
+ metadata.gz: 4de56bd72675519a68bc92e81553cc3a3033fa9f9e06786dcf3b2b79b79d83741730fd91dce142ff9230c64fa090b5ba8c0a65acbfeb4299f5126a3b61d3fb6e
7
+ data.tar.gz: 67987b9a61af61b50606577063103b7e6ca5feb3398dd0946e22b85396d40276ed80c2d78e66a73a1670a327af043e1e072e1c02f1e07263de8e6d25d63449cb
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  <div align="center">
2
2
  <img src="./assets/Designer-9.png" height="120" alt="SnerdMQ Ruby Logo" />
3
- <h1>💎 SnerdMQ Ruby SDK v0.3.2</h1>
3
+ <h1>💎 SnerdMQ Ruby SDK v0.3.5</h1>
4
4
  <p>A zero-config, C-speed background job queue for Ruby. Ditch Redis and Sidekiq for lightweight, persistent background jobs.</p>
5
5
 
6
6
  [![Gem Version](https://badge.fury.io/rb/snerdmq.svg)](https://badge.fury.io/rb/snerdmq)
@@ -9,7 +9,7 @@
9
9
 
10
10
  This is the official Ruby SDK wrapper for **SnerdMQ**. It handles all JSON-RPC communication and `IO.popen` orchestration so you can write lightning-fast background jobs without managing any external databases like Redis or Postgres.
11
11
 
12
- ## ✨ v0.3.2 AI Features
12
+ ## ✨ v0.3.5 AI Features
13
13
  - **Smart API Rate-Limiting**: Natively tracks `rate_limit_group` execution velocity to prevent 429 "Too Many Requests" API errors.
14
14
  - **Payload-Hashing Deduplication**: Automatically computes cryptographic hashes to drop duplicate tasks instantly.
15
15
  - **Dynamic Float Prioritization**: A native Binary Max-Heap bypasses standard FIFO rules for high urgency tasks.
@@ -18,7 +18,7 @@ This is the official Ruby SDK wrapper for **SnerdMQ**. It handles all JSON-RPC c
18
18
  - **Zero Rust Required**: Our gem installation script automatically downloads the pre-compiled C-speed Rust binary for your OS.
19
19
  - **Thread Safe**: Uses native Ruby `Thread`s and `Mutex` locks to orchestrate I/O without blocking your main event loop.
20
20
 
21
- ### ⚙️ Advanced Task Configuration (v0.3.2)
21
+ ### ⚙️ Advanced Task Configuration (v0.3.5)
22
22
  To power complex AI workflows, tasks can now be configured with advanced orchestration parameters:
23
23
 
24
24
  * **`auto_dedupe` (`true/false`)**: If set to `true`, the daemon computes a cryptographic hash of the `task_type` and `data`. If an identical payload is currently sitting in the queue pending execution, this new task is silently dropped. Excellent for preventing duplicate generative AI requests from trigger-happy users!
@@ -167,18 +167,115 @@ end
167
167
 
168
168
  ---
169
169
 
170
- ## 🌍 Advanced: Distributed Scaling
170
+ ## 🧩 Queue Topology: One Queue or Many?
171
171
 
172
- By default, the SDK spins up the Rust daemon which writes the queue to a local file (`.snerdata/tasks/tasks.log`).
172
+ ### Recommended: one queue, all job types (singleton)
173
173
 
174
- If you have multiple Ruby microservices (or Rails instances) running behind a load balancer and want them to share the exact same queue, simply mount a **Shared Network Drive** (like AWS EFS or NFS) to all of your servers and pass the shared path:
174
+ Each `Snerdmq::SnerdQueue` client spawns its own Rust daemon and **exclusively owns** its storage directory (`.snerdata` by default). The recommended pattern is **one client per application process**: register every job type on it and serve a single shared dashboard:
175
175
 
176
176
  ```ruby
177
177
  require 'snerdmq'
178
178
 
179
- # All of your Ruby servers point to the exact same shared file!
180
- # SnerdMQ's native OS file-locking guarantees zero data corruption.
181
- queue = Snerdmq::SnerdQueue.new(storage_path: "/mnt/aws-efs-shared-drive/snerd_tasks.log")
179
+ # ONE queue client for the whole app
180
+ queue = Snerdmq::SnerdQueue.new
181
+
182
+ # Job type #1: image processing
183
+ queue.register_handler("process_image") do |data|
184
+ puts "Processing image: #{data['image_id']}"
185
+ end
186
+
187
+ # Job type #2: OTP emails — same queue, same daemon
188
+ queue.register_handler("send_otp_email") do |data|
189
+ puts "Sending OTP to: #{data['to']}"
190
+ end
191
+
192
+ queue.start_listening
193
+
194
+ # Both job types flow through the exact same queue
195
+ queue.enqueue(task_id: "img-1", task_type: "process_image", data: { "image_id" => "abc123" }, max_retries: 3, retry_after_hours: 0.5)
196
+ queue.enqueue(task_id: "otp-1", task_type: "send_otp_email", data: { "to" => "john@wick.com" }, max_retries: 3, retry_after_hours: 0.5)
197
+
198
+ # ONE dashboard shows every job type
199
+ queue.start_dashboard(port: 8080)
200
+ ```
201
+
202
+ All job types share everything: the same persistent job log, retry/DLQ pipeline, rate-limit state, stats — and one dashboard at `http://localhost:8080` showing all of them.
203
+
204
+ ### 🚫 Same storage twice = fails fast
205
+
206
+ The daemon takes an **exclusive OS-level lock** on its storage directory at startup. A second client on the same storage fails instead of silently double-executing your jobs:
207
+
208
+ ```ruby
209
+ first = Snerdmq::SnerdQueue.new # ✅ owns .snerdata
210
+ second = Snerdmq::SnerdQueue.new # ❌ daemon refuses to start:
211
+ # "Another daemon is already running on storage '.snerdata'"
212
+ ```
213
+
214
+ This applies across processes too — with **Puma/Unicorn clustered workers, every worker is a separate process** that spawns its own daemon, so each worker needs its own `storage_path` (or run a single dedicated worker process for jobs).
215
+
216
+ ### 🔀 Need multiple queues? Give each one its own storage
217
+
218
+ ```ruby
219
+ images = Snerdmq::SnerdQueue.new(storage_path: ".snerdata-images")
220
+ emails = Snerdmq::SnerdQueue.new(storage_path: ".snerdata-emails")
221
+
222
+ images.start_dashboard(port: 8080) # separate dashboards, so separate ports
223
+ emails.start_dashboard(port: 8081)
182
224
  ```
183
225
 
226
+ Now you have two fully independent engines: separate job logs, separate rate-limit state, separate dashboards. Only split when you actually need isolation (different teams, different retention, independent monitoring) — otherwise the singleton is simpler and recommended.
227
+
228
+ ---
229
+
230
+ ## 🌍 Advanced: Distributed Scaling
231
+
232
+ Because the daemon exclusively locks its storage directory, scaling horizontally means **one queue per server**, each with its own storage. Your load balancer routes requests across servers, and every server processes the jobs it enqueued:
233
+
234
+ ```ruby
235
+ require 'snerdmq'
236
+
237
+ # Each server runs its own daemon on its own storage dir (local disk works fine)
238
+ queue = Snerdmq::SnerdQueue.new(storage_path: "/var/data/snerd") # per-server storage
239
+ ```
240
+
241
+ A shared network drive (AWS EFS or NFS) is still a good home for that storage when a single instance needs durable state — e.g. a container that restarts but must keep its queue. Native OS file locking (`flock`) keeps writes safe — no Redis required.
242
+
184
243
  *Built with ❤️ for John Wick tier engineering.*
244
+
245
+
246
+ ## Architecture Best Practices
247
+
248
+ When building production applications with SnerdMQ, it is recommended to initialize the queue as a Singleton, isolate your domain workers into separate files/functions, use Dead Letter Queues (DLQ) for failed tasks via `RegisterMaxRetryHandler`, and ensure manual graceful shutdown. The embedded Dashboard UI can also be easily served from the same instance.
249
+
250
+ ```ruby
251
+ require 'snerdmq'
252
+
253
+ queue = SnerdQueue.new(storage_path: "./.snerdata")
254
+
255
+ def init_email_workers(queue)
256
+ queue.register_handler('send_email') do |data|
257
+ puts "Sending email to #{data['email']}..."
258
+ end
259
+
260
+ queue.register_max_retry_handler('send_email') do |data|
261
+ puts "Email to #{data['email']} failed permanently. Dead letter processing..."
262
+ end
263
+ end
264
+
265
+ def init_image_workers(queue)
266
+ queue.register_handler('process_image') do |data|
267
+ puts "Processing image #{data['imageId']}..."
268
+ end
269
+ end
270
+
271
+ init_email_workers(queue)
272
+ init_image_workers(queue)
273
+
274
+ queue.start_dashboard(8080)
275
+
276
+ # Trap signals for graceful shutdown
277
+ trap('INT') { queue.shutdown; exit }
278
+ trap('TERM') { queue.shutdown; exit }
279
+
280
+ queue.start_listening
281
+ ```
data/snerdmq.gemspec CHANGED
@@ -3,7 +3,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
3
 
4
4
  Gem::Specification.new do |spec|
5
5
  spec.name = "snerdmq"
6
- spec.version = "0.3.2"
6
+ spec.version = "0.3.5"
7
7
  spec.authors = ["Greyhands2"]
8
8
  spec.email = ["developer@example.com"]
9
9
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: snerdmq
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.2
4
+ version: 0.3.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Greyhands2
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-08-18 00:00:00.000000000 Z
11
+ date: 2026-09-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack