Merge pull request #3944 from pixelfed/staging

Add optional home feed caching
This commit is contained in:
daniel 2022-12-16 00:53:18 -07:00 committed by GitHub
commit b9daa3fb48
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 196 additions and 15 deletions

View file

@ -8,6 +8,7 @@
- Server Directory ([#3762](https://github.com/pixelfed/pixelfed/pull/3762)) - Server Directory ([#3762](https://github.com/pixelfed/pixelfed/pull/3762))
- Manually verify email address (php artisan user:verifyemail) ([682f5f0f](https://github.com/pixelfed/pixelfed/commit/682f5f0f)) - Manually verify email address (php artisan user:verifyemail) ([682f5f0f](https://github.com/pixelfed/pixelfed/commit/682f5f0f))
- Manually generate in-app registration confirmation links (php artisan user:app-magic-link) ([73eb9e36](https://github.com/pixelfed/pixelfed/commit/73eb9e36)) - Manually generate in-app registration confirmation links (php artisan user:app-magic-link) ([73eb9e36](https://github.com/pixelfed/pixelfed/commit/73eb9e36))
- Optional home feed caching ([3328b367](https://github.com/pixelfed/pixelfed/commit/3328b367))
### Updates ### Updates
- Update ApiV1Controller, include self likes in favourited_by endpoint ([58b331d2](https://github.com/pixelfed/pixelfed/commit/58b331d2)) - Update ApiV1Controller, include self likes in favourited_by endpoint ([58b331d2](https://github.com/pixelfed/pixelfed/commit/58b331d2))

View file

@ -61,6 +61,7 @@ use App\Jobs\VideoPipeline\{
use App\Services\{ use App\Services\{
AccountService, AccountService,
BookmarkService,
CollectionService, CollectionService,
FollowerService, FollowerService,
InstanceService, InstanceService,
@ -768,6 +769,10 @@ class ApiV1Controller extends Controller
->whereFollowingId($target->id) ->whereFollowingId($target->id)
->delete(); ->delete();
if(config('instance.timeline.home.cached')) {
Cache::forget('pf:timelines:home:' . $user->profile_id);
}
FollowerService::remove($user->profile_id, $target->id); FollowerService::remove($user->profile_id, $target->id);
$target->decrement('followers_count'); $target->decrement('followers_count');
@ -1935,11 +1940,80 @@ class ApiV1Controller extends Controller
$limit = $request->input('limit') ?? 20; $limit = $request->input('limit') ?? 20;
$pid = $request->user()->profile_id; $pid = $request->user()->profile_id;
$following = Cache::remember('profile:following:'.$pid, now()->addMinutes(1440), function() use($pid) { $following = Cache::remember('profile:following:'.$pid, 1209600, function() use($pid) {
$following = Follower::whereProfileId($pid)->pluck('following_id'); $following = Follower::whereProfileId($pid)->pluck('following_id');
return $following->push($pid)->toArray(); return $following->push($pid)->toArray();
}); });
if(config('instance.timeline.home.cached') && (!$min && !$max)) {
$ttl = config('instance.timeline.home.cache_ttl');
$res = Cache::remember(
'pf:timelines:home:' . $pid,
$ttl,
function() use(
$following,
$limit,
$pid
) {
return Status::select(
'id',
'uri',
'caption',
'rendered',
'profile_id',
'type',
'in_reply_to_id',
'reblog_of_id',
'is_nsfw',
'scope',
'local',
'reply_count',
'comments_disabled',
'place_id',
'likes_count',
'reblogs_count',
'created_at',
'updated_at'
)
->whereIn('type', ['photo', 'photo:album', 'video', 'video:album', 'photo:video:album'])
->whereIn('profile_id', $following)
->whereIn('visibility',['public', 'unlisted', 'private'])
->orderBy('created_at', 'desc')
->limit($limit)
->get()
->map(function($s) {
$status = StatusService::get($s->id, false);
if(!$status) {
return false;
}
return $status;
})
->filter(function($s) {
return $s && isset($s['account']['id']);
})
->values()
->toArray();
});
$res = collect($res)
->map(function($s) use ($pid) {
$status = StatusService::get($s['id'], false);
if(!$status) {
return false;
}
$status['favourited'] = (bool) LikeService::liked($pid, $s['id']);
$status['bookmarked'] = (bool) BookmarkService::get($pid, $s['id']);
$status['reblogged'] = (bool) ReblogService::get($pid, $s['id']);
return $status;
})
->filter(function($s) {
return $s && isset($s['account']['id']);
})
->values()
->take($limit)
->toArray();
}
if($min || $max) { if($min || $max) {
$dir = $min ? '>' : '<'; $dir = $min ? '>' : '<';
$id = $min ?? $max; $id = $min ?? $max;

View file

@ -454,29 +454,19 @@ class PublicApiController extends Controller
$user = $request->user(); $user = $request->user();
$key = 'user:last_active_at:id:'.$user->id; $key = 'user:last_active_at:id:'.$user->id;
$ttl = now()->addMinutes(20); if(Cache::get($key) == null) {
Cache::remember($key, $ttl, function() use($user) {
$user->last_active_at = now(); $user->last_active_at = now();
$user->save(); $user->save();
return; Cache::put($key, true, 43200);
}); }
$pid = $user->profile_id; $pid = $user->profile_id;
$following = Cache::remember('profile:following:'.$pid, now()->addMinutes(1440), function() use($pid) { $following = Cache::remember('profile:following:'.$pid, 1209600, function() use($pid) {
$following = Follower::whereProfileId($pid)->pluck('following_id'); $following = Follower::whereProfileId($pid)->pluck('following_id');
return $following->push($pid)->toArray(); return $following->push($pid)->toArray();
}); });
if($recentFeed == true) {
$key = 'profile:home-timeline-cursor:'.$user->id;
$ttl = now()->addMinutes(30);
$min = Cache::remember($key, $ttl, function() use($pid) {
$res = StatusView::whereProfileId($pid)->orderByDesc('status_id')->first();
return $res ? $res->status_id : null;
});
}
$filtered = $user ? UserFilterService::filters($user->profile_id) : []; $filtered = $user ? UserFilterService::filters($user->profile_id) : [];
$types = ['photo', 'photo:album', 'video', 'video:album', 'photo:video:album']; $types = ['photo', 'photo:album', 'video', 'video:album', 'photo:video:album'];
// $types = ['photo', 'photo:album', 'video', 'video:album', 'photo:video:album', 'text']; // $types = ['photo', 'photo:album', 'video', 'video:album', 'photo:video:album', 'text'];
@ -496,6 +486,83 @@ class PublicApiController extends Controller
array_push($types, 'poll'); array_push($types, 'poll');
} }
if(config('instance.timeline.home.cached') && $limit == 6 && (!$min && !$max)) {
$ttl = config('instance.timeline.home.cache_ttl');
$res = Cache::remember(
'pf:timelines:home:' . $pid,
$ttl,
function() use(
$types,
$textOnlyReplies,
$following,
$limit,
$filtered,
$user
) {
return Status::select(
'id',
'uri',
'caption',
'rendered',
'profile_id',
'type',
'in_reply_to_id',
'reblog_of_id',
'is_nsfw',
'scope',
'local',
'reply_count',
'comments_disabled',
'place_id',
'likes_count',
'reblogs_count',
'created_at',
'updated_at'
)
->whereIn('type', $types)
->when(!$textOnlyReplies, function($q, $textOnlyReplies) {
return $q->whereNull('in_reply_to_id');
})
->whereIn('profile_id', $following)
->whereIn('visibility',['public', 'unlisted', 'private'])
->orderBy('created_at', 'desc')
->limit($limit)
->get()
->map(function($s) use ($user) {
$status = StatusService::get($s->id, false);
if(!$status) {
return false;
}
return $status;
})
->filter(function($s) use($filtered) {
return $s && in_array($s['account']['id'], $filtered) == false;
})
->values()
->toArray();
});
$res = collect($res)
->map(function($s) use ($user) {
$status = StatusService::get($s['id'], false);
if(!$status) {
return false;
}
$status['favourited'] = (bool) LikeService::liked($user->profile_id, $s['id']);
$status['bookmarked'] = (bool) BookmarkService::get($user->profile_id, $s['id']);
$status['reblogged'] = (bool) ReblogService::get($user->profile_id, $s['id']);
return $status;
})
->filter(function($s) use($filtered) {
return $s && in_array($s['account']['id'], $filtered) == false;
})
->values()
->take($limit)
->toArray();
return $res;
}
if($min || $max) { if($min || $max) {
$dir = $min ? '>' : '<'; $dir = $min ? '>' : '<';
$id = $min ?? $max; $id = $min ?? $max;

View file

@ -4,6 +4,7 @@ namespace App\Observers;
use App\Follower; use App\Follower;
use App\Services\FollowerService; use App\Services\FollowerService;
use Cache;
class FollowerObserver class FollowerObserver
{ {
@ -15,6 +16,10 @@ class FollowerObserver
*/ */
public function created(Follower $follower) public function created(Follower $follower)
{ {
if(config('instance.timeline.home.cached')) {
Cache::forget('pf:timelines:home:' . $follower->profile_id);
}
FollowerService::add($follower->profile_id, $follower->following_id); FollowerService::add($follower->profile_id, $follower->following_id);
} }

View file

@ -4,6 +4,7 @@ namespace App\Observers;
use App\Status; use App\Status;
use App\Services\ProfileStatusService; use App\Services\ProfileStatusService;
use Cache;
class StatusObserver class StatusObserver
{ {
@ -33,6 +34,10 @@ class StatusObserver
*/ */
public function updated(Status $status) public function updated(Status $status)
{ {
if(config('instance.timeline.home.cached')) {
Cache::forget('pf:timelines:home:' . $status->profile_id);
}
if(in_array($status->scope, ['public', 'unlisted']) && in_array($status->type, ['photo', 'photo:album', 'video'])) { if(in_array($status->scope, ['public', 'unlisted']) && in_array($status->type, ['photo', 'photo:album', 'video'])) {
ProfileStatusService::add($status->profile_id, $status->id); ProfileStatusService::add($status->profile_id, $status->id);
} }
@ -46,6 +51,10 @@ class StatusObserver
*/ */
public function deleted(Status $status) public function deleted(Status $status)
{ {
if(config('instance.timeline.home.cached')) {
Cache::forget('pf:timelines:home:' . $status->profile_id);
}
ProfileStatusService::delete($status->profile_id, $status->id); ProfileStatusService::delete($status->profile_id, $status->id);
} }

View file

@ -27,6 +27,7 @@ class FollowerService
RelationshipService::refresh($actor, $target); RelationshipService::refresh($actor, $target);
Redis::zadd(self::FOLLOWING_KEY . $actor, $ts, $target); Redis::zadd(self::FOLLOWING_KEY . $actor, $ts, $target);
Redis::zadd(self::FOLLOWERS_KEY . $target, $ts, $actor); Redis::zadd(self::FOLLOWERS_KEY . $target, $ts, $actor);
Cache::forget('profile:following:' . $actor);
} }
public static function remove($actor, $target) public static function remove($actor, $target)
@ -38,6 +39,7 @@ class FollowerService
AccountService::del($actor); AccountService::del($actor);
AccountService::del($target); AccountService::del($target);
RelationshipService::refresh($actor, $target); RelationshipService::refresh($actor, $target);
Cache::forget('profile:following:' . $actor);
} }
public static function followers($id, $start = 0, $stop = 10) public static function followers($id, $start = 0, $stop = 10)

View file

@ -164,7 +164,10 @@ class RestrictedNames
'explore', 'explore',
'export', 'export',
'exports', 'exports',
'external',
'f', 'f',
'fedi',
'fediverse',
'feed', 'feed',
'featured', 'featured',
'font', 'font',
@ -175,10 +178,12 @@ class RestrictedNames
'follow-me', 'follow-me',
'follow_me', 'follow_me',
'g', 'g',
'go',
'gdpr', 'gdpr',
'graph', 'graph',
'ghost', 'ghost',
'ghosts', 'ghosts',
'global',
'group', 'group',
'groups', 'groups',
'h', 'h',
@ -217,7 +222,10 @@ class RestrictedNames
'lab', 'lab',
'labs', 'labs',
'legal', 'legal',
'link',
'live', 'live',
'look',
'look-back',
'loop', 'loop',
'loops', 'loops',
'location', 'location',
@ -226,6 +234,8 @@ class RestrictedNames
'logout', 'logout',
'm', 'm',
'media', 'media',
'mini',
'micro',
'menu', 'menu',
'music', 'music',
'my2020', 'my2020',
@ -239,6 +249,7 @@ class RestrictedNames
'my2028', 'my2028',
'my2029', 'my2029',
'my2030', 'my2030',
'my',
'n', 'n',
'news', 'news',
'new', 'new',
@ -249,6 +260,8 @@ class RestrictedNames
'newsrooms', 'newsrooms',
'news-room', 'news-room',
'news-rooms', 'news-rooms',
'network',
'networks',
'o', 'o',
'oauth', 'oauth',
'official', 'official',
@ -262,6 +275,8 @@ class RestrictedNames
'password', 'password',
'portfolio', 'portfolio',
'portfolios', 'portfolios',
'pre',
'post',
'privacy', 'privacy',
'private', 'private',
'q', 'q',
@ -273,6 +288,7 @@ class RestrictedNames
'register', 'register',
'registers', 'registers',
'review', 'review',
'reviews',
'reset', 'reset',
'report', 'report',
'results', 'results',
@ -328,6 +344,8 @@ class RestrictedNames
'www', 'www',
'x', 'x',
'y', 'y',
'year',
'year-in-review',
'z', 'z',
'400', '400',
'401', '401',

View file

@ -23,6 +23,11 @@ return [
'email' => env('INSTANCE_CONTACT_EMAIL'), 'email' => env('INSTANCE_CONTACT_EMAIL'),
'timeline' => [ 'timeline' => [
'home' => [
'cached' => env('PF_HOME_TIMELINE_CACHE', false),
'cache_ttl' => env('PF_HOME_TIMELINE_CACHE_TTL', 900)
],
'local' => [ 'local' => [
'is_public' => env('INSTANCE_PUBLIC_LOCAL_TIMELINE', false) 'is_public' => env('INSTANCE_PUBLIC_LOCAL_TIMELINE', false)
], ],