diff --git a/app/Console/Commands/Test.php b/app/Console/Commands/Test.php index dc65ca62..85376d50 100644 --- a/app/Console/Commands/Test.php +++ b/app/Console/Commands/Test.php @@ -97,9 +97,7 @@ class Test extends Command */ public function handle() { - $authkey = "12|52|abc"; - $subAuthkey = substr($authkey, 0, strrpos($authkey, "|")); - $r = $subAuthkey; + $r = TorrentUpdated::dispatch(5); dd($r); } diff --git a/app/Console/Commands/TorrentLoadBoughtUser.php b/app/Console/Commands/TorrentLoadBoughtUser.php new file mode 100644 index 00000000..815c4ac5 --- /dev/null +++ b/app/Console/Commands/TorrentLoadBoughtUser.php @@ -0,0 +1,37 @@ +argument('torrent_id'); + LoadTorrentBoughtUsers::dispatch($torrentId); + do_log("torrentId: $torrentId"); + return Command::SUCCESS; + } +} diff --git a/app/Jobs/LoadTorrentBoughtUsers.php b/app/Jobs/LoadTorrentBoughtUsers.php new file mode 100644 index 00000000..a75a40a1 --- /dev/null +++ b/app/Jobs/LoadTorrentBoughtUsers.php @@ -0,0 +1,51 @@ +torrentId = $torrentId; + } + + /** + * Execute the job. + * + * @return void + */ + public function handle() + { + $rep = new TorrentRepository(); + $result = $rep->loadBoughtUser($this->torrentId); + do_log("result: $result"); + } + + /** + * Handle a job failure. + * + * @param \Throwable $exception + * @return void + */ + public function failed(\Throwable $exception) + { + do_log("failed: " . $exception->getMessage() . $exception->getTraceAsString(), 'error'); + } +} diff --git a/app/Repositories/TorrentRepository.php b/app/Repositories/TorrentRepository.php index dbee91b1..94343387 100644 --- a/app/Repositories/TorrentRepository.php +++ b/app/Repositories/TorrentRepository.php @@ -21,6 +21,7 @@ use App\Models\StaffMessage; use App\Models\Standard; use App\Models\Team; use App\Models\Torrent; +use App\Models\TorrentBuyLog; use App\Models\TorrentOperationLog; use App\Models\TorrentSecret; use App\Models\TorrentTag; @@ -36,6 +37,8 @@ use Nexus\Database\NexusDB; class TorrentRepository extends BaseRepository { + const BOUGHT_USER_CACHE_KEY_PREFIX = "torrent_purchasers:"; + /** * fetch torrent list * @@ -715,4 +718,41 @@ HTML; return sprintf('', nexus_trans('torrent.paid_torrent'), $verticalAlign, $size, $size); } + public function loadBoughtUser($torrentId): int + { + $size = 500; + $page = 1; + $key = $this->getBoughtUserCacheKey($torrentId); + $redis = NexusDB::redis(); + $total = 0; + while (true) { + $list = TorrentBuyLog::query()->where("torrent_id", $torrentId)->forPage($page, $size)->get(['torrent_id', 'uid']); + if ($list->isEmpty()) { + break; + } + foreach ($list as $item) { + $redis->hSet($key, $item->uid, 1); + $total += 1; + do_log(sprintf("hset %s %s 1", $key, $item->uid)); + } + $page++; + } + do_log("torrent_purchasers:$torrentId LOAD DONE, total: $total"); + if ($total > 0) { + $redis->expire($key, 86400*30); + } + return $total; + } + + public function addBoughtUserToCache($torrentId, $uid) + { + NexusDB::redis()->hSet($this->getBoughtUserCacheKey($torrentId), $uid, 1); + } + + + private function getBoughtUserCacheKey($torrentId): string + { + return self::BOUGHT_USER_CACHE_KEY_PREFIX . $torrentId; + } + } diff --git a/include/constants.php b/include/constants.php index fba577e1..ebc0735e 100644 --- a/include/constants.php +++ b/include/constants.php @@ -1,6 +1,6 @@ $msg]); + if (isset($GLOBALS['rep_dict'])) { + $d = $GLOBALS['rep_dict']; + } else { + $torrent = $GLOBALS['torrent'] ?? []; + $d = [ + "interval" => (int)\App\Repositories\TrackerRepository::MIN_ANNOUNCE_WAIT_SECOND, + "min interval" => (int)\App\Repositories\TrackerRepository::MIN_ANNOUNCE_WAIT_SECOND, + "complete" => intval($torrent['seeders'] ?? 0), + "incomplete" => intval($torrent['leechers'] ?? 0), + "peers" => [], + ]; + if (!empty($_REQUEST['compact'])) { + $d['peers'] = ''; // Change `peers` from array to string + $d['peers6'] = ''; // If peer use IPv6 address , we should add packed string in `peers6` + } + } + if (!empty($_REQUEST['event'])) { + //keep fail response, next request keep event param + $d['failure reason'] = $msg; + } else { + //avoid retry frequent + $d['warning message'] = $msg; + } + benc_resp($d); exit(); } -function warn($msg) -{ - benc_resp(['warning message' => $msg]); - exit(); -} function check_cheater($userid, $torrentid, $uploaded, $downloaded, $anctime, $seeders=0, $leechers=0){ global $cheaterdet_security,$nodetect_security, $CURUSER; diff --git a/public/announce.php b/public/announce.php index f9d6029a..f36c58b7 100644 --- a/public/announce.php +++ b/public/announce.php @@ -53,7 +53,7 @@ if (!empty($_GET['authkey'])) { if (!$isReAnnounce && !$redis->set($reAnnounceCheckByAuthKey, TIMENOW, ['nx', 'ex' => 60])) { $msg = "Request too frequent(a)"; do_log(sprintf("[ANNOUNCE] %s key: %s already exists, value: %s", $msg, $reAnnounceCheckByAuthKey, TIMENOW)); - warn($msg); + err($msg); } if ($redis->get("$authKeyInvalidKey:$authkey")) { $msg = "Invalid authkey"; @@ -89,7 +89,7 @@ $torrentReAnnounceKey = sprintf('reAnnounceCheckByInfoHash:%s:%s', $userAuthenti if (!$isReAnnounce && !$redis->set($torrentReAnnounceKey, TIMENOW, ['nx', 'ex' => 60])) { $msg = "Request too frequent(h)"; do_log(sprintf("[ANNOUNCE] %s key: %s already exists, value: %s", $msg, $torrentReAnnounceKey, TIMENOW)); - warn($msg); + err($msg); } @@ -216,6 +216,7 @@ if (!$torrent) { $redis->set("$torrentNotExistsKey:$info_hash", TIMENOW, ['ex' => 24*3600]); err("torrent not registered with this tracker"); } +$GLOBALS['torrent'] = $torrent; $torrentid = $torrent["id"]; if (isset($authKeyTid) && $authKeyTid != $torrentid) { $redis->set("$authKeyInvalidKey:$authkey", TIMENOW, ['ex' => 3600*24]); @@ -250,8 +251,8 @@ else{ if ($newnumpeers > $rsize) $limit = " ORDER BY RAND() LIMIT $rsize"; else $limit = ""; -$announce_wait = \App\Repositories\TrackerRepository::MIN_ANNOUNCE_WAIT_SECOND; +$announce_wait = \App\Repositories\TrackerRepository::MIN_ANNOUNCE_WAIT_SECOND; $fields = "id, seeder, peer_id, ip, ipv4, ipv6, port, uploaded, downloaded, userid, last_action, UNIX_TIMESTAMP(last_action) as last_action_unix_timestamp, prev_action, (".TIMENOW." - UNIX_TIMESTAMP(last_action)) AS announcetime, UNIX_TIMESTAMP(prev_action) AS prevts"; //$peerlistsql = "SELECT ".$fields." FROM peers WHERE torrent = ".$torrentid." AND connectable = 'yes' ".$only_leech_query.$limit; /** @@ -279,7 +280,7 @@ if ($compact == 1) { $rep_dict['peers'] = ''; // Change `peers` from array to string $rep_dict['peers6'] = ''; // If peer use IPv6 address , we should add packed string in `peers6` } - +$GLOBALS['rep_dict'] = $rep_dict; if ($isReAnnounce) { do_log("$log, [YES_RE_ANNOUNCE]"); benc_resp($rep_dict); @@ -440,29 +441,41 @@ if (!isset($self)) && $torrent['owner'] != $userid && get_setting("torrent.paid_torrent_enabled") == "yes" ) { - $hasBuyCacheKey = sprintf("user_has_buy_torrent:%s:%s", $userid, $torrentid); - $hasBuyCacheTime = 86400*10; - $hasBuy = \Nexus\Database\NexusDB::remember($hasBuyCacheKey, $hasBuyCacheTime, function () use($userid, $torrentid) { - $exists = \App\Models\TorrentBuyLog::query()->where('uid', $userid)->where('torrent_id', $torrentid)->exists(); - return intval($exists); - }); + $hasBuyCacheKey = \App\Repositories\TorrentRepository::BOUGHT_USER_CACHE_KEY_PREFIX . $torrentid; + $hasBuy = $redis->hGet($hasBuyCacheKey, $userid); + if ($hasBuy === false) { + //no cache + $lockName = "load_torrent_bought_user:$torrentid"; + $loadBoughtLock = new \Nexus\Database\NexusLock($lockName, 300); + if ($loadBoughtLock->get()) { + //get lock, do load + executeCommand("torrent:load_bought_user $torrentid", "string", true, false); + } else { + do_log("can not get loadBoughtLock: $lockName", 'debug'); + } + //simple cache the hasBuy result + $hasBuy = \Nexus\Database\NexusDB::remember(sprintf("user_has_buy_torrent:%s:%s", $userid, $torrentid), 86400*10, function () use($userid, $torrentid) { + $exists = \App\Models\TorrentBuyLog::query()->where('uid', $userid)->where('torrent_id', $torrentid)->exists(); + return intval($exists); + }); + } if (!$hasBuy) { $lock = new \Nexus\Database\NexusLock("buying_torrent:$userid", 5); if (!$lock->get()) { $msg = "buying torrent, wait!"; do_log("[ANNOUNCE] user: $userid, torrent: $torrentid, $msg", 'error'); - warn($msg); + err($msg); } $bonusRep = new \App\Repositories\BonusRepository(); try { $bonusRep->consumeToBuyTorrent($az['id'], $torrent['id'], 'Web'); - $redis->set($hasBuyCacheKey, 1, $hasBuyCacheTime); + $redis->hSet($hasBuyCacheKey, $userid, 1); $lock->release(); } catch (\Exception $exception) { $msg = $exception->getMessage(); do_log("[ANNOUNCE] user: $userid, torrent: $torrentid, $msg " . $exception->getTraceAsString(), 'error'); $lock->release(); - warn($msg); + err($msg); } } }