Async-Redis

 view release on metacpan or  search on metacpan

lib/Async/Redis/Subscription.pm  view on Meta::CPAN


# Track a channel subscription
sub _add_channel {
    my ($self, $channel) = @_;
    $self->{channels}{$channel} = 1;
    $self->_start_driver;
}

sub _add_pattern {
    my ($self, $pattern) = @_;
    $self->{patterns}{$pattern} = 1;
    $self->_start_driver;
}

sub _add_sharded_channel {
    my ($self, $channel) = @_;
    $self->{sharded_channels}{$channel} = 1;
    $self->_start_driver;
}

sub _remove_channel {
    my ($self, $channel) = @_;
    delete $self->{channels}{$channel};
}

sub _remove_pattern {
    my ($self, $pattern) = @_;
    delete $self->{patterns}{$pattern};
}

sub _remove_sharded_channel {
    my ($self, $channel) = @_;
    delete $self->{sharded_channels}{$channel};
}

# List subscribed channels/patterns
sub channels { keys %{shift->{channels}} }
sub patterns { keys %{shift->{patterns}} }
sub sharded_channels { keys %{shift->{sharded_channels}} }

sub channel_count {
    my ($self) = @_;
    return scalar(keys %{$self->{channels}})
         + scalar(keys %{$self->{patterns}})
         + scalar(keys %{$self->{sharded_channels}});
}

# Internal dequeue: wait for a message from the queue, dequeue it, and
# signal _slot_waiter so any pending _dispatch_frame can proceed. Used by
# both next() (iterator mode) and _start_driver (callback mode driver loop).
# Returns undef on clean close; dies with typed error on fatal close.
async sub _dequeue {
    my ($self, $exit_on_pause) = @_;

    # Iterator mode (default): pause is transient. Block through it and
    # return real messages once the driver resumes.
    #
    # Callback driver (exit_on_pause=1): exit cleanly on pause so the
    # driver task can be restarted after reconnect without two drivers
    # racing. _pause_for_reconnect wakes any in-flight _message_waiter
    # via done() so this path can exit promptly.
    while (!@{$self->{_pending_messages}}) {
        die $self->{_fatal_error} if $self->{_fatal_error};
        return undef if $self->{_closed};
        return undef if $exit_on_pause && $self->{_paused};
        $self->{_message_waiter} //= Future->new;
        await $self->{_message_waiter};
        delete $self->{_message_waiter};
    }

    die $self->{_fatal_error} if $self->{_fatal_error};
    return undef if $self->{_closed} && !@{$self->{_pending_messages}};
    return undef
        if $exit_on_pause && $self->{_paused} && !@{$self->{_pending_messages}};

    my $msg = shift @{$self->{_pending_messages}};
    if (my $w = delete $self->{_slot_waiter}) {
        $w->done unless $w->is_ready;
    }
    return $msg;
}

# Receive next message (async iterator pattern). Waits on the queue
# populated by _run_reader via _dispatch_frame. Returns undef on clean
# close; dies with the typed error on fatal close.
async sub next {
    my ($self) = @_;

    # Exclusivity check: callback mode disables iterator mode.
    if ($self->{_on_message}) {
        Carp::croak("Cannot call next() on a callback-driven subscription");
    }

    # In iterator mode the unified reader (_run_reader) feeds the queue.
    # The reader is already running (started by _pubsub_command during
    # subscribe). No separate driver start is needed.

    return await $self->_dequeue;
}

# Read one pub/sub frame from the underlying connection. On transient
# read error, attempt reconnect if enabled and fire on_reconnect on
# success; on unrecoverable failure, propagate the error.
# Returns a Future resolving to the raw frame (arrayref) or undef if
# the connection is gone and no more frames are available.
# Shared by next() and the callback driver loop added in a later task.
async sub _read_frame_with_reconnect {
    my ($self) = @_;
    my $redis = $self->{redis};

    while (1) {
        my $frame;
        my $ok = eval {
            $frame = await $redis->_read_pubsub_frame;
            1;
        };

        unless ($ok) {
            my $error = $@;
            if ($redis->{reconnect} && $self->channel_count > 0) {
                my $reconnect_error;



( run in 1.241 second using v1.01-cache-2.11-cpan-0b5f733616e )