Cache:...Managed(3pm)
π Cache::Memcached::Managed(3pm) π
okay so this is the manual page for Cache::Memcached::Managed, and honestly? she's that girl. sit down, grab a boba, and let's go through it together π§β¨
β¨ NAME β¨
Cache::Memcached::Managed β it hands you a whole API for managing cached information. that's the job. that's the assignment. π
π SYNOPSIS π
bestie, look at how clean this is:
use Cache::Memcached::Managed;
my $cache = Cache::Memcached::Managed->new( '127.0.0.1:12345' );
$cache->set( $value );
$cache->set( $value,$id );
$cache->set( value => $value,
id => $id,
key => $key,
version => "1.1",
namespace => 'foo',
expiration => '1D', );
my $value = $cache->get( $id );
my $value = $cache->get( id => $id,
key => $key );
like. it's giving effortless. πΈ
π·οΈ VERSION π·οΈ
this documentation describes version 0.27. no cap. π«Ά
π DIFFERENCES FROM THE Cache::Memcached API π
so Cache::Memcached::Managed gives you an API to values that are cached across one or more memcached servers. it's very similar to the Cache::Memcached API, but it also lets you manage whole groups of values, do simplified key generation and expiration, handle version and namespace management, plus a few other little treats. π
here are the main glow-ups versus plain Cache::Memcached:
automatic key generation
the calling subroutine supplies the key (by default). so whenever your get and set operations happen inside the same subroutine, you don't have to dream up some identifying key that has to be unique across the entire cache. it's just handled. iconic. β¨
ID refinement
you can add an ID onto that (automatically) generated key β there isn't one by default β which makes it super easy to identify similar data objects (think: the primary key of a Class::DBI object). and if you need a unique ID, one can be created automatically for you (very useful when you're logging events). π₯
version management
the caller's package provides an identifying version (by default). this means data structures that got reformatted because the source code changed can live separately from each other inside the cache. no crossover episodes. π
namespace support
a namespace identifier lets different realms co-exist in the same cache (the uid by default). so a whole group of developers can all use the same cache without stepping on each other's toes. bestie behaviour fr fr. π
group management
a piece of cached data can be assigned to any number of groups. then you can retrieve and remove cached data by naming the group it belongs to. this is how you selectively nuke cached data that a database change has invalidated, or pull out logged events when you don't know the identification but you do know the group name. chef's kiss. π
easy (default) expiration specification
you can specify a default expiration per Cache::Memcached::Managed object. and expirations accept mnemonics: D, H, M, S β so '2D3H' would be 2 days and 3 hours. so cute, so legible. π
automatic fork() detection
sockets get automatically reset in forked processes β no manual reset required. this is what lets the module access cached data during the server start phase in a mod_perl environment. π
magical increment
counters get automagically created with incr if they don't exist yet. β¨
instant invalidation
support for the new flush_all memcached action, so you can invalidate all data in a cache in one fell swoop. one button. gone. π
dead memcached server detection
an easy way to check whether all the related memcached servers are still alive. vibe check, but for servers. π«Ά
starting/stopping memcached servers
easy start / stop of indicated memcached servers β mainly intended for development and testing environments. πΈ
extensive test-suite
an extensive test-suite is included, which is sadly lacking in the Cache::Memcached distribution. we said what we said. π
π§ BASIC PREMISES π§
the whole premise here: every single piece of information you want to cache can be identified by a key, an optional ID, a version, and a namespace.
the key is the basic identification of the value being cached. the ID is a refinement on top of that basic identification. the version makes sure that differently formatted values sharing the same key and ID don't interfere with each other. and the namespace makes sure different realms of information (say, different users) don't interfere with each other either. π
key
by default the key is the fully qualified subroutine name from which the cached value is accessed. so if a cached value is accessed from subroutine bar in the Foo package, the key is Foo::bar. you can also give explicit keys, and those may contain any characters except the delimiter.
there's a special case when the cache is being accessed from the lowest level in a script: then the default key gets built out of the server name (as determined by uname -n) plus the absolute path of the executing script. π¦
ID
if you don't specify an ID for a piece of information, then just the key is assumed. the ID can be any string β for instance it could be the primary key of a Class::DBI object. you can pass IDs as a scalar value, as a list ref, or as a hash ref (handy for multi-keyed Class::DBI objects, for example).
some examples, look:
my $value = $cache->get( $id );
my $value = $cache->get( [$id,$checkin,$checkout] );
my $value =
$cache->get( {id => $id,checkin => $checkin,checkout => $checkout} );
and if the ID should be something unique and you genuinely don't care about the ID itself (like, you only care about the group the information gets linked to), you can pass the string ":unique" and a unique ID will be generated for you automatically. π
version management
the version tells you which version (generation) of the data is being fetched or stored. by default it takes the value of the $VERSION variable of the package that the key is associated with. this is the whole reason new modules that cache information can be dropped into a server park without anybody having to fear data format changes. π
you can specify a specific version with each of add, decr, get, get_multi, incr, replace and set, to indicate the link with the group of the information being cached.
Please always use a string as the version indicator. Using floating point values may yield unexpected results, where 1.0 would actually use 1 as the version.
like, please. use a string. I'm begging. π
namespace management
the namespace indicates the realm that the data belongs to. by default it assumes the effective user id of the process (as known by $>). this is what lets several users share the same "data server" and "directory server" while each of them still gets their own set of cached data. π
you can specify a specific namespace with each of add, decr, get, get_multi, incr, replace and set, to indicate the link with the group of the information being cached.
data server
the data server is a Cache::Memcached (compatible) object where all the data (keyed to a "data key") is stored. it uses one or more memcached servers. you can grab the data server with the data object. π
data key
the data key identifies a piece of data inside the "data server". it's formed by appending the namespace (by default the user id of the process), the version, the key, and the ID, all separated by the delimiter.
- if a scalar value is given as the ID, then that value gets used.
- if the ID is given as a list ref, then the values are concatenated with the delimiter.
- if the ID is given as a hash ref, then the sorted key and value pairs are concatenated with the delimiter.
group management
the group concept exists to make managing cached information easier. see, you can't delete cached information from the "data server" by matching a wildcard key value β because you can only access cached information if you know the exact key. so another way was needed to reach whole groups of cached data. π
and it had to be a way that didn't need another (database) backend and didn't depend on running on a single piece of hardware. the answer? a "directory server" β which is basically just another memcached server dedicated to keeping a directory of the data held in the "data server". π
the group concept lets you associate a given "data key" with a named group and a group ID value (for example, the group named group and the name of an SQL table). that information then lives in the "directory server", and from there you can get a list of "data keys" associated with the group name and the ID value. iconic.
in the current implementation, only one group name is recognized by default:
groupβ intended for generic data without specific keys.
you can define your own set of group names using the group_names parameter in new.
group names and IDs can be specified with each of add, decr, incr, replace and set, to indicate the link with the group of the information being cached.
the pseudo group ID ":key" can be specified to say "hey, use the key as the group ID". this is usually used together with the generic "group" group name. β¨
you can get a list of valid group names with the group_names method.
directory server
the directory server is a Cache::Memcached (compatible) object used to store "data key"s (as opposed to the data itself) for "group management". if you didn't specify a directory server, then the data server is assumed. π¦
If there are multiple memcached servers used for the "data server", then it is advised to use a separate directory server (as a failure in one of the memcached backend servers will leave you with an incomplete directory otherwise).
and if the directory server ever fails, and it's vital that no stale data is sitting in the data server, then you'd need to run a flush_all to make sure no stale data is left behind. but obviously that also deletes all the non-stale data from the data server too. so, you know. your mileage may vary. π
expiration specification
expiration can be specified in seconds, but for convenience it can also be given in days, hours and minutes (and seconds). you do this with a number immediately followed by a letter: D for days, H for hours, M for minutes, or S for seconds. for example:
2D3H
means 2 days and 3 hours, which means 183600 seconds. math ate. π₯
transparent fork handling
using this module, you don't have to stress about whether everything will still work after a fork(). the second it's detected that the process has forked, new handles get opened to the memcached servers in the child process β so that meticulous calling of disconnect_all from Cache::Memcached is no longer needed. thank u, next. π
transparent thread handling is still on the todo list though. someday, bestie. someday. πΈ
π CLASS METHODS π
new β¨
my $cache = Cache::Memcached::Managed->new;
my $cache = Cache::Memcached::Managed->new( '127.0.0.1:11311' );
my $cache = Cache::Memcached::Managed->new(
data => '127.0.0.1:11311', # default: '127.0.0.1:11211'
directory => '127.0.0.1:11411', # default: data
delimiter => ';', # default: '#'
expiration => '1H', # default: '1D'
flush_interval => 10, # default: none
namespace => 'foo', # default: $> ($EUID)
group_names => [qw(foo bar)], # default: ['group']
memcached_class => 'Cached::Memcached::Fast', # default: 'Cache::Memcached'
);
my $cache = Cache::Memcached::Managed->new( inactive => 1 );
okay bestie so this is the constructor β it builds you a fresh Cache::Memcached::Managed object π. Here's the tea on how it decides what you meant: if you hand it less than two input parameters, it just assumes that one thing you passed is the value for the "data" field, and if you passed literally nothing it defaults to '127.0.0.1:11211'. If you hand it more than one input parameter, then it treats them like a hash with the fields below π
data π
data => '127.0.0.1:11211,127.0.0.1:11212',
data => ['127.0.0.1:11211','127.0.0.1:11212'],
data => {
servers => ['127.0.0.1:11211','127.0.0.1:11212'],
debug => 1,
},
data => $memcached,
This is the spec for the memcached server backend(s) of the "data server". It's giving flexible, because it can be any of these:
- a string with comma separated memcached server specification
- a list ref with memcached server specification
- a hash ref with
Cache::Memcachedobject specification - a blessed object adhering to the
Cache::MemcachedAPI
π¨ there is NO default for this field, you must specify it. fr fr.
The blessed object can later be obtained with the data method β¨
delimiter π
delimiter => ';', # default: '#'
Pick the delimiter used in key generation. You should only specify this if you're expecting your key, ID, version or namespace values to contain the character '#'. It can be literally any character that won't be part of key, ID, version or namespace values.
The current delimiter can be grabbed with the delimiter method π
Using the null byte (\0) is not advised at this moment, because there are some encoding issues withinCache::Memcachedregarding null bytes π
directory πΈ
directory => '127.0.0.1:11311,127.0.0.1:11312',
directory => ['127.0.0.1:11311','127.0.0.1:11312'],
directory => {
servers => ['127.0.0.1:11311','127.0.0.1:11312'],
debug => 1,
},
directory => $memcached,
Same deal as data but for the "directory server" memcached backend(s). It should be one of:
- a string with comma separated memcached server specification
- a list ref with memcached server specification
- a hash ref with
Cache::Memcachedobject specification - a blessed object adhering to the
Cache::MemcachedAPI
If you don't specify this field at all, then the "data server" object gets assumed instead π«Ά The blessed object can later be obtained with the directory method.
expiration β³
expiration => '1H', # default: '1D'
The spec for the default expiration. These postfixes are allowed:
SsecondsMminutesHhoursDdaysWweeks
The default default expiration is one day ('1D'). This default expiration gets used whenever no expiration has been specified with add, decr, incr, replace or set. You can grab the default expiration with the expiration method π
flush_interval π
flush_interval => 10, # default: none
This specifies the default interval between which memcached servers will be flushed with flush_all. If you don't specify it, no interval gets used by default.
group_names π
group_names => [qw(foo bar)], # default: ['group']
The spec of allowable group names β give it a list reference of the names you'll allow. Defaults to a one element list reference containing 'group' only.
Any group name can be used, as long as it's made of alphanumeric characters and doesn't interfere with other functions. The names that are currently disallowed are:
datadeletedirectoryexpirationidgroup_namesnamespace
ngl there's hardly any penalty for using a lot of different group names in itself. However, linking cached information to a lot of different groups does have a penalty π
inactive π΄
inactive => 1,
Say the object is inactive. If you specify this, you get back an instantiated object with the exact same API as Cache::Memcached::Managed, except it won't do anything. It's meant for situations where no active memcached servers can be reached: all your code will then function as if there are no cached values in the cache. lowkey iconic behaviour tbh.
memcached_class π
memcached_class => 'Cached::Memcached::Fast',
By default, this module uses the Cache::Memcached class as a "memcached" client. Recently though, other implementations have been developed β like Cache::Memcached::Fast β that are considered API compatible. To use one of these other implementations of the memcached client, you can specify the name of the class to use. By default, "Cache::Memcached" is assumed: the module will be loaded automatically if it isn't already π¦
namespace β¨
namespace => 'foo', # default: $> ($EUID)
The spec of the default namespace to use with set, incr, decr, add, replace, get, get_multi, group, get_group and grab_group. Defaults to the effective user ID of the process, as indicated by $> ($EUID) π
π«Ά OBJECT METHODS π«Ά
The following object methods are available (in alphabetical order, because we're organized like that):
add π
$cache->add( $value );
$cache->add( $value, $id );
$cache->add( $value, $id, $expiration );
$cache->add( value => $value,
id => $id, # optional
key => $key, # optional
group => 'foo', # optional
expiration => '3H', # optional
version => '1.0', # optional
namespace => 'foo', # optional
);
Add a value to the cache β but only if it doesn't exist yet π Otherwise it's the same as set.
data π
my $data = $cache->data;
Returns the data server object as specified with new β¨
dead π
my @dead = $cache->dead;
my $dead = $cache->dead; # hash ref
Returns the memcached backend servers that appear to be non-functional π΅ In list context it returns the specifications of the servers in alphabetical order. In scalar context it returns a hash reference where the unresponsive servers are the keys. Call errors to obtain the number of errors that were found for each memcached server.
decr π
$cache->decr;
$cache->decr( $value );
$cache->decr( $value, $id, $expiration );
$cache->decr( value => $value, # default: 1
id => $id, # default: key only
key => $key, # default: caller environment
expiration => '3H', # default: $cache->expiration
version => '1.0', # default: key environment
namespace => 'foo', # default: $cache->namespace
);
Decrement a value in the cache β but only if it already exists π Otherwise it's the same as set. Default for value is 1.
bestie, important: any group associations will never be honoured here. The assumption is that they'd all be the same for every call to this counter, so they're only set withset,addorincr. periodt.
delete ποΈ
$cache->delete;
$cache->delete( $id );
$cache->delete( id => $id, # optional
key => $key, # optional
version => '1.0', # optional
namespace => 'foo', # optional
);
Delete a value, associated with the specified "data key", from the cache. You can call it with unnamed or named parameters (named gets assumed if you give two or more input parameters). If you call it with unnamed parameters, then they are:
- id β the ID used to identify the value to be deleted. Defaults to no ID (then it uses key only).
When you're using named parameters, these are the names you can specify:
idβ the ID used to identify the value to be deleted. Defaults to no ID (then it uses key only).keyβ the key used to identify the value to be deleted. Defaults to the default key (as determined by the caller environment).versionβ the version used to identify the value to be deleted. Defaults to the version associated with the key.namespaceβ the namespace used to identify the value to be deleted. Defaults to the default namespace associated with the object.
delete_group π
my $deleted = $cache->delete_group( group => 'foo' );
Deletes all cached information related to one or more given groups (specified as name and ID value pairs) and returns how many items were actually deleted π₯
delimiter π
my $delimiter = $cache->delimiter;
Returns the delimiter as (implicitly) specified with new π
directory πΈ
my $directory = $cache->directory;
Returns the directory cache object as (implicitly) specified with new β¨
errors π
my $errors = $cache->errors( "reset" );
foreach ($cache->servers) {
print "Found $errors->{$_} errors for $_\n" if exists $errors->{$_};
}
Returns a hash reference with the number of errors that happened when storing data values in a memcached backend server. Use dead to find out whether a server is not responding π A true value for the input parameter means the error counts should be reset.
expiration β³
$expiration = $cache->expiration;
Returns the default expiration as (implicitly) specified with new π«Ά
flush_all π
my $flushed = $cache->flush_all;
my $flushed = $cache->flush_all( 30 ); # flush with 30 second intervals
Initialize the contents of all the memcached backend servers of the "data server". The input parameter specifies the interval between flushes of backend memcached servers; the default is the flush_interval value (implicitly) specified with new. Returns whether all memcached servers were successfully flushed β
bestie heads up: this method returns immediately after instructing each of the memcached servers. Also note that the timedflush_allfunctionality has only recently become part of the standard memcached API (starting from publicly released version 1.2.1). See the file"flush_interval.patch"for a patch for release 1.1.12 of the memcached software that implements timedflush_allfunctionality π
flush_interval π
my $interval = $cache->flush_interval;
So this one hands back the default flush interval values that go with flush_all, exactly as you (implicitly) set them up with new. That's it, that's the whole vibe. π«
get π
my $value = $cache->get;
my $value = $cache->get( $id );
my $value = $cache->get( id => $id, # optional
key => $key, # optional
version => '1.1', # optional
namespace => 'foo', # optional
);
Bestie, this is how you pull a value out of the cache β it's attached to a "data key". You can call it with unnamed params or named params, dealer's choice. β¨
If you go with unnamed params, here's what you're passing:
- id β the ID that identifies the value you wanna fetch. Defaults to no ID (and then it just uses the default key only).
When you're using named params, these are the names you can specify:
idβ the ID used to identify the value to be fetched. Defaults to no ID (so then it useskeyonly).keyβ the key used to identify the value to be fetched. Defaults to the default key (as determined by the caller environment).versionβ the version used to identify the value to be deleted. Defaults to the version associated with the key.namespaceβ the namespace used to identify the value to be deleted. Defaults to the default namespace associated with the object.
get_group π
my $group = $cache->get_group(
group => $groupname,
namespace => $namespace, # default: $cache->namespace
);
foreach my $key (sort keys %{$group}) {
print "key: $key\n"
my $versions = $group->{$key};
foreach my $version (sort keys %{$versions}) {
print " version: $version\n";
my $ids = $versions->{$version};
foreach my $id (sort keys %{$ids}) {
print " id: $ids->{$id}\n";
}
}
}
my @value = $cache->get_group(
group => $groupname,
namespace => $namespace, # default: $cache->namespace
);
So depending on the context you're in, this either gives you a reference to a multi level hash for the given group name and ID (with all the group's data inside π«Ά), or β if you're in list context β a plain list of values (no matter the key, version or id, it does not care).
The input params are a hash that should hold the group name and its associated ID, plus an optional namespace if you want one.
The structure of that returned hash reference is:
$result
|--- key
|-- version
|-- id
|-- value
Peep "group management" if you want the whole tea on groups. And if you want to grab the group and delete it at the same time, go look at grab_group β two birds, one stone, iconic. π
get_multi β‘
my $hash = $cache->get_multi( \@id );
my $hash = $cache->get_multi(
id => \@id,
key => $key,
namespace => $namespace,
);
This is the optimized way to grab a whole bunch of values that all share the same key, in one go. Returns a hash reference with the values it found, keyed to the associated ID. Efficiency? Slay. π₯
Works with named or unnamed params. Unnamed, and here's your list:
- id β a list reference of ID's used to identify the values to be fetched. Must be specified. (Not optional, bestie, don't skip it.)
Named params, and these are your names:
idβ a list reference of ID's used to identify the values to be fetched. Must be specified.keyβ the key used to identify the values to be fetched. Defaults to the default key (as determined by the caller environment).namespaceβ the namespace to fetch values from. Defaults to the namespace that was (implicitly) specified withnew.
grab_group πΈ
my $group = $cache->grab_group(
group => $groupname,
namespace => $namespace, # default: $cache->namespace
);
Same as get_group, except it yeets the returned data out of the cache at the same time it hands it to you. Get it and delete it. Chaotic but efficient. π
group π¦
my $group = $cache->group(
group => $groupname,
namespace => $namespace, # default: $cache->namespace
);
foreach my $key (sort keys %{$group}) {
print "key: $key\n"
print " ids: @{$group->{$key}}\n";
}
Returns a reference to a multi level hash for the given group name and ID. The input params are a hash that should contain the group name and associated ID, with an optional namespace spec.
Here's how the hash is built:
$result
|--- key
|--- [id1,id2..idN]
Wanna know more about groups? "group management" has you covered. β¨
group_names π
my @group_name = $cache->group_names;
my $group_names = $cache->group_names; # hash ref
Gives you back the valid group names, exactly as (implicitly) specified with new. In list context they come at you in alphabetical order β so organized, so demure β or as a hash ref if you're in scalar context.
inactive π
print "Inactive!\n" if $cache->inactive;
Tells you whether the cache object is inactive. This happens when a true value gets specified with new. That's the whole check.
incr π’
$cache->incr;
$cache->incr( $value );
$cache->incr( $value, $id );
$cache->incr( $value, $id, $expiration );
$cache->incr( value => $value, # default: 1
id => $id, # default: key only
key => $key, # default: caller environment
expiration => '3H', # default: $cache->expiration
version => '1.1', # default: key environment
namespace => 'foo', # default: $cache->namespace
group => 'bar', # default: none
);
Increments a value in the cache. Everything else works the same as set. The default for value is 1.
Now here's the β¨magicalβ¨ part, and it's genuinely different from incr() in Cache::Memcached: this increment function automagically adds the counter if it doesn't exist yet. No cap. πͺ
Heads up though β any group associations only get set when the counter is created, and they'll be ignored on any subsequent increments of that same counter. Remember that. π«Ά
namespace π
my $namespace = $cache->namespace;
Gets you the default namespace, as (implicitly) specified with new.
replace π
$cache->replace( $value );
$cache->replace( $value, $id );
$cache->replace( $value, $id, $expiration );
$cache->replace( value => $value, # undef
id => $id, # default: key only
key => $key, # default: caller environment
expiration => '3H', # default: $cache->expiration
version => '1.1', # default: key environment
namespace => 'foo', # default: $cache->namespace
);
Replaces a value in the cache β but only if it already exists. Otherwise it behaves just like set.
Careful, bestie: group associations are never honoured here. The assumption is that they'd all be the same for every call to this counter, so they only get set withset,addorincr. Not here. Periodt.
reset π§Ή
$cache->reset;
Resets the client side of the cache system. Mostly for internal usage only, ngl. Always returns true.
servers π₯οΈ
my @backend = $cache->servers;
my $backend = $cache->servers; # hash ref
Hands you the configuration details of the memcached backend servers that are currently configured to be used. List context? You get a list in alphabetical order. Scalar context? A hash ref. Pick your fighter. π
Also go peek at dead if you wanna find out whether any of those memcached backend servers are not responding. π
set π
$cache->set;
$cache->set( $value );
$cache->set( $value,$id );
$cache->set( $value, $id, $expiration );
$cache->set( value => $value, # default: undef
id => $id, # default: key only
key => $key, # default: caller environment
expiration => '3H', # default: $cache->expiration
version => '1.1', # default: key environment
namespace => 'foo', # default: $cache->namespace
group => 'bar', # default: none
);
Sets a value in the cache β doesn't matter whether it already exists or not, it's going in. Main character energy. π
You can call this with named or unnamed params (unnamed only if you're passing two input params or less). Unnamed, and here's your order:
- value β the value to set in the cache. Defaults to
"undef". - id β the ID used to identify the value. Defaults to no ID (then it uses
keyonly). - expiration β the expiration of the value. Defaults to the value as specified with
expirationfor the key.
With named input params, these are the names and values you can pass as a hash (alphabetical, because we're tidy like that):
expirationβ the expiration time in seconds of the given value. Defaults to the value as specified withexpirationfor the key. Values below30*24*60*60(30 days) get treated as relative to the current time. Any other values are assumed to be absolute epoch times (seconds since 1 Jan. 1970 GMT). Peep "expiration specification" for more ways to set expiration.idβ the ID used to identify the value. Defaults to no ID (then it useskeyonly).keyβ the key used to identify the value. Defaults to the default key (as determined by the caller environment). Can be specified as a relative key when it's prefixed with"::", so"::bar"would refer to the key"Foo::bar"if you called it from the package"Foo".namespaceβ the namespace to associate the value with. Defaults to the namespace that was (implicitly) specified withnew.valueβ the value to set in the cache. Defaults to"undef".versionβ the version used to identify the value to be set. Defaults to the version associated with the key.
And beyond all those named params? You can also pass any number of group name and ID pairs to indicate a link to that group. Extra credit, honestly. π
start π
my $started_ok = $cache->start;
my $started_ok = $cache->start( $config );
Attempts to start the memcached servers that were configured with new (the ones you can find out about with servers) by default, or the servers with the specified configs if you pass them. Returns whether all the servers (implicitly) specified got started successfully. π
Real talk: this only works if the memcached server(s) will be running on the same physical hardware as the script is running β which generally will not be the case in a production environment. So it's of limited usage generally. But it's a handy little feature to have when you're developing or testing. π
Also see stop if you need the other direction.
stats π
my $stats = $cache->stats;
Returns a hash ref with simple statistics from all of the memcached backend servers. The structure of that hash ref goes like this:
$stats
|-- server specification
|-- key
|-- value
Numbers? Immaculate. Stats? Delivered. π
See the memcached server documentation on possible keys and values.
π stop
my $stopped = $cache->stop;
my $stopped = $cache->stop( $config );
Tries to stop the memcached servers you tell it about (via the config value), and hands back whether all of them have actually stopped. If you pass nothing, it defaults to stopping all the servers that were originally set up with new. π
Heads up bestie: this only works if the memcached server(s) are running on the same physical hardware as the script itself (which, real talk, is basically never the case in production). So it's of limited usage generally β but it's a genuinely handy little feature to have if you're developing or testing. π
See also start.
πΈ version
my $version = $cache->version; # hash ref
my $version = $cache->version( $config ); # hash ref
Grabs the version info of the memcached servers you name, or of all the memcached servers in use if you don't pass any input parameters. Returns a hash reference where the keys are the config information of the servers used (as returned by servers) and the values are the version information of the associated memcached server. So cute, so informative. π¦
π EXAMPLES π
π generic grouped event logging
$cache->set( group => 'event1',
id => ':unique',
value => $value
);
This would put the value $value into the cache, linked to the group 'event1'. Since we're not interested in the id of the event, but we do want to make sure it's always unique, the pseudo id ':unique' is specified. β¨
A recurring process, usually a cron job, would then need to do the following to grab all of the values cached:
my @value = $cache->grab_group( group => 'event1' );
foreach (@value) {
# perform whatever you want to do with the value in C<$_>
}
Please note that only the values are returned because grab_group is called in list context. π«Ά
π§ generic content logging
my $cache = Cache::Memcached::Managed->new(
data => $servers,
group_names => [qw(hotel_id room_id)],
expiration => '1H',
);
package Foo;
sub available {
my ($cache,$hotel_id,$room_id,$checkin,$checkout) = @_;
my $available;
unless ($available = $cache->get( id => [$room_id,$checkin,$checkout] )) {
# perform complicated calculations setting C<$available>
$cache->set( id => [$room_id,$checkin,$checkout],
value => $available,
room_id => $room_id,
hotel_id => $hotel_id,
);
}
return $available;
} #available
This example shows availability caching in a specific subroutine. Because the get and the set are located in the same subroutine, it is not necessary to specify the key (which will be automatically set to "Foo::available"). Main character energy from the key generator honestly. π
Please also note the absence of a namespace specification. Since each user of the "available" subroutine should have its "realm" depending on the cache object, no namespace specification is done.
Now, whenever something related to the hotel_id is changed, a simple:
$cache->delete_group( hotel_id => $hotel_id );
would be enough to also remove any availability cached in the above example (for the same value of $hotel_id). π§Ή
The same would apply when something related to the room_id is changed: a simple:
$cache->delete_group( room_id => $room_id );
would be enough to also remove any availability cached in the above example (for the same value of $room_id).
π CAVEATS π
π Race Conditions
Several race conditions exist that can not be fixed because of a lack of semaphores when using memcached. π
Most important race condition is when a group is deleted: between the moment the main pointer ("directory key") is reset and all of the index keys are removed, it is possible for another process to be adding information to the same directory key already. In a worst case scenario, this means that a data key can get lost. π«
To prevent this, a delay of 2 seconds is applied each time a group is deleted. This should give some time for the cleaning process to clean up before other processes start accessing again, but it is in no way a guarantee that other processes wouldn't be able to add information if the cleaning process needs more than 2 seconds to clean up.
Ngl bestie, that 2 second thing is a vibe and a hope, not a promise. π
β° Cron jobs
Because the "data key"s by default include the user id (uid) of the process as the namespace with which the entry was stored in the cache, cron jobs (which usually run under a different user id) will need to set the namespace to the user id of the process storing information into the cache.
π« Incompatibility with Cache module
John Goulah pointed out to me that there is an inconsistency with unnamed parameter passing in respect to the Cache module. Specifically, the "set" method:
$c->set( $key, $data, [ $expiry ] );
is incompatible with this module's "set" method:
$cache->set;
$cache->set( $value );
$cache->set( $value, $id );
$cache->set( $value, $id, $expiration );
The reason for this is simple: in this module, all parameters are optional. So you can specify just a value: the key will be generated for you from the caller environment. Since I felt at the time that you would more likely specify a value than a key, I made that the first parameter (as opposed to the "set" method of Cache). Changing to the format as imposed by the Cache module is not an option at this moment in the lifetime of this module, as it would break existing code (the same way as it breaks the test-suite). π€
π THEORY OF OPERATION π
The group management is implemented by keeping a type of directory information in a (separate) directory memcached server.
For each group, one directory key is maintained in the directory memcached server. This key consists of the string "Cache::Memcached::Managed::", appended with the namespace, group name, the delimiter and the ID of the group. For instance, the directory key for the group
group => 'foo'
when running as user "500" would be:
Cache::Memcached::Managed#500#group#foo
The value of the directory key of a group is used as a counter. Each time some content is added that is linked to the group, that counter will be incremented and its value prepended to create an "index key". So the first index key of the above example would be:
1#Cache::Memcached::Managed#500#group#foo
This index key is then also stored in the directory memcached server, with the original "data key" as its value, and with the same expiration as used for the data key. β¨
Whenever the index keys are needed of a group (e.g. for fetching all of its members, or for deleting all of its members), the value of the directory key of the group is inspected, and that is used to generate a list of index keys. Suppose the value of the directory key is 5, then the following index keys would be generated (essentially mapping 1..5):
1#Cache::Memcached::Managed#500#group#foo
2#Cache::Memcached::Managed#500#group#foo
3#Cache::Memcached::Managed#500#group#foo
4#Cache::Memcached::Managed#500#group#foo
5#Cache::Memcached::Managed#500#group#foo
If the group is to be deleted or fetched, then all possible values for these index keys are obtained. For instance, this would fetch:
1#Cache::Memcached::Managed#500#group#foo => 500#1.0#Foo::zip#23
2#Cache::Memcached::Managed#500#group#foo => 500#1.1#Bar::pod#47
3#Cache::Memcached::Managed#500#group#foo => 500#1.0#Foo::zip#23
4#Cache::Memcached::Managed#500#group#foo => 500#1.1#Bar::pid#12
5#Cache::Memcached::Managed#500#group#foo => 500#1.1#Bar::pid#14
Note that index key 1 and 3 return the same backend key. This can be caused by doing multiple sets with the same key / id combination. The final list of backend keys then becomes:
500#1.0#Foo::zip#23
500#1.1#Bar::pod#47
500#1.1#Bar::pid#12
500#1.1#Bar::pid#14
If the group is to be deleted (delete_group), then the index keys are removed from the directory memcached server. And the associated data keys are removed from the data memcached server.
If the group (data) is to be fetched (group or get_group), then the superfluous index keys are removed from the directory memcached server. In this example, that would be:
1#Cache::Memcached::Managed#500#group#foo
because:
3#Cache::Memcached::Managed#500#group#foo
also refers to the data key
500#1.0#Foo::zip#23
Because of this, the lowest index key with a valid data key has become:
2#Cache::Memcached::Managed#500#group#foo
making "2" the lowest ordinal number of the index keys. In that case a special key, the lowest index key, is saved in the directory memcached server. The name of the keys is the same as the directory key for the group, postfixed with the delimiter and the string "_lowest". In this example, this would be:
Cache::Memcached::Managed#500#group#foo#_lowest
Whenever index keys are fetched, the value of this key is used to determine the start point for the generation of index keys. If, in the above example, another fetch of that group would be done, then these index keys would be generated (essentially mapping 2..5):
2#Cache::Memcached::Managed#500#group#foo
3#Cache::Memcached::Managed#500#group#foo
4#Cache::Memcached::Managed#500#group#foo
5#Cache::Memcached::Managed#500#group#foo
Since 32 bit counters are being used, about 4 billion items can be linked to a group, before a group should be deleted to completely restart. In most live situations, this overflow condition will not occur, since this mechanism was mainly intended to be able to delete groups of information from the cache. And a deletion will remove the counter and all of its associated keys, essentially starting again at 1. π
π§© REQUIRED MODULES π§©
Cache::Memcached (any)
Scalar::Util (any)
π AUTHOR π
Elizabeth Mattijsen
maintained by LNATION, <thisusedtobeanemail@gmail.com>
π HISTORY π
This module started life as an internal module at BOOKINGS Online Hotel Reservation, the foremost European on-line hotel booking portal. With approval and funding of Bookings, this module was generalized and put on CPAN, for which Elizabeth Mattijsen would like to express her gratitude. π«ΆπΈ
π COPYRIGHT π
okay bestie, legal era incoming, but it's short so stay with me π«Ά
This whole thing is copyright of these absolute icons, in chronological order:
- (C) 2005, 2006 BOOKINGS
- (C) 2007, 2008 BOOKING.COM
- (C) 2012 Elizabeth Mattijsen
- (C) 2025 LNATION
π the license bit π
So this program is free software β you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. slay, options π
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY β like, none, zero, nada π β not even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. If you wanna know the deets, go read the GNU General Public License, it's all in there fr fr.
You should have received a copy of the GNU General Public License along with this program. If you somehow didn't (tragic, honestly), write to:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
no cap: that's a real mailing address, not a vibe. the license literally says to write there. β¨
and that's the whole page, bestie, we ate π this one's from perl v5.40.1, dated 2025-04-24, and it lives under the name Cache:...Managed(3pm) in the User Contributed Perl Documentation. go forth and cache responsibly, mwah πππ¦
π¦ where this page comes from
- debian trixie: libcache-memcached-managed-perl 0.27-1 Β· GPL-2.0-or-later
- ubuntu resolute: libcache-memcached-managed-perl 0.27-1 Β· GPL-2.0-or-later
- ubuntu stonking: libcache-memcached-managed-perl 0.27-1 Β· GPL-2.0-or-later
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms