# This code was forked from the LiveJournal project owned and operated # by Live Journal, Inc. The code has been modified and expanded by # Dreamwidth Studios, LLC. These files were originally licensed under # the terms of the license supplied by Live Journal, Inc, which can # currently be found at: # # http://code.livejournal.org/trac/livejournal/browser/trunk/LICENSE-LiveJournal.txt # # In accordance with the original license, this code and all its # modifications are provided under the GNU General Public License. # A copy of that license can be found in the LICENSE file included as # part of this distribution. # # Wrapper around MemCachedClient use Cache::Memcached; use strict; package LJ::MemCache; our $GET_DISABLED = 0; # NOTE: if you update the list of values stored in the cache here, you will # need to increment the version number, too. %LJ::MEMCACHE_ARRAYFMT = ( 'user' => [ qw[2 userid user caps clusterid dversion email password status statusvis statusvisdate name bdate themeid moodthemeid opt_forcemoodtheme allow_infoshow allow_contactshow allow_getljnews opt_showtalklinks opt_whocanreply opt_gettalkemail opt_htmlemail opt_mangleemail useoverrides defaultpicid has_bio is_system journaltype lang oldenc] ], 'trust_group' => [qw[2 userid groupnum groupname sortorder is_public]], # version #101 because old userpic format in memcached was an arrayref of # [width, height, ...] and widths could have been 1 before, although unlikely 'userpic' => [qw[101 width height userid fmt state picdate location flags]], 'userpic2' => [ qw[2 picid fmt width height state pictime md5base64 comment description flags location url] ], 'talk2row' => [qw[1 nodetype nodeid parenttalkid posterid datepost state]], 'usermsg' => [qw[1 journalid parent_msgid otherid timesent type]], 'oauth_consumer' => [ qw[1 consumer_id userid token secret name website createtime updatetime invalidatetime approved active] ], 'oauth_request' => [qw[1 consumer_id userid token secret createtime verifier callback]], 'oauth_access' => [qw[1 consumer_id userid token secret createtime]], ); my $memc; # memcache object sub init { my $opts = {}; my $parser_class = LJ::conf_test($LJ::MEMCACHE_USE_GETPARSERXS) ? 'Cache::Memcached::GetParserXS' : 'Cache::Memcached::GetParser'; # Eval, but we don't care about the result here. Loading errors will have been encountered # when Cache::Memcached was loaded, so we won't even see them here. This may not even return # true. eval "use $parser_class"; # Check to see if the 'new' function/method is defined in the proper namespace, othewise don't # explicitly set a parser class. Cached::Memcached may have attempted to load the XS module, and # failed. This is a reasonable check to make sure it all went OK. if ( eval 'defined &' . $parser_class . '::new' ) { $opts->{'parser_class'} = $parser_class; } $memc = Cache::Memcached->new($opts); reload_conf(); } sub set_memcache { $memc = shift; } sub get_memcache { init() unless $memc; return $memc; } sub client_stats { return $memc->{'stats'} || {}; } sub reload_conf { return $memc if eval { $memc->doesnt_want_configuration; }; $memc->set_servers( \@LJ::MEMCACHE_SERVERS ); $memc->set_debug(0); $memc->set_pref_ip( \%LJ::MEMCACHE_PREF_IP ); $memc->set_compress_threshold($LJ::MEMCACHE_COMPRESS_THRESHOLD); $memc->set_connect_timeout($LJ::MEMCACHE_CONNECT_TIMEOUT); $memc->set_cb_connect_fail($LJ::MEMCACHE_CB_CONNECT_FAIL); $memc->set_stat_callback(undef); $memc->set_readonly(1) if $ENV{LJ_MEMC_READONLY}; return $memc; } sub forget_dead_hosts { $memc->forget_dead_hosts(); } sub disconnect_all { $memc->disconnect_all(); } sub delete { # use delete time if specified return $memc->delete(@_) if defined $_[1]; # else default to 4 seconds: # version 1.1.7 vs. 1.1.6 $memc->delete( @_, 4 ) || $memc->delete(@_); } sub add { ( defined $_[1] ) ? $memc->add(@_) : $memc->add( $_[0], '', $_[2] ); } sub replace { ( defined $_[1] ) ? $memc->replace(@_) : $memc->replace( $_[0], '', $_[2] ); } sub set { ( defined $_[1] ) ? $memc->set(@_) : $memc->set( $_[0], '', $_[2] ); } sub incr { $memc->incr(@_); } sub decr { $memc->decr(@_); } sub get { return undef if $GET_DISABLED; $memc->get(@_); } sub get_multi { return {} if $GET_DISABLED || !@_; $memc->get_multi(@_); } sub _get_sock { $memc->get_sock(@_); } sub run_command { $memc->run_command(@_); } sub array_to_hash { my ( $fmtname, $ar ) = @_; my $fmt = $LJ::MEMCACHE_ARRAYFMT{$fmtname}; return undef unless $fmt; return undef unless $ar && ref $ar eq "ARRAY" && $ar->[0] == $fmt->[0]; my $hash = {}; my $ct = scalar(@$fmt); for ( my $i = 1 ; $i < $ct ; $i++ ) { $hash->{ $fmt->[$i] } = $ar->[$i]; } return $hash; } sub hash_to_array { my ( $fmtname, $hash ) = @_; my $fmt = $LJ::MEMCACHE_ARRAYFMT{$fmtname}; return undef unless $fmt; return undef unless $hash && ref $hash; my $ar = [ $fmt->[0] ]; my $ct = scalar(@$fmt); for ( my $i = 1 ; $i < $ct ; $i++ ) { $ar->[$i] = $hash->{ $fmt->[$i] }; } return $ar; } sub get_or_set { my ( $memkey, $code, $expire ) = @_; my $val = LJ::MemCache::get($memkey); return $val if $val; $val = $code->(); LJ::MemCache::set( $memkey, $val, $expire ); return $val; } 1;