put_lastmodified() の負荷軽減(案)

修正

(PukiWiki 1.4.7)

  1. 関数 lastmodified_add() を新規に作成しました。
    • この関数は、file_write() でput_lastmodified() の代わりにコールされます。
      従来: file_write() => put_lastmodified()
      今後: file_write() => lastmodified_add() [必要なら => put_lastmodified()]
    • put_lastmodified() はディレクトリを走査するため、ページ数に比例した負荷(待ち時間)が発生します。
    • lastmodified_add()は、可能であれば put_lastmodified() の呼び出しをキャンセルし、できるだけ最小限の手順で必要な処理を行います。
    • put_lastmodified() がコールされる条件は以下の通りです:
      • RecentChanges のキャッシュファイル recent.dat が存在しない時
      • あらかじめ大目に recent.dat に記録しておいた件数(デフォルト:10件)を越えてページの削除が行われた時 (RecentChangesを設定通りの件数で表示できなくなるため。ただしページの更新などが行われれば、余剰分は既定の件数までを限度に回復する。また再作成される度に、余剰分は回復する)
      • AutoLinkが有効である時 (常にディレクトリ走査を要求しているため)
  2. put_lastmodified() が情報を格納するキャッシュファイル recent.dat について、必要最小限の件数だけ情報を保存する様になりました。
    • 従来は常に全ページのページ名とその時刻を保存していたため、ページ数に比例した書き込み負荷が発生していました。

関連

メッセージ

official の WebTrack/59(←削除予定) から移動してきました。

ページ更新時の負荷の大部分が put_lastmodified() によるもの*1なので、その負荷軽減の案です。

official ではページ数が 2450 ページ弱*2あるので、更新時に get_filetime($page) が 2450 回呼び出されます。タイムスタンプを取得すべきは 2450 分の 1 ページのため、その他は recent.dat に記録されたタイムスタンプを使いまわすことができれば処理時間の短縮が可能になります。

ページ数が 1000 単位になった場合、処理時間は大きく変わるはずです。

以下に案として挙げておきます。

diff -ur /org/file.php /dev/file.php
--- /org/file.php
+++ /dev/file.php
@@ -259,20 +259,68 @@
 // Update RecentChanges
 function put_lastmodified()
 {
-	global $maxshow, $whatsnew, $non_list, $autolink;
+	global $maxshow, $whatsnew, $non_list, $autolink, $vars;
 
 	if (PKWK_READONLY) return; // Do nothing
 
-	$pages = get_existpages();
+	$term = 60 * 60;// 1時間以上経過していれば作り直す
+
+	$recentFile = CACHE_DIR . 'recent.dat';
+	$recentTimefile = $recentFile . 'time';
+	
+	$timeFlag   = (file_exists($recentTimefile) && time() - $term < filemtime($recentTimefile));
+	$RecentFlag = (file_exists($recentFile) && $timeFlag);
+
 	$recent_pages = array();
 	$non_list_pattern = '/' . $non_list . '/';
-	foreach($pages as $page)
-		if ($page != $whatsnew && ! preg_match($non_list_pattern, $page))
-			$recent_pages[$page] = get_filetime($page);
+	if ($RecentFlag) {// recent.dat を使用する.
+		$varsPage = (isset($vars['page'])) ? $vars['page'] : '';// 空になる可能性は?
+
+		$fp = @fopen($recentFile, 'r');
+
+		// 不整合を防ぐため (排他的ロック && ノーブロック)
+		$wouldblock = true;
+		if ($fp && flock($fp, LOCK_EX + LOCK_NB, $wouldblock) && ! $wouldblock) {
+			$lines = file($recentFile);
+			$lines = str_replace("\n", '', $lines);
+			foreach ($lines as $line) {
+				if (empty($line)) {
+					break;// ないはず.
+				}
+				list($getTime, $getPage) = explode("\t", $line);
+				if ($getPage != $whatsnew && ! preg_match($non_list_pattern, $getPage)) {
+					$recent_pages[$getPage] = $getTime;
+				}
+			}
+			if (is_page($varsPage) && ! preg_match($non_list_pattern, $varsPage)) {
+				$recent_pages[$varsPage] = get_filetime($varsPage);
+			} elseif (isset($recent_pages[$varsPage])) {
+				unset($recent_pages[$varsPage]);
+			}
+		} else {
+			$RecentFlag = false;
+		}
+	}
+
+	// 通常の処理 (排他的ロック中に遭遇した場合も諦めてこちら)
+	// ロックで弾かれた場合、処理が追いつくことはないはず・・・
+	if (! $RecentFlag) {
+		$pages = get_existpages();
+		foreach($pages as $page) {
+			if ($page != $whatsnew && ! preg_match($non_list_pattern, $page)) {
+				$recent_pages[$page] = get_filetime($page);
+			}
+		}
+		@fopen($recentTimefile, 'w');// ファイルを作るだけ
+	}
 
 	// Sort decending order of last-modification date
 	arsort($recent_pages, SORT_NUMERIC);
 
+	if (isset($fp)) {
+		flock($fp, LOCK_UN);
+	}
+
 	// Create recent.dat (for recent.inc.php)
 	$fp = fopen(CACHE_DIR . 'recent.dat', 'w') or
 		die_message('Cannot write cache file ' .

ブラウザ複数窓, sleep() などで実験はしましたが、実用に耐えうるか(整合性が保てるか)が良く分からないので、上記処理は 1 時間に 1 度は現在と同じ処理をするようにしています。更新処理を行なった後 1 時間は軽減される、といった感じです。


コメント

ここまでが WebTrack/59 でのコメントです。



recent.datの内部構造、想定している本来の利用方法について


コメント: バッファ操作?


コメント



*1 オートリンク無効の場合
*2 2006年01月現在 一覧で表示されるページ数
*3 ゴメンなさい裏付けはとってないです。templateやincludeされたページでの更新も若干気にはなるんですが…
*4 のはず
*5 = 3 の変更点を 2*6 で上書き
*6 タイミングによっては拾ってくれるかも知れないが・・・
*7 もしくは適当なファイルを排他的ロック
*8 衝突ではなく純粋に put_lastmodified() に到達する時期が同じになる
*9 ftpでアップ -> wiki/***.txt の日付は変わったが recent.dat の中身は変わらない など

トップ   編集 凍結 差分 履歴 添付 複製 名前変更 リロード   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2010-11-25 (木) 18:10:13
Site admin: PukiWiki Development Team

PukiWiki 1.5.4+ © 2001-2022 PukiWiki Development Team. Powered by PHP 8.2.12. HTML convert time: 0.282 sec.

SourceForge