Data-SortedSet-Shared

 view release on metacpan or  search on metacpan

lib/Data/SortedSet/Shared/Strings.pm  view on Meta::CPAN

    $self->_decode($opt{withscores}, $self->{set}->range_by_score($min, $max, %opt));
}
sub rev_range_by_score {
    my ($self, $max, $min, %opt) = @_;
    $self->_decode($opt{withscores}, $self->{set}->rev_range_by_score($max, $min, %opt));
}

# ---- pop / peek (id -> string) ----

for my $m (qw(pop_min pop_max peek_min peek_max)) {
    no strict 'refs';
    *$m = sub {
        my ($id, $score) = $_[0]{set}->$m;
        return defined $id ? ($_[0]{keys}->string($id), $score) : ();
    };
}

# ---- iteration ----

sub each {
    my ($self, $cb) = @_;
    my $k = $self->{keys};
    $self->{set}->each(sub { $cb->($k->string($_[0]), $_[1]) });
    return;
}

# ---- lifecycle ----

sub sync  { $_[0]{set}->sync; $_[0]{keys}->sync; return }
sub unlink { $_[0]{set}->unlink; $_[0]{keys}->unlink; return }
sub stats { { set => $_[0]{set}->stats, keys => $_[0]{keys}->stats } }

1;
__END__

=encoding utf-8

=head1 NAME

Data::SortedSet::Shared::Strings - string-keyed shared-memory sorted set (ZSET)

=head1 SYNOPSIS

    use Data::SortedSet::Shared::Strings;

    # anonymous (fork-shared); or pass set => $path, keys => $path for file-backed
    my $z = Data::SortedSet::Shared::Strings->new(max => 1_000_000);

    $z->add("alice", 1500);
    $z->incr("alice", 50);                       # 1550
    my @top   = $z->rev_range_by_rank(0, 9);     # ("alice", "bob", ...)
    my $score = $z->score("alice");
    my ($who, $sc) = $z->pop_min;                # remove + return the lowest

=head1 DESCRIPTION

A string-keyed sorted set in shared memory: the same API as
L<Data::SortedSet::Shared> but with B<string members> instead of int64 ids. It is
a thin layer composing two shared structures -- a L<Data::SortedSet::Shared> for
the (id, score) ordering and a L<Data::Intern::Shared> mapping each string key to
a dense id. Keys are interned on the way in and decoded back to strings on the way
out.

Because both backing stores live in shared memory, the set works B<across
processes>: every process resolves a key to the same id, so a string-keyed
leaderboard, priority queue, or rate limiter can be shared by many workers.

Ties among equal scores break by interning id (roughly insertion order of
first-seen keys), B<not> lexicographically. Keys are interned by byte content and
stay interned until C<clear> (see L<Data::Intern::Shared/LIMITS>). B<Linux-only>, 64-bit Perl.

=head1 METHODS

=head2 Construction

    my $z = Data::SortedSet::Shared::Strings->new(
        max      => $max_members,     # required
        set      => $path_or_undef,   # SortedSet backing (undef = anonymous)
        keys     => $path_or_undef,   # Intern backing    (undef = anonymous)
        max_keys => $max_members,     # distinct-key capacity (default: max)
        arena    => $bytes,           # key-arena bytes (default: max_keys * 32)
        mode     => $octal,           # file mode for both backing files (default: 0600)
    );

    my $z = Data::SortedSet::Shared::Strings->wrap($set, $intern);

C<new> creates (or reopens) both backing stores; for cross-process file-backed use,
every process passes the same C<set>/C<keys> paths. C<mode> sets the file mode for
both backing files (default 0600, subject to umask); it is used only when a file is
created and ignored when attaching an existing one. C<wrap> wraps two
already-constructed objects (e.g. memfd-backed ones shared by fd). C<set> and
C<key_table> return the underlying L<Data::SortedSet::Shared> and
L<Data::Intern::Shared> objects.

=head2 API

Every method takes/returns B<string> members; otherwise the semantics are exactly
those of L<Data::SortedSet::Shared>:

    $z->add($str, $score);     # 1 new / 0 updated / undef if a pool is full
    $z->incr($str, $delta);    # add to the score (creates the key if absent); returns the new score
    $z->remove($str);          # true if removed, false if absent
    $z->add_many([ [$s1,$sc1], ... ]);
    $z->clear;

    $z->score($str); $z->rank($str); $z->rev_rank($str); $z->exists($str);
    $z->count; $z->count_in_score($min, $max);

    $z->at_rank($r);
    $z->range_by_rank($start, $stop, %opts);     # ($str, ...) or ($str,$score,...) with withscores
    $z->rev_range_by_rank($start, $stop, %opts);
    $z->range_by_score($min, $max, %opts);       # limit / offset / withscores
    $z->rev_range_by_score($max, $min, %opts);

    my ($str, $score) = $z->pop_min;  # pop_max / peek_min / peek_max
    $z->each(sub { my ($str, $score) = @_; ... });

    $z->sync; $z->unlink; $z->stats;   # stats: { set => {...}, keys => {...} }

C<remove> leaves the key interned (ids are stable); the key universe (C<max_keys>)
must therefore accommodate every distinct key ever added, not just those currently



( run in 2.727 seconds using v1.01-cache-2.11-cpan-600a1bdf6e4 )