fu: Close-up of Fu, bringing a scoop of water to her mouth (Default)
fu ([personal profile] fu) wrote in [site community profile] changelog2011-11-16 05:25 am

[dw-free] move cgi-bin/lj*.pl files into proper modules (in cgi-bin/LJ)

[commit: http://hg.dwscoalition.org/dw-free/rev/b06a6502feca]

http://bugs.dwscoalition.org/show_bug.cgi?id=1726

Move ljrelation.pl to LJ/User.pm

Patch by [personal profile] kareila.

Files modified:
  • cgi-bin/LJ/User.pm
  • cgi-bin/ljlib.pl
  • cgi-bin/ljrelation.pl
--------------------------------------------------------------------------------
diff -r 628592b2c07e -r b06a6502feca cgi-bin/LJ/User.pm
--- a/cgi-bin/LJ/User.pm	Fri Nov 11 21:25:34 2011 +0800
+++ b/cgi-bin/LJ/User.pm	Wed Nov 16 13:27:24 2011 +0800
@@ -7247,6 +7247,7 @@
 ###  16. Entry-Related Functions
 ###  19. OpenID and Identity Functions
 ###  21. Password Functions
+###  23. Relationship Functions
 ###  24. Styles and S2-Related Functions
 
 ########################################################################
@@ -8588,6 +8589,551 @@
 
 
 ########################################################################
+###  23. Relationship Functions
+
+=head2 Relationship Functions (formerly ljrelation.pl)
+=cut
+
+# <LJFUNC>
+# name: LJ::get_reluser_id
+# des: for [dbtable[reluser2]], numbers 1 - 31999 are reserved for
+#      livejournal stuff, whereas numbers 32000-65535 are used for local sites.
+# info: If you wish to add your own hooks to this, you should define a
+#       hook "get_reluser_id" in ljlib-local.pl. No reluser2 [special[reluserdefs]]
+#        types can be a single character, those are reserved for
+#        the [dbtable[reluser]] table, so we don't have namespace problems.
+# args: type
+# des-type: the name of the type you're trying to access, e.g. "hide_comm_assoc"
+# returns: id of type, 0 means it's not a reluser2 type
+# </LJFUNC>
+sub get_reluser_id {
+    my $type = shift;
+    return 0 if length $type == 1; # must be more than a single character
+    my $val =
+        {
+            'hide_comm_assoc' => 1,
+        }->{$type}+0;
+    return $val if $val;
+    return 0 unless $type =~ /^local-/;
+    return LJ::Hooks::run_hook('get_reluser_id', $type)+0;
+}
+
+# <LJFUNC>
+# name: LJ::load_rel_user
+# des: Load user relationship information. Loads all relationships of type 'type' in
+#      which user 'userid' participates on the left side (is the source of the
+#      relationship).
+# args: db?, userid, type
+# des-userid: userid or a user hash to load relationship information for.
+# des-type: type of the relationship
+# returns: reference to an array of userids
+# </LJFUNC>
+sub load_rel_user {
+    my $db = LJ::DB::isdb( $_[0] ) ? shift : undef;
+    my ($userid, $type) = @_;
+    return undef unless $type and $userid;
+    my $u = LJ::want_user($userid);
+    $userid = LJ::want_userid($userid);
+    my $typeid = LJ::get_reluser_id($type)+0;
+    if ($typeid) {
+        # clustered reluser2 table
+        $db = LJ::get_cluster_reader($u);
+        return $db->selectcol_arrayref("SELECT targetid FROM reluser2 WHERE userid=? AND type=?",
+                                       undef, $userid, $typeid);
+    } else {
+        # non-clustered reluser global table
+        $db ||= LJ::get_db_reader();
+        return $db->selectcol_arrayref("SELECT targetid FROM reluser WHERE userid=? AND type=?",
+                                       undef, $userid, $type);
+    }
+}
+
+# <LJFUNC>
+# name: LJ::load_rel_user_cache
+# des: Loads user relationship information of the type 'type' where user
+#      'targetid' participates on the left side (is the source of the relationship)
+#      trying memcache first.  The results from this sub should be
+#      <strong>treated as inaccurate and out of date</strong>.
+# args: userid, type
+# des-userid: userid or a user hash to load relationship information for.
+# des-type: type of the relationship
+# returns: reference to an array of userids
+# </LJFUNC>
+sub load_rel_user_cache {
+    my ($userid, $type) = @_;
+    return undef unless $type && $userid;
+
+    my $u = LJ::want_user($userid);
+    return undef unless $u;
+    $userid = $u->{'userid'};
+
+    my $key = [ $userid, "reluser:$userid:$type" ];
+    my $res = LJ::MemCache::get($key);
+
+    return $res if $res;
+
+    $res = LJ::load_rel_user($userid, $type);
+
+    my $exp = time() + 60*30; # 30 min
+    LJ::MemCache::set($key, $res, $exp);
+
+    return $res;
+}
+
+# <LJFUNC>
+# name: LJ::load_rel_target
+# des: Load user relationship information. Loads all relationships of type 'type' in
+#      which user 'targetid' participates on the right side (is the target of the
+#      relationship).
+# args: db?, targetid, type
+# des-targetid: userid or a user hash to load relationship information for.
+# des-type: type of the relationship
+# returns: reference to an array of userids
+# </LJFUNC>
+sub load_rel_target {
+    my $db = LJ::DB::isdb( $_[0] ) ? shift : undef;
+    my ($targetid, $type) = @_;
+    return undef unless $type and $targetid;
+    my $u = LJ::want_user($targetid);
+    $targetid = LJ::want_userid($targetid);
+    my $typeid = LJ::get_reluser_id($type)+0;
+    if ($typeid) {
+        # clustered reluser2 table
+        $db = LJ::get_cluster_reader($u);
+        return $db->selectcol_arrayref("SELECT userid FROM reluser2 WHERE targetid=? AND type=?",
+                                       undef, $targetid, $typeid);
+    } else {
+        # non-clustered reluser global table
+        $db ||= LJ::get_db_reader();
+        return $db->selectcol_arrayref("SELECT userid FROM reluser WHERE targetid=? AND type=?",
+                                       undef, $targetid, $type);
+    }
+}
+
+# <LJFUNC>
+# name: LJ::load_rel_target_cache
+# des: Loads user relationship information of the type 'type' where user
+#      'targetid' participates on the right side (is the target of the relationship)
+#      trying memcache first.  The results from this sub should be
+#      <strong>treated as inaccurate and out of date</strong>.
+# args: targetid, type
+# des-userid: userid or a user hash to load relationship information for.
+# des-type: type of the relationship
+# returns: reference to an array of userids
+# </LJFUNC>
+sub load_rel_target_cache {
+    my ($userid, $type) = @_;
+    return undef unless $type && $userid;
+
+    my $u = LJ::want_user($userid);
+    return undef unless $u;
+    $userid = $u->{'userid'};
+
+    my $key = [ $userid, "reluser_rev:$userid:$type" ];
+    my $res = LJ::MemCache::get($key);
+
+    return $res if $res;
+
+    $res = LJ::load_rel_target($userid, $type);
+
+    my $exp = time() + 60*30; # 30 min
+    LJ::MemCache::set($key, $res, $exp);
+
+    return $res;
+}
+
+# <LJFUNC>
+# name: LJ::_get_rel_memcache
+# des: Helper function: returns memcached value for a given (userid, targetid, type) triple, if valid.
+# args: userid, targetid, type
+# des-userid: source userid, nonzero
+# des-targetid: target userid, nonzero
+# des-type: type (reluser) or typeid (rel2) of the relationship
+# returns: undef on failure, 0 or 1 depending on edge existence
+# </LJFUNC>
+sub _get_rel_memcache {
+    return undef unless @LJ::MEMCACHE_SERVERS;
+    return undef unless LJ::is_enabled('memcache_reluser');
+
+    my ($userid, $targetid, $type) = @_;
+    return undef unless $userid && $targetid && defined $type;
+
+    # memcache keys
+    my $relkey  = [$userid,   "rel:$userid:$targetid:$type"]; # rel $uid->$targetid edge
+    my $modukey = [$userid,   "relmodu:$userid:$type"      ]; # rel modtime for uid
+    my $modtkey = [$targetid, "relmodt:$targetid:$type"    ]; # rel modtime for targetid
+
+    # do a get_multi since $relkey and $modukey are both hashed on $userid
+    my $memc = LJ::MemCache::get_multi($relkey, $modukey);
+    return undef unless $memc && ref $memc eq 'HASH';
+
+    # [{0|1}, modtime]
+    my $rel = $memc->{$relkey->[1]};
+    return undef unless $rel && ref $rel eq 'ARRAY';
+
+    # check rel modtime for $userid
+    my $relmodu = $memc->{$modukey->[1]};
+    return undef if ! $relmodu || $relmodu > $rel->[1];
+
+    # check rel modtime for $targetid
+    my $relmodt = LJ::MemCache::get($modtkey);
+    return undef if ! $relmodt || $relmodt > $rel->[1];
+
+    # return memcache value if it's up-to-date
+    return $rel->[0] ? 1 : 0;
+}
+
+# <LJFUNC>
+# name: LJ::_set_rel_memcache
+# des: Helper function: sets memcache values for a given (userid, targetid, type) triple
+# args: userid, targetid, type
+# des-userid: source userid, nonzero
+# des-targetid: target userid, nonzero
+# des-type: type (reluser) or typeid (rel2) of the relationship
+# returns: 1 on success, undef on failure
+# </LJFUNC>
+sub _set_rel_memcache {
+    return 1 unless @LJ::MEMCACHE_SERVERS;
+
+    my ($userid, $targetid, $type, $val) = @_;
+    return undef unless $userid && $targetid && defined $type;
+    $val = $val ? 1 : 0;
+
+    # memcache keys
+    my $relkey  = [$userid,   "rel:$userid:$targetid:$type"]; # rel $uid->$targetid edge
+    my $modukey = [$userid,   "relmodu:$userid:$type"      ]; # rel modtime for uid
+    my $modtkey = [$targetid, "relmodt:$targetid:$type"    ]; # rel modtime for targetid
+
+    my $now = time();
+    my $exp = $now + 3600*6; # 6 hour
+    LJ::MemCache::set($relkey, [$val, $now], $exp);
+    LJ::MemCache::set($modukey, $now, $exp);
+    LJ::MemCache::set($modtkey, $now, $exp);
+
+    # Also, delete these keys, since the contents have changed.
+    LJ::MemCache::delete([$userid, "reluser:$userid:$type"]);
+    LJ::MemCache::delete([$targetid, "reluser_rev:$targetid:$type"]);
+
+    return 1;
+}
+
+# <LJFUNC>
+# name: LJ::check_rel
+# des: Checks whether two users are in a specified relationship to each other.
+# args: userid, targetid, type
+# des-userid: source userid, nonzero; may also be a user hash.
+# des-targetid: target userid, nonzero; may also be a user hash.
+# des-type: type of the relationship
+# returns: 1 if the relationship exists, 0 otherwise
+# </LJFUNC>
+sub check_rel {
+    my ($userid, $targetid, $type) = @_;
+    return undef unless $type && $userid && $targetid;
+
+    my $u = LJ::want_user($userid);
+    $userid = LJ::want_userid($userid);
+    $targetid = LJ::want_userid($targetid);
+
+    my $typeid = LJ::get_reluser_id($type)+0;
+    my $eff_type = $typeid || $type;
+
+    my $key = "$userid-$targetid-$eff_type";
+    return $LJ::REQ_CACHE_REL{$key} if defined $LJ::REQ_CACHE_REL{$key};
+
+    # did we get something from memcache?
+    my $memval = LJ::_get_rel_memcache($userid, $targetid, $eff_type);
+    return $memval if defined $memval;
+
+    # are we working on reluser or reluser2?
+    my ( $db, $table );
+    if ($typeid) {
+        # clustered reluser2 table
+        $db = LJ::get_cluster_reader($u);
+        $table = "reluser2";
+    } else {
+        # non-clustered reluser table
+        $db = LJ::get_db_reader();
+        $table = "reluser";
+    }
+
+    # get data from db, force result to be {0|1}
+    my $dbval = $db->selectrow_array("SELECT COUNT(*) FROM $table ".
+                                     "WHERE userid=? AND targetid=? AND type=? ",
+                                     undef, $userid, $targetid, $eff_type)
+        ? 1 : 0;
+
+    # set in memcache
+    LJ::_set_rel_memcache($userid, $targetid, $eff_type, $dbval);
+
+    # return and set request cache
+    return $LJ::REQ_CACHE_REL{$key} = $dbval;
+}
+
+# <LJFUNC>
+# name: LJ::set_rel
+# des: Sets relationship information for two users.
+# args: dbs?, userid, targetid, type
+# des-dbs: Deprecated; optional, a master/slave set of database handles.
+# des-userid: source userid, or a user hash
+# des-targetid: target userid, or a user hash
+# des-type: type of the relationship
+# returns: 1 if set succeeded, otherwise undef
+# </LJFUNC>
+sub set_rel {
+    my ($userid, $targetid, $type) = @_;
+    return undef unless $type and $userid and $targetid;
+
+    my $u = LJ::want_user($userid);
+    $userid = LJ::want_userid($userid);
+    $targetid = LJ::want_userid($targetid);
+
+    my $typeid = LJ::get_reluser_id($type)+0;
+    my $eff_type = $typeid || $type;
+
+    # working on reluser or reluser2?
+    my ($db, $table);
+    if ($typeid) {
+        # clustered reluser2 table
+        $db = LJ::get_cluster_master($u);
+        $table = "reluser2";
+    } else {
+        # non-clustered reluser global table
+        $db = LJ::get_db_writer();
+        $table = "reluser";
+    }
+    return undef unless $db;
+
+    # set in database
+    $db->do("REPLACE INTO $table (userid, targetid, type) VALUES (?, ?, ?)",
+            undef, $userid, $targetid, $eff_type);
+    return undef if $db->err;
+
+    # set in memcache
+    LJ::_set_rel_memcache($userid, $targetid, $eff_type, 1);
+
+    return 1;
+}
+
+# <LJFUNC>
+# name: LJ::set_rel_multi
+# des: Sets relationship edges for lists of user tuples.
+# args: edges
+# des-edges: array of arrayrefs of edges to set: [userid, targetid, type].
+#            Where:
+#            userid: source userid, or a user hash;
+#            targetid: target userid, or a user hash;
+#            type: type of the relationship.
+# returns: 1 if all sets succeeded, otherwise undef
+# </LJFUNC>
+sub set_rel_multi {
+    return _mod_rel_multi({ mode => 'set', edges => \@_ });
+}
+
+# <LJFUNC>
+# name: LJ::clear_rel_multi
+# des: Clear relationship edges for lists of user tuples.
+# args: edges
+# des-edges: array of arrayrefs of edges to clear: [userid, targetid, type].
+#            Where:
+#            userid: source userid, or a user hash;
+#            targetid: target userid, or a user hash;
+#            type: type of the relationship.
+# returns: 1 if all clears succeeded, otherwise undef
+# </LJFUNC>
+sub clear_rel_multi {
+    return _mod_rel_multi({ mode => 'clear', edges => \@_ });
+}
+
+# <LJFUNC>
+# name: LJ::_mod_rel_multi
+# des: Sets/Clears relationship edges for lists of user tuples.
+# args: keys, edges
+# des-keys: keys: mode  => {clear|set}.
+# des-edges: edges =>  array of arrayrefs of edges to set: [userid, targetid, type]
+#            Where:
+#            userid: source userid, or a user hash;
+#            targetid: target userid, or a user hash;
+#            type: type of the relationship.
+# returns: 1 if all updates succeeded, otherwise undef
+# </LJFUNC>
+sub _mod_rel_multi {
+    my $opts = shift;
+    return undef unless @{$opts->{edges}};
+
+    my $mode = $opts->{mode} eq 'clear' ? 'clear' : 'set';
+    my $memval = $mode eq 'set' ? 1 : 0;
+
+    my @reluser  = (); # [userid, targetid, type]
+    my @reluser2 = ();
+    foreach my $edge (@{$opts->{edges}}) {
+        my ($userid, $targetid, $type) = @$edge;
+        $userid = LJ::want_userid($userid);
+        $targetid = LJ::want_userid($targetid);
+        next unless $type && $userid && $targetid;
+
+        my $typeid = LJ::get_reluser_id($type)+0;
+        my $eff_type = $typeid || $type;
+
+        # working on reluser or reluser2?
+        push @{$typeid ? \@reluser2 : \@reluser}, [$userid, $targetid, $eff_type];
+    }
+
+    # now group reluser2 edges by clusterid
+    my %reluser2 = (); # cid => [userid, targetid, type]
+    my $users = LJ::load_userids(map { $_->[0] } @reluser2);
+    foreach (@reluser2) {
+        my $cid = $users->{$_->[0]}->{clusterid} or next;
+        push @{$reluser2{$cid}}, $_;
+    }
+    @reluser2 = ();
+
+    # try to get all required cluster masters before we start doing database updates
+    my %cache_dbcm = ();
+    foreach my $cid (keys %reluser2) {
+        next unless @{$reluser2{$cid}};
+
+        # return undef immediately if we won't be able to do all the updates
+        $cache_dbcm{$cid} = LJ::get_cluster_master($cid)
+            or return undef;
+    }
+
+    # if any error occurs with a cluster, we'll skip over that cluster and continue
+    # trying to process others since we've likely already done some amount of db
+    # updates already, but we'll return undef to signify that everything did not
+    # go smoothly
+    my $ret = 1;
+
+    # do clustered reluser2 updates
+    foreach my $cid (keys %cache_dbcm) {
+        # array of arrayrefs: [userid, targetid, type]
+        my @edges = @{$reluser2{$cid}};
+
+        # set in database, then in memcache.  keep the two atomic per clusterid
+        my $dbcm = $cache_dbcm{$cid};
+
+        my @vals = map { @$_ } @edges;
+
+        if ($mode eq 'set') {
+            my $bind = join(",", map { "(?,?,?)" } @edges);
+            $dbcm->do("REPLACE INTO reluser2 (userid, targetid, type) VALUES $bind",
+                      undef, @vals);
+        }
+
+        if ($mode eq 'clear') {
+            my $where = join(" OR ", map { "(userid=? AND targetid=? AND type=?)" } @edges);
+            $dbcm->do("DELETE FROM reluser2 WHERE $where", undef, @vals);
+        }
+
+        # don't update memcache if db update failed for this cluster
+        if ($dbcm->err) {
+            $ret = undef;
+            next;
+        }
+
+        # updates to this cluster succeeded, set memcache
+        LJ::_set_rel_memcache(@$_, $memval) foreach @edges;
+    }
+
+    # do global reluser updates
+    if (@reluser) {
+
+        # nothing to do after this block but return, so we can
+        # immediately return undef from here if there's a problem
+        my $dbh = LJ::get_db_writer()
+            or return undef;
+
+        my @vals = map { @$_ } @reluser;
+
+        if ($mode eq 'set') {
+            my $bind = join(",", map { "(?,?,?)" } @reluser);
+            $dbh->do("REPLACE INTO reluser (userid, targetid, type) VALUES $bind",
+                     undef, @vals);
+        }
+
+        if ($mode eq 'clear') {
+            my $where = join(" OR ", map { "userid=? AND targetid=? AND type=?" } @reluser);
+            $dbh->do("DELETE FROM reluser WHERE $where", undef, @vals);
+        }
+
+        # don't update memcache if db update failed for this cluster
+        return undef if $dbh->err;
+
+        # $_ = [userid, targetid, type] for each iteration
+        LJ::_set_rel_memcache(@$_, $memval) foreach @reluser;
+    }
+
+    return $ret;
+}
+
+
+# <LJFUNC>
+# name: LJ::clear_rel
+# des: Deletes a relationship between two users or all relationships of a particular type
+#      for one user, on either side of the relationship.
+# info: One of userid,targetid -- bit not both -- may be '*'. In that case,
+#       if, say, userid is '*', then all relationship edges with target equal to
+#       targetid and of the specified type are deleted.
+#       If both userid and targetid are numbers, just one edge is deleted.
+# args: dbs?, userid, targetid, type
+# des-dbs: Deprecated; optional, a master/slave set of database handles.
+# des-userid: source userid, or a user hash, or '*'
+# des-targetid: target userid, or a user hash, or '*'
+# des-type: type of the relationship
+# returns: 1 if clear succeeded, otherwise undef
+# </LJFUNC>
+sub clear_rel {
+    my ($userid, $targetid, $type) = @_;
+    return undef if $userid eq '*' and $targetid eq '*';
+
+    my $u;
+    $u = LJ::want_user($userid) unless $userid eq '*';
+    $userid = LJ::want_userid($userid) unless $userid eq '*';
+    $targetid = LJ::want_userid($targetid) unless $targetid eq '*';
+    return undef unless $type && $userid && $targetid;
+
+    my $typeid = LJ::get_reluser_id($type)+0;
+
+    if ($typeid) {
+        # clustered reluser2 table
+        return undef unless $u->writer;
+
+        $u->do("DELETE FROM reluser2 WHERE " . ($userid ne '*' ? "userid=$userid AND " : "") .
+               ($targetid ne '*' ? "targetid=$targetid AND " : "") . "type=$typeid");
+
+        return undef if $u->err;
+    } else {
+        # non-clustered global reluser table
+        my $dbh = LJ::get_db_writer()
+            or return undef;
+
+        my $qtype = $dbh->quote($type);
+        $dbh->do("DELETE FROM reluser WHERE " . ($userid ne '*' ? "userid=$userid AND " : "") .
+                 ($targetid ne '*' ? "targetid=$targetid AND " : "") . "type=$qtype");
+
+        return undef if $dbh->err;
+    }
+
+    # if one of userid or targetid are '*', then we need to note the modtime
+    # of the reluser edge from the specified id (the one that's not '*')
+    # so that subsequent gets on rel:userid:targetid:type will know to ignore
+    # what they got from memcache
+    my $eff_type = $typeid || $type;
+    if ($userid eq '*') {
+        LJ::MemCache::set([$targetid, "relmodt:$targetid:$eff_type"], time());
+    } elsif ($targetid eq '*') {
+        LJ::MemCache::set([$userid, "relmodu:$userid:$eff_type"], time());
+
+    # if neither userid nor targetid are '*', then just call _set_rel_memcache
+    # to update the rel:userid:targetid:type memcache key as well as the
+    # userid and targetid modtime keys
+    } else {
+        LJ::_set_rel_memcache($userid, $targetid, $eff_type, 0);
+    }
+
+    return 1;
+}
+
+########################################################################
 ###  24. Styles and S2-Related Functions
 
 =head2 Styles and S2-Related Functions (LJ)
diff -r 628592b2c07e -r b06a6502feca cgi-bin/ljlib.pl
--- a/cgi-bin/ljlib.pl	Fri Nov 11 21:25:34 2011 +0800
+++ b/cgi-bin/ljlib.pl	Wed Nov 16 13:27:24 2011 +0800
@@ -107,7 +107,6 @@
 require "ljtimeutil.pl";
 use LJ::Capabilities;
 use DW::Mood;
-require "ljrelation.pl";
 use LJ::Global::Img;  # defines LJ::Img
 
 require "$LJ::HOME/cgi-bin/ljlib-local.pl"
diff -r 628592b2c07e -r b06a6502feca cgi-bin/ljrelation.pl
--- a/cgi-bin/ljrelation.pl	Fri Nov 11 21:25:34 2011 +0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,561 +0,0 @@
-# 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.
-
-package LJ;
-use strict;
-
-# <LJFUNC>
-# name: LJ::get_reluser_id
-# des: for [dbtable[reluser2]], numbers 1 - 31999 are reserved for
-#      livejournal stuff, whereas numbers 32000-65535 are used for local sites.
-# info: If you wish to add your own hooks to this, you should define a
-#       hook "get_reluser_id" in ljlib-local.pl. No reluser2 [special[reluserdefs]]
-#        types can be a single character, those are reserved for
-#        the [dbtable[reluser]] table, so we don't have namespace problems.
-# args: type
-# des-type: the name of the type you're trying to access, e.g. "hide_comm_assoc"
-# returns: id of type, 0 means it's not a reluser2 type
-# </LJFUNC>
-sub get_reluser_id {
-    my $type = shift;
-    return 0 if length $type == 1; # must be more than a single character
-    my $val =
-        {
-            'hide_comm_assoc' => 1,
-        }->{$type}+0;
-    return $val if $val;
-    return 0 unless $type =~ /^local-/;
-    return LJ::Hooks::run_hook('get_reluser_id', $type)+0;
-}
-
-# <LJFUNC>
-# name: LJ::load_rel_user
-# des: Load user relationship information. Loads all relationships of type 'type' in
-#      which user 'userid' participates on the left side (is the source of the
-#      relationship).
-# args: db?, userid, type
-# des-userid: userid or a user hash to load relationship information for.
-# des-type: type of the relationship
-# returns: reference to an array of userids
-# </LJFUNC>
-sub load_rel_user
-{
-    my $db = LJ::DB::isdb( $_[0] ) ? shift : undef;
-    my ($userid, $type) = @_;
-    return undef unless $type and $userid;
-    my $u = LJ::want_user($userid);
-    $userid = LJ::want_userid($userid);
-    my $typeid = LJ::get_reluser_id($type)+0;
-    if ($typeid) {
-        # clustered reluser2 table
-        $db = LJ::get_cluster_reader($u);
-        return $db->selectcol_arrayref("SELECT targetid FROM reluser2 WHERE userid=? AND type=?",
-                                       undef, $userid, $typeid);
-    } else {
-        # non-clustered reluser global table
-        $db ||= LJ::get_db_reader();
-        return $db->selectcol_arrayref("SELECT targetid FROM reluser WHERE userid=? AND type=?",
-                                       undef, $userid, $type);
-    }
-}
-
-# <LJFUNC>
-# name: LJ::load_rel_user_cache
-# des: Loads user relationship information of the type 'type' where user
-#      'targetid' participates on the left side (is the source of the relationship)
-#      trying memcache first.  The results from this sub should be
-#      <strong>treated as inaccurate and out of date</strong>.
-# args: userid, type
-# des-userid: userid or a user hash to load relationship information for.
-# des-type: type of the relationship
-# returns: reference to an array of userids
-# </LJFUNC>
-sub load_rel_user_cache
-{
-    my ($userid, $type) = @_;
-    return undef unless $type && $userid;
-
-    my $u = LJ::want_user($userid);
-    return undef unless $u;
-    $userid = $u->{'userid'};
-
-    my $key = [ $userid, "reluser:$userid:$type" ];
-    my $res = LJ::MemCache::get($key);
-
-    return $res if $res;
-
-    $res = LJ::load_rel_user($userid, $type);
-
-    my $exp = time() + 60*30; # 30 min
-    LJ::MemCache::set($key, $res, $exp);
-
-    return $res;
-}
-
-# <LJFUNC>
-# name: LJ::load_rel_target
-# des: Load user relationship information. Loads all relationships of type 'type' in
-#      which user 'targetid' participates on the right side (is the target of the
-#      relationship).
-# args: db?, targetid, type
-# des-targetid: userid or a user hash to load relationship information for.
-# des-type: type of the relationship
-# returns: reference to an array of userids
-# </LJFUNC>
-sub load_rel_target
-{
-    my $db = LJ::DB::isdb( $_[0] ) ? shift : undef;
-    my ($targetid, $type) = @_;
-    return undef unless $type and $targetid;
-    my $u = LJ::want_user($targetid);
-    $targetid = LJ::want_userid($targetid);
-    my $typeid = LJ::get_reluser_id($type)+0;
-    if ($typeid) {
-        # clustered reluser2 table
-        $db = LJ::get_cluster_reader($u);
-        return $db->selectcol_arrayref("SELECT userid FROM reluser2 WHERE targetid=? AND type=?",
-                                       undef, $targetid, $typeid);
-    } else {
-        # non-clustered reluser global table
-        $db ||= LJ::get_db_reader();
-        return $db->selectcol_arrayref("SELECT userid FROM reluser WHERE targetid=? AND type=?",
-                                       undef, $targetid, $type);
-    }
-}
-
-# <LJFUNC>
-# name: LJ::load_rel_target_cache
-# des: Loads user relationship information of the type 'type' where user
-#      'targetid' participates on the right side (is the target of the relationship)
-#      trying memcache first.  The results from this sub should be
-#      <strong>treated as inaccurate and out of date</strong>.
-# args: targetid, type
-# des-userid: userid or a user hash to load relationship information for.
-# des-type: type of the relationship
-# returns: reference to an array of userids
-# </LJFUNC>
-sub load_rel_target_cache
-{
-    my ($userid, $type) = @_;
-    return undef unless $type && $userid;
-
-    my $u = LJ::want_user($userid);
-    return undef unless $u;
-    $userid = $u->{'userid'};
-
-    my $key = [ $userid, "reluser_rev:$userid:$type" ];
-    my $res = LJ::MemCache::get($key);
-
-    return $res if $res;
-
-    $res = LJ::load_rel_target($userid, $type);
-
-    my $exp = time() + 60*30; # 30 min
-    LJ::MemCache::set($key, $res, $exp);
-
-    return $res;
-}
-
-# <LJFUNC>
-# name: LJ::_get_rel_memcache
-# des: Helper function: returns memcached value for a given (userid, targetid, type) triple, if valid.
-# args: userid, targetid, type
-# des-userid: source userid, nonzero
-# des-targetid: target userid, nonzero
-# des-type: type (reluser) or typeid (rel2) of the relationship
-# returns: undef on failure, 0 or 1 depending on edge existence
-# </LJFUNC>
-sub _get_rel_memcache {
-    return undef unless @LJ::MEMCACHE_SERVERS;
-    return undef unless LJ::is_enabled('memcache_reluser');
-
-    my ($userid, $targetid, $type) = @_;
-    return undef unless $userid && $targetid && defined $type;
-
-    # memcache keys
-    my $relkey  = [$userid,   "rel:$userid:$targetid:$type"]; # rel $uid->$targetid edge
-    my $modukey = [$userid,   "relmodu:$userid:$type"      ]; # rel modtime for uid
-    my $modtkey = [$targetid, "relmodt:$targetid:$type"    ]; # rel modtime for targetid
-
-    # do a get_multi since $relkey and $modukey are both hashed on $userid
-    my $memc = LJ::MemCache::get_multi($relkey, $modukey);
-    return undef unless $memc && ref $memc eq 'HASH';
-
-    # [{0|1}, modtime]
-    my $rel = $memc->{$relkey->[1]};
-    return undef unless $rel && ref $rel eq 'ARRAY';
-
-    # check rel modtime for $userid
-    my $relmodu = $memc->{$modukey->[1]};
-    return undef if ! $relmodu || $relmodu > $rel->[1];
-
-    # check rel modtime for $targetid
-    my $relmodt = LJ::MemCache::get($modtkey);
-    return undef if ! $relmodt || $relmodt > $rel->[1];
-
-    # return memcache value if it's up-to-date
-    return $rel->[0] ? 1 : 0;
-}
-
-# <LJFUNC>
-# name: LJ::_set_rel_memcache
-# des: Helper function: sets memcache values for a given (userid, targetid, type) triple
-# args: userid, targetid, type
-# des-userid: source userid, nonzero
-# des-targetid: target userid, nonzero
-# des-type: type (reluser) or typeid (rel2) of the relationship
-# returns: 1 on success, undef on failure
-# </LJFUNC>
-sub _set_rel_memcache {
-    return 1 unless @LJ::MEMCACHE_SERVERS;
-
-    my ($userid, $targetid, $type, $val) = @_;
-    return undef unless $userid && $targetid && defined $type;
-    $val = $val ? 1 : 0;
-
-    # memcache keys
-    my $relkey  = [$userid,   "rel:$userid:$targetid:$type"]; # rel $uid->$targetid edge
-    my $modukey = [$userid,   "relmodu:$userid:$type"      ]; # rel modtime for uid
-    my $modtkey = [$targetid, "relmodt:$targetid:$type"    ]; # rel modtime for targetid
-
-    my $now = time();
-    my $exp = $now + 3600*6; # 6 hour
-    LJ::MemCache::set($relkey, [$val, $now], $exp);
-    LJ::MemCache::set($modukey, $now, $exp);
-    LJ::MemCache::set($modtkey, $now, $exp);
-
-    # Also, delete these keys, since the contents have changed.
-    LJ::MemCache::delete([$userid, "reluser:$userid:$type"]);
-    LJ::MemCache::delete([$targetid, "reluser_rev:$targetid:$type"]);
-
-    return 1;
-}
-
-# <LJFUNC>
-# name: LJ::check_rel
-# des: Checks whether two users are in a specified relationship to each other.
-# args: userid, targetid, type
-# des-userid: source userid, nonzero; may also be a user hash.
-# des-targetid: target userid, nonzero; may also be a user hash.
-# des-type: type of the relationship
-# returns: 1 if the relationship exists, 0 otherwise
-# </LJFUNC>
-sub check_rel {
-    my ($userid, $targetid, $type) = @_;
-    return undef unless $type && $userid && $targetid;
-
-    my $u = LJ::want_user($userid);
-    $userid = LJ::want_userid($userid);
-    $targetid = LJ::want_userid($targetid);
-
-    my $typeid = LJ::get_reluser_id($type)+0;
-    my $eff_type = $typeid || $type;
-
-    my $key = "$userid-$targetid-$eff_type";
-    return $LJ::REQ_CACHE_REL{$key} if defined $LJ::REQ_CACHE_REL{$key};
-
-    # did we get something from memcache?
-    my $memval = LJ::_get_rel_memcache($userid, $targetid, $eff_type);
-    return $memval if defined $memval;
-
-    # are we working on reluser or reluser2?
-    my ( $db, $table );
-    if ($typeid) {
-        # clustered reluser2 table
-        $db = LJ::get_cluster_reader($u);
-        $table = "reluser2";
-    } else {
-        # non-clustered reluser table
-        $db = LJ::get_db_reader();
-        $table = "reluser";
-    }
-
-    # get data from db, force result to be {0|1}
-    my $dbval = $db->selectrow_array("SELECT COUNT(*) FROM $table ".
-                                     "WHERE userid=? AND targetid=? AND type=? ",
-                                     undef, $userid, $targetid, $eff_type)
-        ? 1 : 0;
-
-    # set in memcache
-    LJ::_set_rel_memcache($userid, $targetid, $eff_type, $dbval);
-
-    # return and set request cache
-    return $LJ::REQ_CACHE_REL{$key} = $dbval;
-}
-
-# <LJFUNC>
-# name: LJ::set_rel
-# des: Sets relationship information for two users.
-# args: dbs?, userid, targetid, type
-# des-dbs: Deprecated; optional, a master/slave set of database handles.
-# des-userid: source userid, or a user hash
-# des-targetid: target userid, or a user hash
-# des-type: type of the relationship
-# returns: 1 if set succeeded, otherwise undef
-# </LJFUNC>
-sub set_rel {
-    my ($userid, $targetid, $type) = @_;
-    return undef unless $type and $userid and $targetid;
-
-    my $u = LJ::want_user($userid);
-    $userid = LJ::want_userid($userid);
-    $targetid = LJ::want_userid($targetid);
-
-    my $typeid = LJ::get_reluser_id($type)+0;
-    my $eff_type = $typeid || $type;
-
-    # working on reluser or reluser2?
-    my ($db, $table);
-    if ($typeid) {
-        # clustered reluser2 table
-        $db = LJ::get_cluster_master($u);
-        $table = "reluser2";
-    } else {
-        # non-clustered reluser global table
-        $db = LJ::get_db_writer();
-        $table = "reluser";
-    }
-    return undef unless $db;
-
-    # set in database
-    $db->do("REPLACE INTO $table (userid, targetid, type) VALUES (?, ?, ?)",
-            undef, $userid, $targetid, $eff_type);
-    return undef if $db->err;
-
-    # set in memcache
-    LJ::_set_rel_memcache($userid, $targetid, $eff_type, 1);
-
-    return 1;
-}
-
-# <LJFUNC>
-# name: LJ::set_rel_multi
-# des: Sets relationship edges for lists of user tuples.
-# args: edges
-# des-edges: array of arrayrefs of edges to set: [userid, targetid, type].
-#            Where:
-#            userid: source userid, or a user hash;
-#            targetid: target userid, or a user hash;
-#            type: type of the relationship.
-# returns: 1 if all sets succeeded, otherwise undef
-# </LJFUNC>
-sub set_rel_multi {
-    return _mod_rel_multi({ mode => 'set', edges => \@_ });
-}
-
-# <LJFUNC>
-# name: LJ::clear_rel_multi
-# des: Clear relationship edges for lists of user tuples.
-# args: edges
-# des-edges: array of arrayrefs of edges to clear: [userid, targetid, type].
-#            Where:
-#            userid: source userid, or a user hash;
-#            targetid: target userid, or a user hash;
-#            type: type of the relationship.
-# returns: 1 if all clears succeeded, otherwise undef
-# </LJFUNC>
-sub clear_rel_multi {
-    return _mod_rel_multi({ mode => 'clear', edges => \@_ });
-}
-
-# <LJFUNC>
-# name: LJ::_mod_rel_multi
-# des: Sets/Clears relationship edges for lists of user tuples.
-# args: keys, edges
-# des-keys: keys: mode  => {clear|set}.
-# des-edges: edges =>  array of arrayrefs of edges to set: [userid, targetid, type]
-#            Where:
-#            userid: source userid, or a user hash;
-#            targetid: target userid, or a user hash;
-#            type: type of the relationship.
-# returns: 1 if all updates succeeded, otherwise undef
-# </LJFUNC>
-sub _mod_rel_multi
-{
-    my $opts = shift;
-    return undef unless @{$opts->{edges}};
-
-    my $mode = $opts->{mode} eq 'clear' ? 'clear' : 'set';
-    my $memval = $mode eq 'set' ? 1 : 0;
-
-    my @reluser  = (); # [userid, targetid, type]
-    my @reluser2 = ();
-    foreach my $edge (@{$opts->{edges}}) {
-        my ($userid, $targetid, $type) = @$edge;
-        $userid = LJ::want_userid($userid);
-        $targetid = LJ::want_userid($targetid);
-        next unless $type && $userid && $targetid;
-
-        my $typeid = LJ::get_reluser_id($type)+0;
-        my $eff_type = $typeid || $type;
-
-        # working on reluser or reluser2?
-        push @{$typeid ? \@reluser2 : \@reluser}, [$userid, $targetid, $eff_type];
-    }
-
-    # now group reluser2 edges by clusterid
-    my %reluser2 = (); # cid => [userid, targetid, type]
-    my $users = LJ::load_userids(map { $_->[0] } @reluser2);
-    foreach (@reluser2) {
-        my $cid = $users->{$_->[0]}->{clusterid} or next;
-        push @{$reluser2{$cid}}, $_;
-    }
-    @reluser2 = ();
-
-    # try to get all required cluster masters before we start doing database updates
-    my %cache_dbcm = ();
-    foreach my $cid (keys %reluser2) {
-        next unless @{$reluser2{$cid}};
-
-        # return undef immediately if we won't be able to do all the updates
-        $cache_dbcm{$cid} = LJ::get_cluster_master($cid)
-            or return undef;
-    }
-
-    # if any error occurs with a cluster, we'll skip over that cluster and continue
-    # trying to process others since we've likely already done some amount of db
-    # updates already, but we'll return undef to signify that everything did not
-    # go smoothly
-    my $ret = 1;
-
-    # do clustered reluser2 updates
-    foreach my $cid (keys %cache_dbcm) {
-        # array of arrayrefs: [userid, targetid, type]
-        my @edges = @{$reluser2{$cid}};
-
-        # set in database, then in memcache.  keep the two atomic per clusterid
-        my $dbcm = $cache_dbcm{$cid};
-
-        my @vals = map { @$_ } @edges;
-
-        if ($mode eq 'set') {
-            my $bind = join(",", map { "(?,?,?)" } @edges);
-            $dbcm->do("REPLACE INTO reluser2 (userid, targetid, type) VALUES $bind",
-                      undef, @vals);
-        }
-
-        if ($mode eq 'clear') {
-            my $where = join(" OR ", map { "(userid=? AND targetid=? AND type=?)" } @edges);
-            $dbcm->do("DELETE FROM reluser2 WHERE $where", undef, @vals);
-        }
-
-        # don't update memcache if db update failed for this cluster
-        if ($dbcm->err) {
-            $ret = undef;
-            next;
-        }
-
-        # updates to this cluster succeeded, set memcache
-        LJ::_set_rel_memcache(@$_, $memval) foreach @edges;
-    }
-
-    # do global reluser updates
-    if (@reluser) {
-
-        # nothing to do after this block but return, so we can
-        # immediately return undef from here if there's a problem
-        my $dbh = LJ::get_db_writer()
-            or return undef;
-
-        my @vals = map { @$_ } @reluser;
-
-        if ($mode eq 'set') {
-            my $bind = join(",", map { "(?,?,?)" } @reluser);
-            $dbh->do("REPLACE INTO reluser (userid, targetid, type) VALUES $bind",
-                     undef, @vals);
-        }
-
-        if ($mode eq 'clear') {
-            my $where = join(" OR ", map { "userid=? AND targetid=? AND type=?" } @reluser);
-            $dbh->do("DELETE FROM reluser WHERE $where", undef, @vals);
-        }
-
-        # don't update memcache if db update failed for this cluster
-        return undef if $dbh->err;
-
-        # $_ = [userid, targetid, type] for each iteration
-        LJ::_set_rel_memcache(@$_, $memval) foreach @reluser;
-    }
-
-    return $ret;
-}
-
-
-# <LJFUNC>
-# name: LJ::clear_rel
-# des: Deletes a relationship between two users or all relationships of a particular type
-#      for one user, on either side of the relationship.
-# info: One of userid,targetid -- bit not both -- may be '*'. In that case,
-#       if, say, userid is '*', then all relationship edges with target equal to
-#       targetid and of the specified type are deleted.
-#       If both userid and targetid are numbers, just one edge is deleted.
-# args: dbs?, userid, targetid, type
-# des-dbs: Deprecated; optional, a master/slave set of database handles.
-# des-userid: source userid, or a user hash, or '*'
-# des-targetid: target userid, or a user hash, or '*'
-# des-type: type of the relationship
-# returns: 1 if clear succeeded, otherwise undef
-# </LJFUNC>
-sub clear_rel {
-    my ($userid, $targetid, $type) = @_;
-    return undef if $userid eq '*' and $targetid eq '*';
-
-    my $u;
-    $u = LJ::want_user($userid) unless $userid eq '*';
-    $userid = LJ::want_userid($userid) unless $userid eq '*';
-    $targetid = LJ::want_userid($targetid) unless $targetid eq '*';
-    return undef unless $type && $userid && $targetid;
-
-    my $typeid = LJ::get_reluser_id($type)+0;
-
-    if ($typeid) {
-        # clustered reluser2 table
-        return undef unless $u->writer;
-
-        $u->do("DELETE FROM reluser2 WHERE " . ($userid ne '*' ? "userid=$userid AND " : "") .
-               ($targetid ne '*' ? "targetid=$targetid AND " : "") . "type=$typeid");
-
-        return undef if $u->err;
-    } else {
-        # non-clustered global reluser table
-        my $dbh = LJ::get_db_writer()
-            or return undef;
-
-        my $qtype = $dbh->quote($type);
-        $dbh->do("DELETE FROM reluser WHERE " . ($userid ne '*' ? "userid=$userid AND " : "") .
-                 ($targetid ne '*' ? "targetid=$targetid AND " : "") . "type=$qtype");
-
-        return undef if $dbh->err;
-    }
-
-    # if one of userid or targetid are '*', then we need to note the modtime
-    # of the reluser edge from the specified id (the one that's not '*')
-    # so that subsequent gets on rel:userid:targetid:type will know to ignore
-    # what they got from memcache
-    my $eff_type = $typeid || $type;
-    if ($userid eq '*') {
-        LJ::MemCache::set([$targetid, "relmodt:$targetid:$eff_type"], time());
-    } elsif ($targetid eq '*') {
-        LJ::MemCache::set([$userid, "relmodu:$userid:$eff_type"], time());
-
-    # if neither userid nor targetid are '*', then just call _set_rel_memcache
-    # to update the rel:userid:targetid:type memcache key as well as the
-    # userid and targetid modtime keys
-    } else {
-        LJ::_set_rel_memcache($userid, $targetid, $eff_type, 0);
-    }
-
-    return 1;
-}
-
-1;
--------------------------------------------------------------------------------