*/ const COUNTABLE_STATUS_TYPES = [ 'photo', 'photo:album', 'video', 'video:album', 'photo:video:album', ]; /** * Canonical source-of-truth status_count for a profile. */ public static function recalculateStatusCount($pid): int { return (int) Status::whereProfileId($pid) ->whereIn('type', self::COUNTABLE_STATUS_TYPES) ->count(); } /** * Canonical source-of-truth follower count for a profile. */ public static function recalculateFollowerCount($pid): int { return (int) Follower::whereFollowingId($pid)->count(); } /** * Canonical source-of-truth following count for a profile. */ public static function recalculateFollowingCount($pid): int { return (int) Follower::whereProfileId($pid)->count(); } /** * Reconcile a profile's cached count columns against source-of-truth * tables. Only writes/clears caches for columns that actually drifted. * * @param array $only Restrict to a subset of * ['statuses','followers','following']. * @return array * Per-metric before/after summary. */ public static function reconcileProfileCounts($profile, array $only = ['statuses', 'followers', 'following']): array { if (! $profile instanceof Profile) { $profile = Profile::find($profile); } if (! $profile) { return []; } $summary = []; $changed = false; if (in_array('statuses', $only, true)) { $cached = (int) $profile->status_count; $live = self::recalculateStatusCount($profile->id); $drift = $cached !== $live; if ($drift) { $profile->status_count = $live; $changed = true; } $summary['statuses'] = ['cached' => $cached, 'live' => $live, 'drifted' => $drift]; } if (in_array('followers', $only, true)) { $cached = (int) $profile->followers_count; $live = self::recalculateFollowerCount($profile->id); $drift = $cached !== $live; if ($drift) { $profile->followers_count = $live; $changed = true; } $summary['followers'] = ['cached' => $cached, 'live' => $live, 'drifted' => $drift]; } if (in_array('following', $only, true)) { $cached = (int) $profile->following_count; $live = self::recalculateFollowingCount($profile->id); $drift = $cached !== $live; if ($drift) { $profile->following_count = $live; $changed = true; } $summary['following'] = ['cached' => $cached, 'live' => $live, 'drifted' => $drift]; } if ($changed) { $profile->save(); Cache::forget('profile:status_count:'.$profile->id); Cache::forget('profile:follower_count:'.$profile->id); Cache::forget('profile:following_count:'.$profile->id); AccountService::del($profile->id); } return $summary; } public static function incrementPostCount($pid) { return Redis::zadd(self::REFRESH_CACHE_KEY, $pid, $pid); } public static function decrementPostCount($pid) { return Redis::zadd(self::REFRESH_CACHE_KEY, $pid, $pid); } public static function removeFromPostCount($pid) { return Redis::zrem(self::REFRESH_CACHE_KEY, $pid); } public static function getAllPostCountIncr($limit = -1) { return Redis::zrange(self::REFRESH_CACHE_KEY, 0, $limit); } public static function getPostCountChunk($lastId, $count) { return Redis::zrangebyscore( self::REFRESH_CACHE_KEY, '('.$lastId, '+inf', ['limit' => [0, $count]] ); } }