/ 最近 .rdf 追記 編集 設定 本棚

脳log[20071225] PStore, category.rb: 日記に変更があるたびにカテゴリキャッシュファイルが一斉に更新されるのをなんとかする。



2007年12月25日 (火) なんでもかんでも「相性」で片付けて原因究明を怠る(一部の)風潮が嫌い

[Ruby][tDiary] PStore, category.rb: 日記に変更があるたびにカテゴリキャッシュファイルが一斉に更新されるのをなんとかする。

日記を更新すると、TDiary::Config#data_path/category/ 以下の、カテゴリごとに作られるキャッシュファイルがずいぶんたくさん更新される。全部ではないが半分近い 21のファイルが更新されていた。日記の内容はというと一つのカテゴリしか使っていない。

どこのコードが無駄にファイルを更新しているのかと絞っていくと、category.rbの中の Category::Cache#replace_sectionsだとわかった。ではこの replace_sectionsが悪いのかというとそうではない。replace_sectionsの中の

PStore.new(cache_file(c)).transaction do |db|
end

に囲まれた部分をすべてコメントアウトしても 21のキャッシュファイルが一斉に更新されたのだから。一部の PStoreファイルは開いて閉じるだけで常に更新されるのだとしか考えられない。

PStoreは transactionの前後で Marshal::dump の戻り値のサイズと MD5が変化したかどうかを見て、変更があったかどうかを判断し、ファイルに書き込みをするかしないかを決めている。Marshal::load/dump が対称ではないのだろか。(そもそも Hashを Marshal::dumpした結果が一定だと仮定してよいのだろうか*)

原因が何であるにせよサーバーの pstore.rbを書き換えるわけにもいかないので、tDiaryの category.rbに対策を施した。

 PStore#transaction(true)を使う

読み出し専用であることが予めわかっている PStore#transactionはすべて trueを引数にして(readonly=trueの意)呼び出す。実はそういう transactionはすべて PStore#abortで終わっているのでこの対策は必要ないのだが、ファイルを排他ロックすることと MD5を計算する手間が省けるので一応。

 PStore#abortを使う

肝心の Category::Cache#replace_sectionsは transactionの開始前に変更があるのかどうかがわからないので PStore#transaction(true)は使えない。日付の削除が空振りに終わったときにだけ PStore#abortを呼ぶことにする。これで必要最低限のキャッシュファイルだけが更新されるようになった。

	#
	# cache each section of diary
	# used in update_proc
	#
	def replace_sections(diary)
		return if diary.nil? or !diary.categorizable?

		categorized = categorize_diary(diary)
		categories = restore_categories
		deleted = []
		ymd = diary.date.strftime('%Y%m%d')

		categories.each do |c|
			PStore.new(cache_file(c)).transaction do |db|
				db['category'] = {} unless db.root?('category')
				if categorized[c] and diary.visible?
					db['category'].update(categorized[c])
				else
					# diary is invisible or sections of this category is deleted
					db.abort unless db['category'].delete(ymd)
					deleted << c if db['category'].empty?
				end
			end
		end

		if !deleted.empty?
			deleted.each do |c|
				File.unlink(cache_file(c))
			end
			replace_categories(categories - deleted)
		end
	end

* 追記:最近の Ruby(1.8.7だか 1.9.0)は Hashの keyの順序を保存しているみたいだけど。