Merge branch '1.0.x' into testing

This commit is contained in:
Evan Prodromou 2011-08-03 11:02:16 -04:00
commit 7d214953e3
9 changed files with 259 additions and 148 deletions

View File

@ -44,7 +44,7 @@ class Fave extends Memcached_DataObject
common_log_db_error($fave, 'INSERT', __FILE__); common_log_db_error($fave, 'INSERT', __FILE__);
return false; return false;
} }
self::blow('fave:by_notice:%d', $fave->notice_id); self::blow('fave:list:notice_id:%d', $fave->notice_id);
Event::handle('EndFavorNotice', array($profile, $notice)); Event::handle('EndFavorNotice', array($profile, $notice));
} }
@ -62,7 +62,7 @@ class Fave extends Memcached_DataObject
if (Event::handle('StartDisfavorNotice', array($profile, $notice, &$result))) { if (Event::handle('StartDisfavorNotice', array($profile, $notice, &$result))) {
$result = parent::delete(); $result = parent::delete();
self::blow('fave:by_notice:%d', $this->notice_id); self::blow('fave:list:notice_id:%d', $this->notice_id);
if ($result) { if ($result) {
Event::handle('EndDisfavorNotice', array($profile, $notice)); Event::handle('EndDisfavorNotice', array($profile, $notice));
@ -156,31 +156,4 @@ class Fave extends Memcached_DataObject
return $fav; return $fav;
} }
/**
* Grab a list of profile who have favored this notice.
*
* @return ArrayWrapper masquerading as a Fave
*/
static function byNotice($noticeId)
{
$c = self::memcache();
$key = Cache::key('fave:by_notice:' . $noticeId);
$wrapper = $c->get($key);
if (!$wrapper) {
// @fixme caching & scalability!
$fave = new Fave();
$fave->notice_id = $noticeId;
$fave->find();
$list = array();
while ($fave->fetch()) {
$list[] = clone($fave);
}
$wrapper = new ArrayWrapper($list);
$c->set($key, $wrapper);
}
return $wrapper;
}
} }

View File

