Pārlūkot izejas kodu

Update ReblogService, fix cache sync issues

Daniel Supernault 10 mēneši atpakaļ
vecāks
revīzija
3de8ceca74
1 mainītis faili ar 148 papildinājumiem un 58 dzēšanām
  1. 148 58
      app/Services/ReblogService.php

+ 148 - 58
app/Services/ReblogService.php

@@ -2,67 +2,157 @@
 
 namespace App\Services;
 
+use App\Status;
 use Illuminate\Support\Facades\Cache;
 use Illuminate\Support\Facades\Redis;
-use App\Status;
+use Illuminate\Support\Lottery;
 
 class ReblogService
 {
-	const CACHE_KEY = 'pf:services:reblogs:';
-	const REBLOGS_KEY = 'pf:services:reblogs:v1:post:';
-	const COLDBOOT_KEY = 'pf:services:reblogs:v1:post_:';
-
-	public static function get($profileId, $statusId)
-	{
-		if (!Redis::zcard(self::CACHE_KEY . $profileId)) {
-			return false;
-		}
-
-		return Redis::zscore(self::CACHE_KEY . $profileId, $statusId) != null;
-	}
-
-	public static function add($profileId, $statusId)
-	{
-		return Redis::zadd(self::CACHE_KEY . $profileId, $statusId, $statusId);
-	}
-
-	public static function del($profileId, $statusId)
-	{
-		return Redis::zrem(self::CACHE_KEY . $profileId, $statusId);
-	}
-
-	public static function getPostReblogs($id, $start = 0, $stop = 10)
-	{
-		if(!Redis::zcard(self::REBLOGS_KEY . $id)) {
-			return Cache::remember(self::COLDBOOT_KEY . $id, 86400, function() use($id) {
-				return Status::whereReblogOfId($id)
-					->pluck('id')
-					->each(function($reblog) use($id) {
-						self::addPostReblog($id, $reblog);
-					})
-					->map(function($reblog) {
-						return (string) $reblog;
-					});
-			});
-		}
-		return Redis::zrange(self::REBLOGS_KEY . $id, $start, $stop);
-	}
-
-	public static function addPostReblog($parentId, $reblogId)
-	{
-		$pid = intval($parentId);
-		$id = intval($reblogId);
-		if($pid && $id) {
-			return Redis::zadd(self::REBLOGS_KEY . $pid, $id, $id);
-		}
-	}
-
-	public static function removePostReblog($parentId, $reblogId)
-	{
-		$pid = intval($parentId);
-		$id = intval($reblogId);
-		if($pid && $id) {
-			return Redis::zrem(self::REBLOGS_KEY . $pid, $id);
-		}
-	}
+    const CACHE_KEY = 'pf:services:reblogs:';
+
+    const REBLOGS_KEY = 'pf:services:reblogs:v1:post:';
+
+    const COLDBOOT_KEY = 'pf:services:reblogs:v1:post_:';
+
+    const CACHE_SKIP_KEY = 'pf:services:reblogs:skip_empty_check:';
+
+    public static function get($profileId, $statusId)
+    {
+        return Lottery::odds(1, 20)
+            ->winner(fn () => self::getFromDatabaseCheck($profileId, $statusId))
+            ->loser(fn () => self::getFromRedis($profileId, $statusId))
+            ->choose();
+    }
+
+    public static function getFromDatabaseCheck($profileId, $statusId)
+    {
+        if (! Redis::zcard(self::CACHE_KEY.$profileId)) {
+            if (Cache::has(self::CACHE_SKIP_KEY.$profileId)) {
+                return false;
+            } else {
+                self::warmCache($profileId);
+                sleep(1);
+
+                return self::getFromRedis($profileId, $statusId);
+            }
+        }
+
+        $minId = SnowflakeService::byDate(now()->subMonths(12));
+
+        if ($minId > $statusId) {
+            return Redis::zscore(self::CACHE_KEY.$profileId, $statusId) != null;
+        }
+
+        $cachedRes = (bool) Redis::zscore(self::CACHE_KEY.$profileId, $statusId) != null;
+        $databaseRes = (bool) self::getFromDatabase($profileId, $statusId);
+
+        if ($cachedRes === $databaseRes) {
+            return $cachedRes;
+        }
+
+        self::warmCache($profileId);
+        sleep(1);
+
+        return self::getFromDatabase($profileId, $statusId);
+    }
+
+    public static function getFromRedis($profileId, $statusId)
+    {
+        if (! Redis::zcard(self::CACHE_KEY.$profileId)) {
+            if (Cache::has(self::CACHE_SKIP_KEY.$profileId)) {
+                return false;
+            } else {
+                self::warmCache($profileId);
+                sleep(1);
+
+                return self::getFromDatabase($profileId, $statusId);
+            }
+        }
+
+        return Redis::zscore(self::CACHE_KEY.$profileId, $statusId) != null;
+    }
+
+    public static function getFromDatabase($profileId, $statusId)
+    {
+        return Status::whereProfileId($profileId)
+            ->where('reblog_of_id', $statusId)
+            ->exists();
+    }
+
+    public static function add($profileId, $statusId)
+    {
+        return Redis::zadd(self::CACHE_KEY.$profileId, $statusId, $statusId);
+    }
+
+    public static function count($profileId)
+    {
+        return Redis::zcard(self::CACHE_KEY.$profileId);
+    }
+
+    public static function del($profileId, $statusId)
+    {
+        return Redis::zrem(self::CACHE_KEY.$profileId, $statusId);
+    }
+
+    public static function getWarmCacheCount($profileId)
+    {
+        $minId = SnowflakeService::byDate(now()->subMonths(12));
+
+        return Status::where('id', '>', $minId)
+            ->whereProfileId($profileId)
+            ->whereNotNull('reblog_of_id')
+            ->count();
+    }
+
+    public static function warmCache($profileId)
+    {
+        Redis::del(self::CACHE_KEY.$profileId);
+        $minId = SnowflakeService::byDate(now()->subMonths(12));
+        foreach (
+            Status::where('id', '>', $minId)
+                ->whereProfileId($profileId)
+                ->whereNotNull('reblog_of_id')
+                ->lazy() as $post
+        ) {
+            self::add($profileId, $post->reblog_of_id);
+        }
+        Cache::put(self::CACHE_SKIP_KEY.$profileId, 1, now()->addHours(24));
+    }
+
+    public static function getPostReblogs($id, $start = 0, $stop = 10)
+    {
+        if (! Redis::zcard(self::REBLOGS_KEY.$id)) {
+            return Cache::remember(self::COLDBOOT_KEY.$id, 86400, function () use ($id) {
+                return Status::whereReblogOfId($id)
+                    ->pluck('id')
+                    ->each(function ($reblog) use ($id) {
+                        self::addPostReblog($id, $reblog);
+                    })
+                    ->map(function ($reblog) {
+                        return (string) $reblog;
+                    });
+            });
+        }
+
+        return Redis::zrange(self::REBLOGS_KEY.$id, $start, $stop);
+    }
+
+    public static function addPostReblog($parentId, $reblogId)
+    {
+        $pid = intval($parentId);
+        $id = intval($reblogId);
+        if ($pid && $id) {
+            return Redis::zadd(self::REBLOGS_KEY.$pid, $id, $id);
+        }
+    }
+
+    public static function removePostReblog($parentId, $reblogId)
+    {
+        $pid = intval($parentId);
+        $id = intval($reblogId);
+        if ($pid && $id) {
+            return Redis::zrem(self::REBLOGS_KEY.$pid, $id);
+        }
+    }
 }