time() + 5) { return false; } return $entry; } function txiaw_home_news_cached($cacheDirectory = null, $loader = null) { if ($cacheDirectory === null) { $cacheDirectory = dirname(__DIR__) . '/Runtime/TxiawHomeNews'; } if ($loader === null) { $loader = function () { return gxl_mysql_news('field:news_id,news_cid,news_name;limit:100;order:news_addtime desc'); }; } $file = $cacheDirectory . '/news-list-v1.json'; $entry = txiaw_read_home_news_cache($file); if ($entry !== false && time() - $entry['created'] < 60) { return $entry['items']; } if (!is_dir($cacheDirectory) && !@mkdir($cacheDirectory, 0750, true) && !is_dir($cacheDirectory)) { return call_user_func($loader); } $lock = @fopen($cacheDirectory . '/news-list-v1.lock', 'c'); if ($lock === false) { return call_user_func($loader); } if (!flock($lock, LOCK_EX | LOCK_NB)) { fclose($lock); // Other requests can use a recently expired value while one request refreshes it. if ($entry !== false && time() - $entry['created'] < 300) { return $entry['items']; } // A cold cache gets a bounded wait; never keep an entire PHP pool waiting on a lock. for ($attempt = 0; $attempt < 20; $attempt++) { usleep(10000); $entry = txiaw_read_home_news_cache($file); if ($entry !== false && time() - $entry['created'] < 60) { return $entry['items']; } } // Preserve page availability if the cache filesystem or refresher fails. // The indexed query and Nginx limits still bound database work in this fallback. return call_user_func($loader); } try { // Another request may have refreshed between our first read and lock acquisition. $latest = txiaw_read_home_news_cache($file); if ($latest !== false && time() - $latest['created'] < 60) { return $latest['items']; } try { $items = call_user_func($loader); } catch (Exception $exception) { if ($entry !== false && time() - $entry['created'] < 300) { return $entry['items']; } throw $exception; } if (is_array($items)) { $encoded = json_encode(array('created' => time(), 'items' => $items)); $temporary = $encoded === false ? false : @tempnam($cacheDirectory, '.news-'); if ($temporary !== false) { @chmod($temporary, 0640); $written = @file_put_contents($temporary, $encoded); if ($written !== strlen($encoded) || !@rename($temporary, $file)) { @unlink($temporary); } } } return $items; } finally { flock($lock, LOCK_UN); fclose($lock); } }