@ -55,23 +55,6 @@ class File extends Memcached_DataObject
return 'http://www.facebook.com/login.php' === $url; return 'http://www.facebook.com/login.php' === $url;
} }
/**
* Get the attachments for a particlar notice.
*
* @param int $post_id
* @return array of File objects
*/
static function getAttachments($post_id) {
$file = new File();
$query = "select file.* from file join file_to_post on (file_id = file.id) where post_id = " . $file->escape($post_id);
$file = Memcached_DataObject::cachedQuery('File', $query);
$att = array();
while ($file->fetch()) {
$att[] = clone($file);
}
return $att;
}
/** /**
* Save a new file record. * Save a new file record.
* *

View File

@ -157,6 +157,44 @@ class Memcached_DataObject extends Safe_DataObject
return $result; return $result;
} }
function listGet($cls, $keyCol, $keyVals)
{
$result = array_fill_keys($keyVals, array());
$toFetch = array();
foreach ($keyVals as $keyVal) {
$l = self::cacheGet(sprintf("%s:list:%s:%s", $cls, $keyCol, $keyVal));
if ($l !== false) {
$result[$keyVal] = $l;
} else {
$toFetch[] = $keyVal;
}
}
if (count($toFetch) > 0) {
$i = DB_DataObject::factory($cls);
if (empty($i)) {
throw new Exception(_('Cannot instantiate class ' . $cls));
}
$i->whereAddIn($keyCol, $toFetch, $i->columnType($keyCol));
if ($i->find()) {
while ($i->fetch()) {
$copy = clone($i);
$copy->encache();
$result[$i->$keyCol][] = $copy;
}
}
foreach ($toFetch as $keyVal)
{
self::cacheSet(sprintf("%s:list:%s:%s", $cls, $keyCol, $keyVal),
$result[$keyVal]);
}
}
return $result;
}
function columnType($columnName) function columnType($columnName)
{ {
$keys = $this->table(); $keys = $this->table();

View File

@ -755,62 +755,33 @@ class Notice extends Memcached_DataObject
return true; return true;
} }
function getUploadedAttachment() { protected $_attachments = -1;
$post = clone $this;
$query = 'select file.url as up, file.id as i from file join file_to_post on file.id = file_id where post_id=' . $post->escape($post->id) . ' and url like "%/notice/%/file"';
$post->query($query);
$post->fetch();
if (empty($post->up) || empty($post->i)) {
$ret = false;
} else {
$ret = array($post->up, $post->i);
}
$post->free();
return $ret;
}
function hasAttachments() {
$post = clone $this;
$query = "select count(file_id) as n_attachments from file join file_to_post on (file_id = file.id) join notice on (post_id = notice.id) where post_id = " . $post->escape($post->id);
$post->query($query);
$post->fetch();
$n_attachments = intval($post->n_attachments);
$post->free();
return $n_attachments;
}
function attachments() { function attachments() {
$keypart = sprintf('notice:file_ids:%d', $this->id); if ($this->_attachments != -1) {
return $this->_attachments;
$idstr = self::cacheGet($keypart);
if ($idstr !== false) {
$ids = explode(',', $idstr);
} else {
$ids = array();
$f2p = new File_to_post;
$f2p->post_id = $this->id;
if ($f2p->find()) {
while ($f2p->fetch()) {
$ids[] = $f2p->file_id;
}
}
self::cacheSet($keypart, implode(',', $ids));
} }
$att = array(); $f2ps = Memcached_DataObject::listGet('File_to_post', 'post_id', array($this->id));
foreach ($ids as $id) { $ids = array();
$f = File::staticGet('id', $id);
if (!empty($f)) { foreach ($f2ps[$this->id] as $f2p) {
$att[] = clone($f); $ids[] = $f2p->file_id;
}
} }
return $att; $files = Memcached_DataObject::multiGet('File', 'id', $ids);
$this->_attachments = $files->fetchAll();
return $this->_attachments;
} }
function _setAttachments($attachments)
{
$this->_attachments = $attachments;
}
function publicStream($offset=0, $limit=20, $since_id=0, $max_id=0) function publicStream($offset=0, $limit=20, $since_id=0, $max_id=0)
{ {
@ -1332,6 +1303,8 @@ class Notice extends Memcached_DataObject
return $reply; return $reply;
} }
protected $_replies = -1;
/** /**
* Pull the complete list of @-reply targets for this notice. * Pull the complete list of @-reply targets for this notice.
* *
@ -1339,31 +1312,28 @@ class Notice extends Memcached_DataObject
*/ */
function getReplies() function getReplies()
{ {
$keypart = sprintf('notice:reply_ids:%d', $this->id); if ($this->_replies != -1) {
return $this->_replies;
$idstr = self::cacheGet($keypart);
if ($idstr !== false) {
$ids = explode(',', $idstr);
} else {
$ids = array();
$reply = new Reply();
$reply->selectAdd();
$reply->selectAdd('profile_id');
$reply->notice_id = $this->id;
if ($reply->find()) {
while($reply->fetch()) {
$ids[] = $reply->profile_id;
}
}
self::cacheSet($keypart, implode(',', $ids));
} }
$replyMap = Memcached_DataObject::listGet('Reply', 'notice_id', array($this->id));
$ids = array();
foreach ($replyMap[$this->id] as $reply) {
$ids[] = $reply->profile_id;
}
$this->_replies = $ids;
return $ids; return $ids;
} }
function _setReplies($replies)
{
$this->_replies = $replies;
}
/** /**
* Pull the complete list of @-reply targets for this notice. * Pull the complete list of @-reply targets for this notice.
* *
@ -1408,6 +1378,9 @@ class Notice extends Memcached_DataObject
* *
* @return array of Group objects * @return array of Group objects
*/ */
protected $_groups = -1;
function getGroups() function getGroups()
{ {
// Don't save groups for repeats // Don't save groups for repeats
@ -1416,30 +1389,30 @@ class Notice extends Memcached_DataObject
return array(); return array();
} }
if ($this->_groups != -1)
{
return $this->_groups;
}
$gis = Memcached_DataObject::listGet('Group_inbox', 'notice_id', array($this->id));
$ids = array(); $ids = array();
$keypart = sprintf('notice:groups:%d', $this->id); foreach ($gis[$this->id] as $gi)
{
$idstr = self::cacheGet($keypart); $ids[] = $gi->group_id;
}
if ($idstr !== false) {
$ids = explode(',', $idstr);
} else {
$gi = new Group_inbox();
$gi->selectAdd();
$gi->selectAdd('group_id');
$gi->notice_id = $this->id;
$ids = $gi->fetchAll('group_id');
self::cacheSet($keypart, implode(',', $ids));
}
$groups = User_group::multiGet('id', $ids); $groups = User_group::multiGet('id', $ids);
return $groups->fetchAll(); $this->_groups = $groups->fetchAll();
return $this->_groups;
}
function _setGroups($groups)
{
$this->_groups = $groups;
} }
/** /**
@ -2462,7 +2435,7 @@ class Notice extends Memcached_DataObject
function __sleep() function __sleep()
{ {
$vars = parent::__sleep(); $vars = parent::__sleep();
$skip = array('_original', '_profile'); $skip = array('_original', '_profile', '_groups', '_attachments', '_faves', '_replies');
return array_diff($vars, $skip); return array_diff($vars, $skip);
} }
@ -2503,4 +2476,120 @@ class Notice extends Memcached_DataObject
return Memcached_DataObject::pivotGet('Profile', 'id', $ids); return Memcached_DataObject::pivotGet('Profile', 'id', $ids);
} }
static function fillGroups(&$notices)
{
$ids = self::_idsOf($notices);
$gis = Memcached_DataObject::listGet('Group_inbox', 'notice_id', $ids);
$gids = array();
foreach ($gis as $id => $gi)
{
foreach ($gi as $g)
{
$gids[] = $g->group_id;
}
}
$gids = array_unique($gids);
$group = Memcached_DataObject::pivotGet('User_group', 'id', $gids);
foreach ($notices as $notice)
{
$grps = array();
$gi = $gis[$notice->id];
foreach ($gi as $g) {
$grps[] = $group[$g->group_id];
}
$notice->_setGroups($grps);
}
}
static function _idsOf(&$notices)
{
$ids = array();
foreach ($notices as $notice) {
$ids[] = $notice->id;
}
$ids = array_unique($ids);
return $ids;
}
static function fillAttachments(&$notices)
{
$ids = self::_idsOf($notices);
$f2pMap = Memcached_DataObject::listGet('File_to_post', 'post_id', $ids);
$fileIds = array();
foreach ($f2pMap as $noticeId => $f2ps) {
foreach ($f2ps as $f2p) {
$fileIds[] = $f2p->file_id;
}
}
$fileIds = array_unique($fileIds);
$fileMap = Memcached_DataObject::pivotGet('File', 'id', $fileIds);
foreach ($notices as $notice)
{
$files = array();
$f2ps = $f2pMap[$notice->id];
foreach ($f2ps as $f2p) {
$files[] = $fileMap[$f2p->file_id];
}
$notice->_setAttachments($files);
}
}
protected $_faves = -1;
/**
* All faves of this notice
*
* @return array Array of Fave objects
*/
function getFaves()
{
if ($this->_faves != -1) {
return $this->_faves;
}
$faveMap = Memcached_DataObject::listGet('Fave', 'notice_id', array($noticeId));
$this->_faves = $faveMap[$noticeId];
return $this->_faves;
}
function _setFaves($faves)
{
$this->_faves = $faves;
}
static function fillFaves(&$notices)
{
$ids = self::_idsOf($notices);
$faveMap = Memcached_DataObject::listGet('Fave', 'notice_id', $ids);
foreach ($notices as $notice) {
$notice->_setFaves($faveMap[$notice->id]);
}
}
static function fillReplies(&$notices)
{
$ids = self::_idsOf($notices);
$replyMap = Memcached_DataObject::listGet('Reply', 'notice_id', $ids);
foreach ($notices as $notice) {
$replies = $replyMap[$notice->id];
$ids = array();
foreach ($replies as $reply) {
$ids[] = $reply->profile_id;
}
$notice->_setReplies($ids);
}
}
} }

