ReblogService.php 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. <?php
  2. namespace App\Services;
  3. use App\Status;
  4. use Illuminate\Support\Facades\Cache;
  5. use Illuminate\Support\Facades\Redis;
  6. use Illuminate\Support\Lottery;
  7. class ReblogService
  8. {
  9. const CACHE_KEY = 'pf:services:reblogs:';
  10. const REBLOGS_KEY = 'pf:services:reblogs:v1:post:';
  11. const COLDBOOT_KEY = 'pf:services:reblogs:v1:post_:';
  12. const CACHE_SKIP_KEY = 'pf:services:reblogs:skip_empty_check:';
  13. public static function get($profileId, $statusId)
  14. {
  15. return Lottery::odds(1, 20)
  16. ->winner(fn () => self::getFromDatabaseCheck($profileId, $statusId))
  17. ->loser(fn () => self::getFromRedis($profileId, $statusId))
  18. ->choose();
  19. }
  20. public static function getFromDatabaseCheck($profileId, $statusId)
  21. {
  22. if (! Redis::zcard(self::CACHE_KEY.$profileId)) {
  23. if (Cache::has(self::CACHE_SKIP_KEY.$profileId)) {
  24. return false;
  25. } else {
  26. self::warmCache($profileId);
  27. sleep(1);
  28. return self::getFromRedis($profileId, $statusId);
  29. }
  30. }
  31. $minId = SnowflakeService::byDate(now()->subMonths(12));
  32. if ($minId > $statusId) {
  33. return Redis::zscore(self::CACHE_KEY.$profileId, $statusId) != null;
  34. }
  35. $cachedRes = (bool) Redis::zscore(self::CACHE_KEY.$profileId, $statusId) != null;
  36. $databaseRes = (bool) self::getFromDatabase($profileId, $statusId);
  37. if ($cachedRes === $databaseRes) {
  38. return $cachedRes;
  39. }
  40. self::warmCache($profileId);
  41. sleep(1);
  42. return self::getFromDatabase($profileId, $statusId);
  43. }
  44. public static function getFromRedis($profileId, $statusId)
  45. {
  46. if (! Redis::zcard(self::CACHE_KEY.$profileId)) {
  47. if (Cache::has(self::CACHE_SKIP_KEY.$profileId)) {
  48. return false;
  49. } else {
  50. self::warmCache($profileId);
  51. sleep(1);
  52. return self::getFromDatabase($profileId, $statusId);
  53. }
  54. }
  55. return Redis::zscore(self::CACHE_KEY.$profileId, $statusId) != null;
  56. }
  57. public static function getFromDatabase($profileId, $statusId)
  58. {
  59. return Status::whereProfileId($profileId)
  60. ->where('reblog_of_id', $statusId)
  61. ->exists();
  62. }
  63. public static function add($profileId, $statusId)
  64. {
  65. return Redis::zadd(self::CACHE_KEY.$profileId, $statusId, $statusId);
  66. }
  67. public static function count($profileId)
  68. {
  69. return Redis::zcard(self::CACHE_KEY.$profileId);
  70. }
  71. public static function del($profileId, $statusId)
  72. {
  73. return Redis::zrem(self::CACHE_KEY.$profileId, $statusId);
  74. }
  75. public static function getWarmCacheCount($profileId)
  76. {
  77. $minId = SnowflakeService::byDate(now()->subMonths(12));
  78. return Status::where('id', '>', $minId)
  79. ->whereProfileId($profileId)
  80. ->whereNotNull('reblog_of_id')
  81. ->count();
  82. }
  83. public static function warmCache($profileId)
  84. {
  85. Redis::del(self::CACHE_KEY.$profileId);
  86. $minId = SnowflakeService::byDate(now()->subMonths(12));
  87. foreach (
  88. Status::where('id', '>', $minId)
  89. ->whereProfileId($profileId)
  90. ->whereNotNull('reblog_of_id')
  91. ->lazy() as $post
  92. ) {
  93. self::add($profileId, $post->reblog_of_id);
  94. }
  95. Cache::put(self::CACHE_SKIP_KEY.$profileId, 1, now()->addHours(24));
  96. }
  97. public static function getPostReblogs($id, $start = 0, $stop = 10)
  98. {
  99. if (! Redis::zcard(self::REBLOGS_KEY.$id)) {
  100. return Cache::remember(self::COLDBOOT_KEY.$id, 86400, function () use ($id) {
  101. return Status::whereReblogOfId($id)
  102. ->pluck('id')
  103. ->each(function ($reblog) use ($id) {
  104. self::addPostReblog($id, $reblog);
  105. })
  106. ->map(function ($reblog) {
  107. return (string) $reblog;
  108. });
  109. });
  110. }
  111. return Redis::zrange(self::REBLOGS_KEY.$id, $start, $stop);
  112. }
  113. public static function addPostReblog($parentId, $reblogId)
  114. {
  115. $pid = intval($parentId);
  116. $id = intval($reblogId);
  117. if ($pid && $id) {
  118. return Redis::zadd(self::REBLOGS_KEY.$pid, $id, $id);
  119. }
  120. }
  121. public static function removePostReblog($parentId, $reblogId)
  122. {
  123. $pid = intval($parentId);
  124. $id = intval($reblogId);
  125. if ($pid && $id) {
  126. return Redis::zrem(self::REBLOGS_KEY.$pid, $id);
  127. }
  128. }
  129. }