Pukiwiki 高速化

研究室で教授から研究室Wikiの動作が遅いので何とかして欲しいと頼まれた。研究室で使用しているWikiPukiwikiで、主に研究室ゼミで使用する発表資料の共有等に使用されている。一番表示に時間がかかるのがこの発表資料を共有するページである。使用ポリシーを決めずにどんどん発表資料を添付していったせいで、添付ファイル数が200を超え、HTML convert timeが10秒台中盤となっていた。

今後ページを作るときには半期ごとにページを作り直し、前の半期へのリンクを張るというようなポリシーにすれば大丈夫そうだ。しかし、既にあるページを手動で書き換えるのは面倒である。

てなわけでPukiwikiの高速化に着手した。

参考にしたのは「Revulo's Laboratory」の「PukiWiki/改造/高速化」のページ。「Pukiwiki 高速化」でググッたところ、一番上に出てきた。

このページで紹介されている方法はconvert後のhtmlをファイルにキャッシュするというものである。キャッシュは定石ですね。

早速パッチを適用したのだが、既に存在しているページが空ページとして表示されるという不具合があった。これは編集が終わったタイミングでキャッシュを生成するという実装になっている為である。

この不具合を解決するために、編集後のタイミングでキャッシュを破棄し、html convert時にキャッシュがない場合はキャッシュを生成する、という動作に変更した。

これで本文のhtml生成は高速化された。ところが添付ファイル一覧のhtml生成は高速化されなかった。添付ファイル一覧は別のルーチンで作られていたようだ。

これにもキャッシュを適用したところ、添付ファイルの多いページでもhtml生成が高速化された。具体的には10秒中盤だったページが0.010秒に短縮された。キャッシュは流石ですね。

ただ、ファイルのロックの仕方がこれであっているのかどうか不安だ。特にファイルの削除の周辺のファイルのロックがこれで合っているのか分からない。

詳しい方教えてくださいm(_ _)m