View File

@ -76,7 +76,7 @@ class AttachmentList extends Widget
*/ */
function show() function show()
{ {
$att = File::getAttachments($this->notice->id); $att = $this->notice->attachments();
if (empty($att)) return 0; if (empty($att)) return 0;
$this->showListStart(); $this->showListStart();

View File

@ -81,9 +81,16 @@ abstract class FilteringNoticeStream extends NoticeStream
break; break;
} }
while ($raw->fetch()) { $notices = $raw->fetchAll();
if ($this->filter($raw)) {
$filtered[] = clone($raw); // XXX: this should probably only be in the scoping one.
Notice::fillGroups($notices);
Notice::fillReplies($notices);
foreach ($notices as $notice) {
if ($this->filter($notice)) {
$filtered[] = $notice;
if (count($filtered) >= $total) { if (count($filtered) >= $total) {
break; break;
} }

View File

@ -124,6 +124,10 @@ class NoticeList extends Widget
static function prefill(&$notices, $avatarSize=AVATAR_STREAM_SIZE) static function prefill(&$notices, $avatarSize=AVATAR_STREAM_SIZE)
{ {
// Prefill attachments
Notice::fillAttachments($notices);
// Prefill attachments
Notice::fillFaves($notices);
// Prefill the profiles // Prefill the profiles
$profiles = Notice::fillProfiles($notices); $profiles = Notice::fillProfiles($notices);
// Prefill the avatars // Prefill the avatars

View File

@ -470,9 +470,9 @@ class ThreadedNoticeListFavesItem extends NoticeListActorsItem
{ {
function getProfiles() function getProfiles()
{ {
$fave = Fave::byNotice($this->notice->id); $faves = $this->notice->getFaves();
$profiles = array(); $profiles = array();
while ($fave->fetch()) { foreach ($faves as $fave) {
$profiles[] = $fave->user_id; $profiles[] = $fave->user_id;
} }
return $profiles; return $profiles;

View File

@ -20,20 +20,31 @@
define('INSTALLDIR', realpath(dirname(__FILE__) . '/..')); define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
$shortoptions = 'u:n:b:g:j:t:x:z:'; $shortoptions = 'b:g:j:n:t:u:w:x:z:';
$longoptions = array('users=', 'notices=', 'subscriptions=', 'groups=', 'joins=', 'tags=', 'prefix='); $longoptions = array(
'subscriptions=',
'groups=',
'joins=',
'notices=',
'tags=',
'users=',
'words=',
'prefix=',
'groupprefix'
);
$helptext = <<<END_OF_CREATESIM_HELP $helptext = <<<END_OF_CREATESIM_HELP
Creates a lot of test users and notices to (loosely) simulate a real server. Creates a lot of test users and notices to (loosely) simulate a real server.
-u --users Number of users (default 100)
-n --notices Average notices per user (default 100)
-b --subscriptions Average subscriptions per user (default no. users/20) -b --subscriptions Average subscriptions per user (default no. users/20)
-g --groups Number of groups (default 20) -g --groups Number of groups (default 20)
-j --joins Number of groups per user (default 5) -j --joins Number of groups per user (default 5)
-n --notices Average notices per user (default 100)
-t --tags Number of distinct hash tags (default 10000) -t --tags Number of distinct hash tags (default 10000)
-x --prefix User name prefix (default 'testuser') -u --users Number of users (default 100)
-w --words Words file (default '/usr/share/dict/words') -w --words Words file (default '/usr/share/dict/words')
-x --prefix User name prefix (default 'testuser')
-z --groupprefix Group name prefix (default 'testgroup')
END_OF_CREATESIM_HELP; END_OF_CREATESIM_HELP;
@ -300,6 +311,8 @@ function main($usercount, $groupcount, $noticeavg, $subsavg, $joinsavg, $tagmax)
} }
} }
$defaultWordfile = '/usr/share/dict/words';
$usercount = (have_option('u', 'users')) ? get_option_value('u', 'users') : 100; $usercount = (have_option('u', 'users')) ? get_option_value('u', 'users') : 100;
$groupcount = (have_option('g', 'groups')) ? get_option_value('g', 'groups') : 20; $groupcount = (have_option('g', 'groups')) ? get_option_value('g', 'groups') : 20;
$noticeavg = (have_option('n', 'notices')) ? get_option_value('n', 'notices') : 100; $noticeavg = (have_option('n', 'notices')) ? get_option_value('n', 'notices') : 100;
@ -308,11 +321,15 @@ $joinsavg = (have_option('j', 'joins')) ? get_option_value('j', 'joins') : 5;
$tagmax = (have_option('t', 'tags')) ? get_option_value('t', 'tags') : 10000; $tagmax = (have_option('t', 'tags')) ? get_option_value('t', 'tags') : 10000;
$userprefix = (have_option('x', 'prefix')) ? get_option_value('x', 'prefix') : 'testuser'; $userprefix = (have_option('x', 'prefix')) ? get_option_value('x', 'prefix') : 'testuser';
$groupprefix = (have_option('z', 'groupprefix')) ? get_option_value('z', 'groupprefix') : 'testgroup'; $groupprefix = (have_option('z', 'groupprefix')) ? get_option_value('z', 'groupprefix') : 'testgroup';
$wordsfile = (have_option('w', 'words')) ? get_option_value('w', 'words') : '/usr/share/dict/words'; $wordsfile = (have_option('w', 'words')) ? get_option_value('w', 'words') : $defaultWordfile;
if (is_readable($wordsfile)) { if (is_readable($wordsfile)) {
$words = file($wordsfile); $words = file($wordsfile);
} else { } else {
if ($wordsfile != $defaultWordfile) {
// user specified words file couldn't be read
throw new Exception("Couldn't read words file: {$wordfile}.");
}
$words = null; $words = null;
} }