Async-Redis

 view release on metacpan or  search on metacpan

Changes  view on Meta::CPAN

        - Adds Future::Selector 0.05 as a runtime dependency.
    - Security: TLS identity enforced by default
        - SSL_hostname and SSL_verifycn_name set when connecting by
          hostname. IP literals verify against IP SAN by default (fails
          if the cert has no IP SAN for the connected address). New
          verify_hostname option for opt-out when connecting by IP to a
          hostname-only cert.
    - Security: failed handshake no longer leaves object connected
        - connect() rolls back (reset + typed error) on AUTH/SELECT
          failure
        - Password "0" is now sent correctly (was silently skipped by
          truthy guard)
    - Correctness: blocking command deadlines
        - BLMPOP/BZMPOP read timeout from position 0 (was last)
        - Server timeout 0 means indefinite (no client-side deadline)
        - WAIT, WAITAOF, XREAD BLOCK, XREADGROUP BLOCK all covered
    - Correctness: subscription lifecycle
        - _close (intentional) distinct from _fail_fatal (unrecoverable);
          iterator next() returns undef vs typed error accordingly
        - Reconnect uses _pause_for_reconnect / _resume_after_reconnect
          verbs that preserve replay state; _resume_after_reconnect
          sets in_pubsub=1 before replay to mirror initial-subscribe
          timing
        - Identity-guarded parent-slot clearing prevents stale _close
          from wiping a newer subscription
    - Correctness: pool release and shutdown
        - Double-release is a loud no-op (was silent double-push into
          idle)
        - Shutdown flag rejects further acquires; active releases are
          destroyed
        - refaddr-based identity (was stringification)
        - release(undef) is a silent no-op
    - Correctness: timeout and reset invariant
        - _await_with_deadline non-throwing helper replaces the
          wait_any + throwing await pattern that silently skipped
          timeout cleanup
        - _reader_fatal detaches inflight before closing the socket so
          the typed error is preserved; idempotent via
          _fatal_in_progress guard under eval+finally
    - Correctness: transaction state cleanup
        - watch() and multi_start() now set their state flags only
          after the underlying command succeeds (previously set the
          flag, then awaited; a failed command left the client claiming
          to be in MULTI/WATCH state)
        - watch_multi() unwinds WATCH on a callback die (previously a
          callback exception left the connection holding watches; the
          next command on the client would hit a poisoned state)
        - DISCARD now correctly clears the watching flag (Redis DISCARD
          drops watches; the client previously claimed to still be
          watching)
    - Breaking: removed `install` option from define_command
        - The option used to install the script as a method on the
          Async::Redis class. Pass `install => 1` now dies with a clear
          message pointing callers at run_script(); use that or hold
          the returned Async::Redis::Script directly.
    - Privacy: OTel command arguments no longer in spans by default
        - otel_include_args now defaults to 0; pass 1 to re-enable
    - Added: message_queue_depth constructor option (default 1)
    - Added: key prefixing for PFADD, PFCOUNT, PFMERGE, GETBIT, SETBIT,
      BITCOUNT, BITPOS, HSTRLEN, ZMSCORE
    - Added: Async::Redis::Cookbook (POD) — runnable, tested recipes
      for connection management, pipelines, transactions, pubsub,
      pool, scripts, and observability.
    - Added: examples/ — async job queue, bulk insert, and a stress
      harness (examples/stress/) that drives all major features under
      load with chaos injection (CLIENT KILL) and integrity verification.
    - Added: GitHub Actions CI workflow (thanks @GaNardelli, PR #4)
      runs the full test suite with RELEASE_TESTING=1 against a
      Dockerized Redis on every push and pull request.
    - Documentation: TASK LIFECYCLE POD section explaining the
      Future::Selector contract; user-disconnect vs reader-fatal path
      distinction; broad POD review and corrections across the public
      surface.

0.001008  2026-04-22
    - Bug Fix: _reconnect no longer spins forever on permanent failure
        - New reconnect_max_attempts constructor option (default 10;
          0 = unlimited). Once exceeded, _reconnect dies with an
          Async::Redis::Error::Disconnected; the failure propagates
          through _reconnect_pubsub to the Subscription's read loop,
          where it routes to on_fail / on_error per existing contract.
        - Previously, an unreachable Redis would cause _reconnect to
          loop with exponential backoff (capped at reconnect_delay_max
          = 60s) indefinitely, with no way for a consumer to tell
          "reconnecting" from "broken".
    - Bug Fix: _reconnect_attempt resets to 0 on successful reconnect
        - Previously the counter only incremented, so cumulative
          reconnects across a long-running process caused subsequent
          backoff delays to start from an ever-larger base
          (0.1 * 2^N where N was total historical reconnects). Now
          each successful reconnect resets the counter to 0.

0.001007  2026-04-21
    - Feature: Callback-driven Subscription delivery
        - New on_message($cb) and on_error($cb) setters on
          Async::Redis::Subscription, alongside the existing
          on_reconnect($cb)
        - Callback signature is ($sub, $msg) / ($sub, $err), consistent
          with on_reconnect
        - Message hashref shape matches next(): type, channel, pattern
          (always present, undef for non-pmessage), data
        - Synchronous by default; return a Future from the callback for
          opt-in backpressure (driver awaits it before reading the next
          frame; failed Futures route to on_error)
        - Once on_message is set, next() croaks — sticky callback mode
          for the lifetime of the subscription
        - Default on_error behavior is to die loudly to prevent silent
          zombie subscriptions; register an explicit no-op to swallow
        - Designed for fire-and-forget listeners (channel-layer
          middleware, websocket gateways, background dispatchers) that
          trigger Future::AsyncAwait "lost its returning future"
          warnings with the iterator pattern
    - Behavior change: Async::Redis::disconnect now calls _close on any
      active Subscription before closing the socket, so the
      subscription's driver doesn't trip over an EOF on its pending
      read. Additive — existing iterator-mode callers are unaffected
    - Internal: factored _dispatch_frame and _read_frame_with_reconnect
      out of Subscription::next; shared between the iterator and
      callback paths. Reconnect semantics are identical across both
    - Pattern subscription responses now include pattern => undef on
      non-pmessage frames (previously omitted); no exists() check needed



( run in 0.839 second using v1.01-cache-2.11-cpan-6aa56a78535 )