Data-HashMap-Shared
view release on metacpan or search on metacpan
lib/Data/HashMap/Shared.pm view on Meta::CPAN
C<$max_entries>, C<$max_size>, C<$ttl>, and C<$lru_skip> are used only when
creating a new file; when opening an existing one, all parameters are read
from the stored header and the constructor arguments are ignored.
Multiple processes can open the same file simultaneously.
Dies if the file exists but was created by a different variant or is corrupt.
Optional C<$max_size> enables LRU eviction: when the map reaches C<$max_size>
entries, the least-recently-used entry is evicted on insert. Set to 0 (default)
to disable. LRU uses a clock/second-chance algorithm: C<get> sets an accessed
bit (lock-free, no write lock), and eviction gives a second chance to recently
accessed entries before evicting.
Optional C<$ttl> sets a default time-to-live in seconds for all entries.
Expired entries are lazily removed on access. Set to 0 (default) to disable.
When TTL is active, C<get> and C<exists> check expiry.
Optional C<$lru_skip> (0-99, default 0) sets the probability (as a percentage)
of skipping LRU promotion on C<get>. This reduces write-lock contention for
Zipfian (power-law) access patterns where a small set of hot keys dominates
reads. The LRU tail (eviction victim) is never skipped, preserving eviction
correctness. Set to 0 for strict LRU ordering.
B<Zero-cost when disabled>: with both C<$max_size=0> and C<$ttl=0>, the fast
lock-free read path is used. The only overhead is a branch (predicted away).
=head2 String Keys and UTF-8
String-key variants (C<SS>, C<SI>, C<IS>, C<I16S>, C<I32S>, C<SI16>, C<SI32>)
compare keys as raw bytes: two keys are the same entry if and only if they
contain the same byte sequence. The SV UTF-8 flag is stored alongside the
key so retrieval round-trips it to the returned SV, but it is B<not> part
of key identity. Consequences:
=over
=item *
ASCII keys with a toggled UTF-8 flag hash and match the same entry
(C<use utf8>, C<utf8::upgrade>, and C<utf8::downgrade> on ASCII are all
equivalent from the map's point of view).
=item *
Non-ASCII keys with different byte encodings are B<distinct>. C<"caf\xe9">
(latin-1, 4 bytes) and C<"café"> with C<use utf8> (5 UTF-8 bytes) are two
different keys. If your input comes in mixed encodings, normalize with
C<Encode::encode_utf8> before use.
=back
=head2 Sharding
my $map = Data::HashMap::Shared::II->new_sharded($path_prefix, $shards, $max_entries, ...);
Creates C<$shards> independent maps (files C<$path_prefix.0>, C<$path_prefix.1>,
...) behind a single handle, each with up to C<$max_entries> entries
(total capacity is C<$shards * $max_entries>). Per-key operations automatically
route to the correct shard via hash dispatch. Writes to different shards
proceed in parallel with independent locks.
All operations work transparently on sharded maps: C<put>, C<get>, C<remove>,
C<exists>, C<add>, C<update>, C<swap>, C<take>, C<incr>, C<cas>,
C<get_or_set>, C<put_ttl>, C<touch>, C<persist>, C<set_ttl>, C<keys>,
C<values>, C<items>, C<to_hash>, C<set_multi> (method only),
C<get_multi> (method only), C<each>, C<pop>, C<shift>, C<drain>,
C<clear>, C<flush_expired>, C<flush_expired_partial>, C<size>,
C<stats> (method only), C<reserve>, and all diagnostic keywords.
Cursors chain across shards automatically. C<cursor_seek> routes to the
correct shard based on key hash. C<$shards> is rounded up to the next
power of 2.
=head2 API
Replace C<xx> with variant prefix: C<i16>, C<i32>, C<ii>, C<i16s>,
C<i32s>, C<is>, C<si16>, C<si32>, C<si>, C<ss>.
my $ok = shm_xx_put $map, $key, $value; # insert or overwrite
my $ok = shm_xx_add $map, $key, $value; # insert only if key absent
my $ok = shm_xx_update $map, $key, $value; # overwrite only if key exists
my $old = shm_xx_swap $map, $key, $value; # put + return old value (undef if new)
my $n = $map->set_multi($k, $v, ...); # batch put under single lock, returns count
my @v = $map->get_multi($k1, $k2, ...); # batch get under single lock with prefetch pipeline
my $v = shm_xx_get $map, $key; # returns undef if not found
my $ok = shm_xx_remove $map, $key; # returns false if not found
my $ok = shm_xx_exists $map, $key; # returns boolean
my $s = shm_xx_size $map;
my $m = shm_xx_max_entries $map;
my @k = shm_xx_keys $map;
my @v = shm_xx_values $map;
my @items = shm_xx_items $map; # flat (k, v, k, v, ...)
while (my ($k, $v) = shm_xx_each $map) { ... } # auto-resets at end
shm_xx_iter_reset $map;
shm_xx_clear $map;
my $href = shm_xx_to_hash $map;
my $v = shm_xx_get_or_set $map, $key, $default; # returns value
Integer-value variants also have:
my $n = shm_xx_incr $map, $key; # returns new value
my $n = shm_xx_decr $map, $key; # returns new value
my $ok = shm_xx_cas $map, $key, $expected, $desired; # compare-and-swap
my $n = shm_xx_incr_by $map, $key, $delta;
LRU/TTL operations (require TTL-enabled map for C<put_ttl>):
my $ok = shm_xx_put_ttl $map, $key, $value, $ttl_sec; # per-key TTL (0 = permanent); requires TTL-enabled map
my $ms = shm_xx_max_size $map; # LRU capacity (0 = disabled)
my $t = shm_xx_ttl $map; # default TTL in seconds
my $r = shm_xx_ttl_remaining $map, $key; # seconds left (0 = permanent, undef if missing/expired/no TTL)
my $ok = shm_xx_touch $map, $key; # reset TTL to default; promotes in LRU; false if no TTL/LRU
my $ok = shm_xx_persist $map, $key; # remove TTL, make key permanent; false on non-TTL maps
my $ok = shm_xx_set_ttl $map, $key, $sec; # change TTL without changing value (0 = permanent); false on non-TTL maps
my $n = shm_xx_flush_expired $map; # proactively expire all stale entries, returns count
my ($n, $done) = shm_xx_flush_expired_partial $map, $limit; # gradual: scan $limit slots
Atomic remove-and-return:
my $v = shm_xx_take $map, $key; # remove key and return value (undef if missing)
my ($k, $v) = shm_xx_pop $map; # remove+return from LRU tail / scan forward
my ($k, $v) = shm_xx_shift $map; # remove+return from LRU head / scan backward
( run in 0.731 second using v1.01-cache-2.11-cpan-39bf76dae61 )