diff -Bwbr /cygdrive/c/develop/pukiwiki-1.4.7_notb_utf8/lib/file.php /cygdrive/c/var/www/html/pukiwiki/lib/file.php
90a91,92
> 
> 	remove_page_cache($page);
791a794,905
> 
> function get_converted_html($page)
> {
> 	if (! is_page($page)) {
> 		return '';
> 	}
> 
> 	$path  = CACHE_DIR . encode($page) . '.body.txt';
> 
> 	if (! file_exists($path)) {
> 		update_page_cache($page);
> 	}
> 
> 	return file_get_contents_with_file_locking($path);
> }
> 
> function get_notes($page)
> {
> 	if (! is_page($page)) {
> 		return array();
> 	}
> 
> 	$path  = CACHE_DIR . encode($page) . '.notes.txt';
> 
> 	if (! file_exists($path)) {
> 		update_page_cache($page);
> 	}
> 
> 	return file_with_file_locking($path);
> }
> 
> function update_page_cache($page = NULL)
> {
> 	if (! is_page($page)) {
> 		return;
> 	}
> 
> 	// Create and write converted html
> 	$body  = convert_html(get_source($page));
> 	$html_path = CACHE_DIR . encode($page) . '.body.txt';
> 	file_put_contents_with_file_locking($html_path, $body);
> 
> 	global $foot_explain, $note_hr;
> 	ksort($foot_explain, SORT_NUMERIC);
> 	$notes = ! empty($foot_explain) ? join("\n", $foot_explain) : '';
> 	$notes_path = CACHE_DIR . encode($page) . '.notes.txt';
> 	file_put_contents_with_file_locking($notes_path, $notes);
> }
> 
> function remove_page_cache($page = NULL)
> {
> 	if (! is_page($page)) {
> 		return;
> 	}
> 
> 	$html_path = CACHE_DIR . encode($page) . '.body.txt';
> 	unlink_with_file_locking($html_path);
> 
> 	$notes_path = CACHE_DIR . encode($page) . '.notes.txt';
> 	unlink_with_file_locking($notes_path);
> }
> 
> function file_with_file_locking($filename)
> {
> 	$lock = fopen($filename . '.lock', 'a');
> 	flock($lock, LOCK_SH);
> 
> 	$body = file($filename);
> 
> 	flock($lock, LOCK_UN);
> 	fclose($lock);
> 	unlink($filename . '.lock');
> 
> 	return $body;
> }
> 
> function file_get_contents_with_file_locking($filename)
> {
> 	$lock = fopen($filename . '.lock', 'a');
> 	flock($lock, LOCK_SH);
> 
> 	$body = file_get_contents($filename);
> 
> 	flock($lock, LOCK_UN);
> 	fclose($lock);
> 	unlink($filename . '.lock');
> 
> 	return $body;
> }
> 
> function file_put_contents_with_file_locking($filename, $data)
> {
> 	$lock = fopen($filename . '.lock', 'a');
> 	flock($lock, LOCK_EX);
> 
> 	file_put_contents($filename, $data);
> 
> 	flock($lock, LOCK_UN);
> 	fclose($lock);
> 	unlink($filename . '.lock');
> }
> 
> function unlink_with_file_locking($filename) {
> 	$lock = fopen($filename . '.lock', 'a');
> 	flock($lock, LOCK_EX);
> 
> 	unlink($filename);
> 
> 	flock($lock, LOCK_UN);
> 	fclose($lock);
> 	unlink($filename . '.lock');
> }
diff -Bwbr /cygdrive/c/develop/pukiwiki-1.4.7_notb_utf8/lib/pukiwiki.php /cygdrive/c/var/www/html/pukiwiki/lib/pukiwiki.php
123c123,125
< 	$body  = convert_html(get_source($base));
---
> 	//$body  = convert_html(get_source($base));
> 	$body  = get_converted_html($base);
> 	$foot_explain = get_notes($base);
diff -Bwbr /cygdrive/c/develop/pukiwiki-1.4.7_notb_utf8/plugin/attach.inc.php /cygdrive/c/var/www/html/pukiwiki/plugin/attach.inc.php
140a141,158
> 	if (! is_page($page)) {
> 		return '';
> 	}
> 
> 	$path = CACHE_DIR . encode($page) . '.attaches.txt';
> 
> 	if (! file_exists($path)) {
> 		update_attaches_cache($page);
> 	}
> 
> 	return file_get_contents_with_file_locking($path);
> }
> 
> function attach_filelist_directly()
> {
> 	global $vars, $_attach_messages;
> 
> 	$page = isset($vars['page']) ? $vars['page'] : '';
144,147c162,164
< 	if (! isset($obj->pages[$page])) {
< 		return '';
< 	} else {
< 		return $_attach_messages['msg_file'] . ': ' .
---
> 	$html = '';
> 	if (isset($obj->pages[$page])) {
> 		$html = $_attach_messages['msg_file'] . ': ' .
149a167,168
> 
> 	return $html;
225a245,246
> 	remove_attaches_cache($page);
> 
653a674,675
> 		remove_attaches_cache($this->page);
> 
678a701,702
> 		remove_attaches_cache($this->page);
> 
691a716,717
> 		remove_attaches_cache($this->page);
> 
851a878,891
> 
> function update_attaches_cache($page = NULL)
> {
> 	$attaches = attach_filelist_directly();
> 	$attaches_path = CACHE_DIR . encode($page) . '.attaches.txt';
> 	file_put_contents_with_file_locking($attaches_path, $attaches);
> }
> 
> function remove_attaches_cache($page = NULL)
> {
> 	// TODO lock the file.
> 	$attaches_path = CACHE_DIR . encode($page) . '.attaches.txt';
> 	unlink_with_file_locking($attaches_path);
> }

PukiWiki/改造/高速化 - Revulo's Laboratory
http://www.revulo.com/PukiWiki/Hack/Cache.html

追記 2010/02/13 0:54

キャッシュの読み込みと削除が同時に行われた場合に削除に失敗する恐れがあったため修正しました。上記の修正パッチには添付ファイルのダウンロード数が×2されるバグがあります。