solid-redis 0.1.0 → 0.1.1

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 +172 -0
  3. data/lib/solid_redis/version.rb +1 -1
  4. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a4bfba5a15f75c77fa0c5a9d2a85a0d5d8b50ebada6fbd5e1895e56e47b5813a
4
- data.tar.gz: 842b205f8ba0b9298451f24bec08c0d76542e6b46026b27539853d35c06fce54
3
+ metadata.gz: e9cdb565b2c42299ce1e286ac7eb07dfdaa4fadbe91ad8eff2008a730d14b3f8
4
+ data.tar.gz: dbc14ce529f4d359eabe77b476a56687e637aa94e5953210e40cef7da65bad5a
5
5
  SHA512:
6
- metadata.gz: 8a6afa66bb51a4723939441be103372efd6260c670dd8e68ca81d788cc2430bc732221c2b7b930a35365d207b1e095a654ecbd88bac715c08bc8b6f86028872d
7
- data.tar.gz: c010108671dfc8c343b213fde778afc1e9c3e2750051629ae585d2857037d8da1dd455ff2dba974b78098f014a7c25c495f30d4b1fc88b2b9ab2bf236bd0bca6
6
+ metadata.gz: 9e1ce93fc017d9651eff97bfaf642e34dc6c090ea1c7708eeade64bf6a45235444c977cf4a18633bd3850977f9da67bcc2295e2635609401407fd2bf1c78c882
7
+ data.tar.gz: 22058737facf0f458a049e0228fd87b3a4810efd107a6bcc949fd41751a0736aa959028e1102abbe56eba342560edaab3099341b5d120bb7da82421603f6837b
data/README.md CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  [![Build Status](https://github.com/nicolasva/solid-redis/actions/workflows/ci.yml/badge.svg)](https://github.com/nicolasva/solid-redis/actions/workflows/ci.yml)
4
4
  [![Gem Version](https://badge.fury.io/rb/solid-redis.svg)](https://rubygems.org/gems/solid-redis)
5
+ [![Downloads](https://img.shields.io/gem/dt/solid-redis.svg)](https://rubygems.org/gems/solid-redis)
5
6
  [![Documentation Status](https://img.shields.io/badge/docs-RubyDoc.info-blue.svg)](https://www.rubydoc.info/gems/solid-redis)
6
7
 
7
8
  `solid-redis` is a dependency-free Redis client designed around Ractor
@@ -242,6 +243,177 @@ Method registration is required for a Ractor-shareable callback collection.
242
243
  Block callbacks retain mutable lexical context and are therefore rejected.
243
244
  Callback exceptions propagate to the caller.
244
245
 
246
+ ## Examples
247
+
248
+ ### Parallel job workers, one Ractor each
249
+
250
+ Every worker receives the same shareable Sentinel specification and builds
251
+ its own pool. Nothing but the specification and plain data crosses the
252
+ Ractor boundary.
253
+
254
+ ```ruby
255
+ SENTINEL = SolidRedis.sentinel(
256
+ name: "mymaster",
257
+ sentinels: ["redis://sentinel-1:26379", "redis://sentinel-2:26379"],
258
+ password: ENV.fetch("REDIS_PASSWORD"),
259
+ timeout: 1.0
260
+ )
261
+
262
+ workers = 4.times.map do |index|
263
+ Ractor.new(SENTINEL, index) do |sentinel, worker_id|
264
+ pool = sentinel.new_pool(size: 2)
265
+ processed = 0
266
+
267
+ while (job = pool.call("RPOP", "jobs"))
268
+ pool.call("HINCRBY", "stats", "worker:#{worker_id}", 1)
269
+ processed += 1
270
+ end
271
+
272
+ processed
273
+ ensure
274
+ pool&.close
275
+ end
276
+ end
277
+
278
+ workers.sum(&:value) # total processed jobs (use &:take on Ruby 3.x)
279
+ ```
280
+
281
+ ### Threads sharing a pool inside one Ractor
282
+
283
+ Threads within a Ractor may share a pool. On CRuby < 4.0 keep all threads in
284
+ a single Ractor (see the limitation below).
285
+
286
+ ```ruby
287
+ pool = SolidRedis.config(url: "redis://localhost:6379").new_pool(size: 8)
288
+
289
+ threads = 20.times.map do |i|
290
+ Thread.new { pool.call("SET", "key:#{i}", i) }
291
+ end
292
+ threads.each(&:join)
293
+
294
+ pool.call("DBSIZE") # => 20
295
+ pool.close
296
+ ```
297
+
298
+ ### Pipelines and error handling
299
+
300
+ `call` raises `SolidRedis::CommandError` on a Redis error reply. A pipeline
301
+ sends all commands in one round trip, reads every reply, and then raises the
302
+ first `CommandError` if any; the whole pipeline succeeds or raises.
303
+
304
+ ```ruby
305
+ client = SolidRedis.config(url: "redis://localhost:6379").new_client
306
+
307
+ begin
308
+ client.call("INCR", "not-a-number")
309
+ rescue SolidRedis::CommandError => error
310
+ error.message # => "ERR value is not an integer or out of range"
311
+ end
312
+
313
+ client.pipelined do |pipeline|
314
+ pipeline.call("SET", "counter", 1)
315
+ pipeline.call("INCR", "counter")
316
+ pipeline.call("GET", "counter")
317
+ end
318
+ # => ["OK", 2, "2"]
319
+
320
+ begin
321
+ client.pipelined do |pipeline|
322
+ pipeline.call("SET", "counter", "abc")
323
+ pipeline.call("INCR", "counter") # fails; the SET was still applied
324
+ end
325
+ rescue SolidRedis::CommandError => error
326
+ error.message # => "ERR value is not an integer or out of range"
327
+ end
328
+ ```
329
+
330
+ ### Building commands dynamically
331
+
332
+ `call_v` accepts an array, which is convenient for variadic commands.
333
+
334
+ ```ruby
335
+ fields = { "name" => "Ada", "language" => "Ruby" }
336
+ client.call_v(["HSET", "user:1", *fields.flatten])
337
+ client.call("HGETALL", "user:1")
338
+ # => { "name" => "Ada", "language" => "Ruby" } with RESP3
339
+ # => ["name", "Ada", "language", "Ruby"] with RESP2
340
+ ```
341
+
342
+ ### Reading from replicas
343
+
344
+ Use `role: :replica` for read-only traffic and keep a separate `:master`
345
+ specification for writes. Both are shareable and resolve independently.
346
+
347
+ ```ruby
348
+ WRITER = SolidRedis.sentinel(name: "mymaster", sentinels: SENTINELS, role: :master)
349
+ READER = SolidRedis.sentinel(name: "mymaster", sentinels: SENTINELS, role: :replica)
350
+
351
+ Ractor.new(WRITER, READER) do |writer, reader|
352
+ writer.new_client.call("SET", "greeting", "hello")
353
+ reader.new_client.call("GET", "greeting") # after replication
354
+ end
355
+ ```
356
+
357
+ ### Observing failover
358
+
359
+ After a connection error the Ractor's cached target is dropped and the next
360
+ attempt asks Sentinel again. Register a callback to trace it.
361
+
362
+ ```ruby
363
+ module FailoverLog
364
+ def self.connection_error(type, message) = warn("[redis] #{type}: #{message}")
365
+ def self.resolved(name, url) = warn("[redis] #{name} -> #{url}")
366
+ end
367
+
368
+ callbacks = CallbackCollection.new do |collection|
369
+ collection.register(:connection_error, FailoverLog)
370
+ collection.register(:resolved, FailoverLog)
371
+ end
372
+
373
+ sentinel = SolidRedis.sentinel(
374
+ name: "mymaster",
375
+ sentinels: SENTINELS,
376
+ reconnect_attempts: 2,
377
+ callbacks: callbacks
378
+ )
379
+
380
+ client = sentinel.new_client
381
+ client.call("PING") # [redis] mymaster -> redis://10.0.0.15:6379
382
+ # ... master goes down, Sentinel promotes a replica ...
383
+ client.call("PING") # [redis] ConnectionError: Connection reset by peer
384
+ # [redis] mymaster -> redis://10.0.0.16:6379
385
+ ```
386
+
387
+ ### Strict at-most-once delivery
388
+
389
+ Retries after a connection error may replay a command. Disable them for
390
+ non-idempotent operations and handle the error yourself.
391
+
392
+ ```ruby
393
+ config = SolidRedis.config(url: "redis://localhost:6379", reconnect_attempts: 0)
394
+ client = config.new_client
395
+
396
+ begin
397
+ client.call("LPUSH", "payments", payment_id)
398
+ rescue SolidRedis::ConnectionError
399
+ # Nothing was retried; decide whether to re-enqueue.
400
+ end
401
+ ```
402
+
403
+ ### Unix socket and TLS
404
+
405
+ ```ruby
406
+ SolidRedis.config(url: "unix:///var/run/redis/redis.sock", db: 2)
407
+
408
+ SolidRedis.config(
409
+ url: "rediss://redis.example:6380",
410
+ ssl_params: {
411
+ verify_mode: OpenSSL::SSL::VERIFY_PEER,
412
+ ca_file: "/etc/ssl/certs/redis-ca.pem"
413
+ }
414
+ )
415
+ ```
416
+
245
417
  ## Semantics and current scope
246
418
 
247
419
  - Clients, pools, sockets, mutexes, and Sentinel runtime state are never
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SolidRedis
4
- VERSION = "0.1.0"
4
+ VERSION = "0.1.1"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: solid-redis
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nicolas Vandenbogaerde
@@ -95,7 +95,7 @@ licenses:
95
95
  - MIT
96
96
  metadata:
97
97
  rubygems_mfa_required: 'true'
98
- documentation_uri: https://www.rubydoc.info/gems/solid-redis/0.1.0
98
+ documentation_uri: https://www.rubydoc.info/gems/solid-redis/0.1.1
99
99
  source_code_uri: https://github.com/nicolasva/solid-redis
100
100
  rdoc_options: []
101
101
  require_paths: