Refactor following & relationship logic. Replace FollowerObserver with FollowerService and added RelationshipService to cache results. Removed NotificationTransformer includes and replaced with cached services to improve performance and reduce database queries.

pull/2978/head
Daniel Supernault 2021-10-07 03:27:13 -06:00
rodzic 0a8eb81bf0
commit 80d9b9399a
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 0DEF1C662C9033F7
8 zmienionych plików z 151 dodań i 139 usunięć

Wyświetl plik

@ -55,6 +55,7 @@ use App\Services\{
MediaPathService,
PublicTimelineService,
ProfileService,
RelationshipService,
SearchApiV2Service,
StatusService,
MediaBlocklistService
@ -551,7 +552,7 @@ class ApiV1Controller extends Controller
*
* @param array|integer $id
*
* @return \App\Transformer\Api\RelationshipTransformer
* @return \App\Services\RelationshipService
*/
public function accountRelationshipsById(Request $request)
{
@ -563,12 +564,9 @@ class ApiV1Controller extends Controller
]);
$pid = $request->user()->profile_id ?? $request->user()->profile->id;
$ids = collect($request->input('id'));
$filtered = $ids->filter(function($v) use($pid) {
return $v != $pid;
$res = $ids->map(function($id) use($pid) {
return RelationshipService::get($pid, $id);
});
$relations = Profile::whereNull('status')->findOrFail($filtered->values());
$fractal = new Fractal\Resource\Collection($relations, new RelationshipTransformer());
$res = $this->fractal->createData($fractal)->toArray();
return response()->json($res);
}

Wyświetl plik

@ -12,6 +12,7 @@ use Auth, Cache;
use Illuminate\Http\Request;
use App\Jobs\FollowPipeline\FollowPipeline;
use App\Util\ActivityPub\Helpers;
use App\Services\FollowerService;
class FollowerController extends Controller
{
@ -70,7 +71,9 @@ class FollowerController extends Controller
]);
if($remote == true && config('federation.activitypub.remoteFollow') == true) {
$this->sendFollow($user, $target);
}
}
FollowerService::add($user->id, $target->id);
} elseif ($private == false && $isFollowing == 0) {
if($user->following()->count() >= Follower::MAX_FOLLOWING) {
abort(400, 'You cannot follow more than ' . Follower::MAX_FOLLOWING . ' accounts');
@ -87,6 +90,7 @@ class FollowerController extends Controller
if($remote == true && config('federation.activitypub.remoteFollow') == true) {
$this->sendFollow($user, $target);
}
FollowerService::add($user->id, $target->id);
FollowPipeline::dispatch($follower);
} else {
if($force == true) {
@ -101,6 +105,7 @@ class FollowerController extends Controller
Follower::whereProfileId($user->id)
->whereFollowingId($target->id)
->delete();
FollowerService::remove($user->id, $target->id);
}
}

Wyświetl plik

@ -1,64 +0,0 @@
<?php
namespace App\Observers;
use App\Follower;
use App\Services\FollowerService;
class FollowerObserver
{
/**
* Handle the Follower "created" event.
*
* @param \App\Models\Follower $follower
* @return void
*/
public function created(Follower $follower)
{
FollowerService::add($follower->profile_id, $follower->following_id);
}
/**
* Handle the Follower "updated" event.
*
* @param \App\Models\Follower $follower
* @return void
*/
public function updated(Follower $follower)
{
FollowerService::add($follower->profile_id, $follower->following_id);
}
/**
* Handle the Follower "deleted" event.
*
* @param \App\Models\Follower $follower
* @return void
*/
public function deleted(Follower $follower)
{
FollowerService::remove($follower->profile_id, $follower->following_id);
}
/**
* Handle the Follower "restored" event.
*
* @param \App\Models\Follower $follower
* @return void
*/
public function restored(Follower $follower)
{
FollowerService::add($follower->profile_id, $follower->following_id);
}
/**
* Handle the Follower "force deleted" event.
*
* @param \App\Models\Follower $follower
* @return void
*/
public function forceDeleted(Follower $follower)
{
FollowerService::remove($follower->profile_id, $follower->following_id);
}
}

Wyświetl plik

@ -4,7 +4,6 @@ namespace App\Providers;
use App\Observers\{
AvatarObserver,
FollowerObserver,
LikeObserver,
NotificationObserver,
ModLogObserver,
@ -15,7 +14,6 @@ use App\Observers\{
};
use App\{
Avatar,
Follower,
Like,
Notification,
ModLog,
@ -50,7 +48,6 @@ class AppServiceProvider extends ServiceProvider
StatusHashtag::observe(StatusHashtagObserver::class);
User::observe(UserObserver::class);
UserFilter::observe(UserFilterObserver::class);
Follower::observe(FollowerObserver::class);
Horizon::auth(function ($request) {
return Auth::check() && $request->user()->is_admin;
});

Wyświetl plik

@ -17,12 +17,14 @@ class FollowerService
public static function add($actor, $target)
{
RelationshipService::refresh($actor, $target);
Redis::zadd(self::FOLLOWING_KEY . $actor, $target, $target);
Redis::zadd(self::FOLLOWERS_KEY . $target, $actor, $actor);
}
public static function remove($actor, $target)
{
RelationshipService::refresh($actor, $target);
Redis::zrem(self::FOLLOWING_KEY . $actor, $target);
Redis::zrem(self::FOLLOWERS_KEY . $target, $actor);
Cache::forget('pf:services:follow:audience:' . $actor);

Wyświetl plik

@ -0,0 +1,86 @@
<?php
namespace App\Services;
use Illuminate\Support\Facades\Cache;
use App\Follower;
use App\FollowRequest;
use App\Profile;
use App\UserFilter;
class RelationshipService
{
const CACHE_KEY = 'pf:services:urel:';
public static function get($aid, $tid)
{
$actor = AccountService::get($aid);
$target = AccountService::get($tid);
if(!$actor || !$target) {
return self::defaultRelation($tid);
}
if($actor['id'] === $target['id']) {
return self::defaultRelation($tid);
}
return Cache::remember(self::key("a_{$aid}:t_{$tid}"), 1209600, function() use($aid, $tid) {
return [
'id' => (string) $tid,
'following' => Follower::whereProfileId($aid)->whereFollowingId($tid)->exists(),
'followed_by' => Follower::whereProfileId($tid)->whereFollowingId($aid)->exists(),
'blocking' => UserFilter::whereUserId($aid)
->whereFilterableType('App\Profile')
->whereFilterableId($tid)
->whereFilterType('block')
->exists(),
'muting' => UserFilter::whereUserId($aid)
->whereFilterableType('App\Profile')
->whereFilterableId($tid)
->whereFilterType('mute')
->exists(),
'muting_notifications' => null,
'requested' => FollowRequest::whereFollowerId($aid)
->whereFollowingId($tid)
->exists(),
'domain_blocking' => null,
'showing_reblogs' => null,
'endorsed' => false
];
});
}
public static function delete($aid, $tid)
{
return Cache::forget(self::key("a_{$aid}:t_{$tid}"));
}
public static function refresh($aid, $tid)
{
self::delete($tid, $aid);
self::delete($aid, $tid);
self::get($tid, $aid);
return self::get($aid, $tid);
}
public static function defaultRelation($tid)
{
return [
'id' => (string) $tid,
'following' => false,
'followed_by' => false,
'blocking' => false,
'muting' => false,
'muting_notifications' => null,
'requested' => false,
'domain_blocking' => null,
'showing_reblogs' => null,
'endorsed' => false
];
}
protected static function key($suffix)
{
return self::CACHE_KEY . $suffix;
}
}

Wyświetl plik

@ -2,50 +2,65 @@
namespace App\Transformer\Api;
use App\{
Notification,
Status
};
use App\Notification;
use App\Services\AccountService;
use App\Services\HashidService;
use App\Services\StatusService;
use League\Fractal;
class NotificationTransformer extends Fractal\TransformerAbstract
{
protected $defaultIncludes = [
'account',
'status',
'relationship',
'modlog',
'tagged'
// 'relationship',
];
public function transform(Notification $notification)
{
return [
$res = [
'id' => (string) $notification->id,
'type' => $this->replaceTypeVerb($notification->action),
'created_at' => (string) $notification->created_at->format('c'),
];
}
public function includeAccount(Notification $notification)
{
return $this->item($notification->actor, new AccountTransformer());
}
$n = $notification;
if($n->item_id && $n->item_type == 'App\Status' && in_array($n->action, ['group:comment'])) {
$status = $n->status;
$res['group_id'] = $status->group_id;
public function includeStatus(Notification $notification)
{
$item = $notification;
if($item->item_id && $item->item_type == 'App\Status') {
$status = Status::with('media')->find($item->item_id);
if($status) {
return $this->item($status, new StatusTransformer());
} else {
return null;
if($n->action == 'group:comment') {
$res['group_post_url'] = GroupPost::whereStatusId($status->id)->first()->url();
}
} else {
return null;
}
if(in_array($n->action, ['group.join.approved', 'group.join.rejected', 'group.like'])) {
$res['group'] = GroupService::get($n->item_id);
}
if($n->actor_id) {
$res['account'] = AccountService::get($n->actor_id);
}
if($n->item_id && $n->item_type == 'App\Status') {
$res['status'] = StatusService::get($n->item_id, false);
}
if($n->item_id && $n->item_type == 'App\ModLog') {
$ml = $n->item;
$res['modlog'] = [
'id' => $ml->object_uid,
'url' => url('/i/admin/users/modlogs/' . $ml->object_uid)
];
}
if($n->item_id && $n->item_type == 'App\MediaTag') {
$ml = $n->item;
$res['tagged'] = [
'username' => $ml->tagged_username,
'post_url' => '/p/'.HashidService::encode($ml->status_id)
];
}
return $res;
}
public function replaceTypeVerb($verb)
@ -57,13 +72,21 @@ class NotificationTransformer extends Fractal\TransformerAbstract
'reblog' => 'share',
'share' => 'share',
'like' => 'favourite',
'group:like' => 'favourite',
'comment' => 'comment',
'admin.user.modlog.comment' => 'modlog',
'tagged' => 'tagged',
'group:comment' => 'group:comment',
'story:react' => 'story:react',
'story:comment' => 'story:comment'
'story:comment' => 'story:comment',
'group:join:approved' => 'group:join:approved',
'group:join:rejected' => 'group:join:rejected'
];
if(!isset($verbs[$verb])) {
return $verb;
}
return $verbs[$verb];
}
@ -71,42 +94,4 @@ class NotificationTransformer extends Fractal\TransformerAbstract
{
return $this->item($notification->actor, new RelationshipTransformer());
}
public function includeModlog(Notification $notification)
{
$n = $notification;
if($n->item_id && $n->item_type == 'App\ModLog') {
$ml = $n->item;
if(!empty($ml)) {
$res = $this->item($ml, function($ml) {
return [
'id' => $ml->object_uid,
'url' => url('/i/admin/users/modlogs/' . $ml->object_uid)
];
});
return $res;
} else {
return null;
}
} else {
return null;
}
}
public function includeTagged(Notification $notification)
{
$n = $notification;
if($n->item_id && $n->item_type == 'App\MediaTag') {
$ml = $n->item;
$res = $this->item($ml, function($ml) {
return [
'username' => $ml->tagged_username,
'post_url' => '/p/'.HashidService::encode($ml->status_id)
];
});
return $res;
} else {
return null;
}
}
}

Wyświetl plik

@ -455,6 +455,7 @@ class Inbox
Cache::forget('profile:follower_count:'.$actor->id);
Cache::forget('profile:following_count:'.$target->id);
Cache::forget('profile:following_count:'.$actor->id);
FollowerService::add($actor->id, $target->id);
} else {
$follower = new Follower;
@ -464,6 +465,7 @@ class Inbox
$follower->save();
FollowPipeline::dispatch($follower);
FollowerService::add($actor->id, $target->id);
// send Accept to remote profile
$accept = [
@ -722,6 +724,7 @@ class Inbox
->whereItemId($following->id)
->whereItemType('App\Profile')
->forceDelete();
FollowerService::remove($profile->id, $following->id);
break;
case 'Like':