ロリポップ!のサーバ移設でInternal Server Errorが出たので対応
ブログやQiitaに書かないようなこと(最近は主にUbuntuのインストールメモ)は個人的に立てているPukiWiki(http://debugitos.main.jp/)の方に書くようにしているが、昨日ここにアクセスしたら"Internal Server Error"が出てしまっていた。
このPukiWikiはロリポップ!のサービスでホストしていただいているのだが、そういえば8/18に「サーバ移設」が行われていた。
ということで、多分このせいでエラーが出るようになったのだろう。
いろいろ調べたところ、どうもアクセス制限のための設置していた.htaccessがいけなかったらしい。もともとは<Files>ディレクティブを使って一部のファイルにのみアクセス制限をかけていたが、これを取り除くと動くようになった。
サーバ移設によってApache httpdのバージョンが2.2から2.4に上がったらしいが、そのせいでエラーになっていたのか、それとも利用可能なディレクティブがより少なく設定されているのか、よく分からない。
ただ、この作業の過程でよかれと思ってPHPのバージョンを5.2から5.6に上げたのだが、そのせいで今度はPukiWikiのバグによるエラーが出るようになった。
どうやら古いバージョンのPukiWikiではPHP 5.4以降でエラーが出るらしい。
ということで、せっかくなのでPukiWikiを最新版に差し替えて、ついでにEUC-JPからUTF8に移行することにした。
まず、phpをローカルにインストールする。コマンドラインで起動できれば十分。
$ sudo aptitude install php-cli php-mbstring
次に、以下のサイトからdata2utf8.php.txtをダウンロードし、data2utf8.phpに名前変更。
data2utf8.phpをPukiWikiのルートディレクトリに設置して、実行。
$ php -f data2utf8.php
これで文字コードの変換は完了。
UTF-8の最新版PukiWikiに旧PukiWikiのデータを流し込む。
$ cp -an ../old/wiki/* wiki/ $ cp -an ../old/attach/* attach/
backupやcacheは面倒なのでいじらなかったが、これでなんとなくうまく動くようになった。
あとは適宜skinを移植。基本的にimageリソースとskin/pukiwiki.{css,skin}.phpをコピーすればそのまま動いた。
ただ、これでサイト上のPukiWikiページにアクセスするとどうもページロードが遅い。というか、スタイルがワンテンポ遅れて当たっている感じだ。調べてみると、pukiwiki.css.phpのロードに700msもかかっていた。遅すぎる。
ということで、pukiwiki.css.phpが吐いたCSSを手動でファイルに保存し、skinではこのCSSファイルを直接読みこむように変更したところ、だいぶマトモなスピードで動作するようになった。
慢性上咽頭炎の近況(4)
前回の近況からまた2ヶ月がたったので、これまであったことを記す。
Bスポットの終了
2016年4月5日にいつも通り耳鼻科でBスポット療法を受けた。この日でだいたいBスポットを始めてから半年が経過したことになる。症状はだいぶ改善してきているし、ここでいったん定期的なBスポット療法をやめ、様子を見ることになった。
耳鼻科の先生が言うには、「何ヶ月かしてまた戻ってくる人もいれば、こない人もいる。ともかく鼻呼吸を心がけよ」とのことだった。
それからここ2ヶ月の間は以下のような症状があった。
- 後鼻漏。
- 頭がぼうっとする感覚。目まいほどではないが、午前中に間食すると起こりやすかった。
- 光がまぶしい感覚。特に午後3時から4時あたりで西日が強い時によく感じた。
- 軽い吐き気。頻度は高くなかったが、液晶モニタでずっと文書を読んでいる時などに感じた。
こういった症状はコーヒーを飲んだり涼しい場所に移動することでやわらげることができた。
いずれにせよ、これらの症状は次第に弱くなってきており、今ではほとんど感じない程度になっている。
その他の措置
鼻うがいは朝夕に1日2回、引き続き行っている。特に寝ている間に鼻の奥に鼻水がたまることがあるので、朝に鼻うがいをすると気分がスッキリする。
さすがに鼻うがいには慣れてきたようで、水を押し込むタイプでも吸い込むタイプでもほぼ百発百中で鼻からノドに流せるようになってきた。不慣れなうちは、特に押し込むタイプではノドに抜けなかったことがあったが、ノドに抜ける感覚をイメージしながらゆっくり水を押し流すとうまくいくように思う。
腕と頭へのお灸は、最近は頻度を少し減らしてみている。結局効果があったのかどうか、よく分からない。鍼灸院で買ったもぐさがまだだいぶ余っているので、それを使い切るまでは続けようかと思っている。
.cabalファイルのghc-optionsに何を書くべきか
ふと、Haskellパッケージの.cabalファイルのghc-optionsフィールドに何を書いておくとよいか不安になったので、少し調べた。
なお、cabalが実際にどのようなオプションをghcに与えているかは--verboseオプションで確認できる。
$ cabal build --verbose
stackを使う場合、--verboseオプションはstack自身の、--cabal-verboseオプションはcabalのverbosityを制御する。
$ stack build --verbose --cabal-verbose
参考:
-Wall -fno-warn-unused-imports
これらは書くようにしている。
"-Wall"はご存知コンパイラの警告を全て表示するオプションである。
"-fno-warn-unused-imports"は不必要なモジュールやシンボルのimportに対する警告を無効化するものである。古いバージョンのghcでは必要だったimportが新しいバージョンのghcでは必要無くなることがあるのだが、このオプションがないとそんな場合にも警告が出てうっとうしいからだ。
-O[n]
最適化オプション。個人的には、これは書かなくてよいと思っている。
cabalはデフォルトで-Oオプションをghcに渡してコンパイルするらしい(cabal-install-1.24.0.0時点)。この挙動はconfigure時に設定できる。
$ cabal configure -O2
デフォルトでレベル1の最適化をするので、まあ特に設定しなくていいかなと思っている。他人のパッケージの.cabalをいくつか見てみたが、-O2と書いているものもあれば、何も書いてないものもあり、まちまちなようだ。
他のオプションでもそうだが、最終的にはユーザーがビルドする際に上書き設定すればよいだろう。
-X[LANGUAGE_EXTENSION]
ghc拡張機能スイッチ。これは書くべきではない。.cabalではdefault-extensionsフィールドとother-extensionsフィールドが使える(Cabal-1.10から。それより前ならextensionsフィールドが使える)ので、こっちを使うべきである。
なお、default-extensionsフィールドに書いた拡張機能はパッケージ内の全モジュールで有効になる。other-extensionsフィールドはパッケージが使う拡張機能を宣言するだけのものであり、各モジュールはLANGUAGEプラグマを使って必要な拡張機能のみを有効にする。
-threaded
OSスレッドを使用する。リンカオプションなのでlibraryセクションでは(多分)不要。他は個別の事情に合わせて、といったところだろう。
プログラムの作りによっては-threadedオプションがないと動かなかったり、逆にあると動かなかったりするので注意を要する。
以前、同じテストプログラムを-threadedのあるバージョンとないバージョンで2通りテストしたいことがあったが、結局test-suiteセクションをコピーして2つ並べてしまったことがある。何かよい方法はないだろうか。
-rtsopts
コマンドラインやGHCRTS環境変数からのRTSオプションを全て受け付ける。リンカオプションである。入れたほうがexecutableを使う立場としては便利だが、セキュリティ上のリスクがあるのかもしれない。
なお、デフォルト(-rtsopts=some)ではごくごく一部のRTSオプションしか受け付けないらしい。
"-with-rtsopts=-N[n]"
"-with-rtsopts"はRTSオプションをリンク時に指定する。-Nオプションはn個のOSスレッドの同時使用を許可する。n省略時はCPUコア数に応じたよしなな値が使われる。-threadedをつけるならセットでつけておいた方がなにかと便利だろう。
なお、-with-rtsoptsによる設定は-rtsoptsがなくても有効になる。-rtsoptsはあくまでプログラム実行時にRTSオプションを設定できるかどうかを制御するものである。
また、-with-rtsoptsではスペース区切りで複数のRTSオプションを並べることができるが、これを.cabalに書くには上に示したように-with-rtsopts=...全体をダブルクォートするべしとのこと( https://github.com/haskell/cabal/pull/1346 )。
"-with-rtsopts=-M512m"
"-M"オプションは最大ヒープサイズを制限する。自分の書いたプログラムのバグによってメモリが無限に食いつぶされるのが怖いので、自分はtest-suiteにはこれをいれるようにした。(参考: http://qiita.com/debug-ito/items/87fa50d5324e20936d7e )
Stackageに自分のパッケージを上げる
最近はもうstackとstackageには頼りっきりで、使いたいパッケージがstackageに入っていないとちょっとイラッとくるくらいになってきたが、そういえば自分もいくつかのパッケージをhackageに上げている。大したものではないとはいえ、それらをstackageに載せないのはスジが通らないなと思い、stackageに上げることにした。
さて、自分のパッケージをstackageに上げるにはどうすればいいか。必要なことは以下のページに書いてある。
手順としては、stackageレポジトリをforkして、build-constraints.yamlに自分の名前とパッケージのリストを追加して、pull requestを送るだけだ。
ただし、stackageに入れるパッケージは以下の3点を満たす必要がある(あるいは、満たす「べき」である?)
- All packages are buildable and testable from Hackage.
- All packages are compatible with the newest versions of all dependencies.
- All packages in a snapshot are compatible with the versions of libraries that ship with the GHC used in the snapshot
travisスクリプトとビルドエラー対応
第1の条件はこれまで通りきちんとパッケージングしてhackageに上げれば満たされる。
テストに関しては.travis.ymlが提供されているのでこれをサクっと利用するのがいいだろう。
GHC + cabalおよびstackを用いたビルドパターンをいくつかのバージョンでやっている。なかなか良さそうだ。
なお、stackageに上げたパッケージがビルドに失敗する場合、「1週間以内を目安に解決しろ」とのこと。
依存パッケージのバージョン上限問題
第2の条件は、stackageに上げるパッケージが依存するパッケージのバージョン上限に関係する。
例えば、.cabalファイルに以下のようなbuild-dependsがある場合、
build-depends: foo-bar >=0.3.0.1 && <0.5
foo-bar-0.5がリリースされると第2の条件が満たされなくなる。この場合、foo-barに依存しているパッケージの作者が(1週間以内を目安に)解決しなければいけない。foo-barの変化が小さければ新バージョンでのビルドとテストを確認してbuild-dependsを変更するだけでよい。そうでない場合はちょっと厄介なことになるだろう。
ここで問題となるのは、依存パッケージが他人のものである場合、それがいつメジャーバージョンアップされるか分からないということだ。この問題に対処するために、以下のサービスが公開されている。
このサービスは検索したパッケージの依存パッケージのバージョン上限を調べ、それが現在の最新版をカバーしているかチェックしてくれる。パッケージ検索ではauthorフィールドも検索対象になるので、例えば筆者の場合は
をウォッチすればいい。RSSフィードも吐いてくれるので、こいつをフィードリーダーに突っ込んでおけば速やかに依存パッケージのメジャーバージョンアップに対処できる、はずだ。
なお、PVPには反してしまうが、依存パッケージにバージョン上限を記載しないという考え方もある。この件についてMichael Snoyman氏がブログに書いている。
stackage的にはバージョン上限はあってもなくても構わない、ということだろうか。また、stack sdistとstack uploadには--pvp-bounds
というオプションがあり、build-dependsのバージョン下限・上限をいい感じに書き加えてくれるらしい。
個人的には、今のところbuild-dependsのバージョンは自分で管理すればいいと思っている。いろいろ規模が大きくなると破綻しそうな気もするが。
依存パッケージのバージョン下限問題
第3の条件は依存パッケージのバージョン下限に関係する。要は、「GHCにバンドルされるパッケージバージョンがカバーされるくらいには下限を広く取れ」ということである。
このことの理由は以下で詳細に説明されている。
"lenient"とは"not as strict as expected"という意味らしい。
例えばあるGHCバージョンにパッケージfoo-bar-0.2がバンドルされており、自分のパッケージのbuild-dependsがfoo-bar >=0.3だとする。すると、バンドルされたfoo-bar-0.2とユーザスペースにインストールされたfoo-bar-0.3が混在する状況になりうる。これが結構ややこしいコンパイルエラーを引き起こし、運が悪い場合は詰む。
さらに言うと、Haskell Platformで提供されるパッケージバージョンをカバーしておくといいようだ。これらは以下のページで確認できる。
ぶっちゃけそれほど多くないので、現状では人力でチェックすればいいと思う。
Gtk-Uguiss-2.00をリリースした
Gtk2/Gtk3/xfwm4テーマであるGtk-Uguissの新バージョンを作った。
バージョン1.xx系列はXubuntu 14.04 (Trusty)が、バージョン2.xx系列はXubuntu 16.04 (Xenial)がターゲットである。いろいろ試行錯誤して目に着いたバグは潰したのでリリースした。
なお、そもそもなんでGtkのテーマなぞ作っているかはGtk2/Gtk3/xfwm4テーマを作った - DebugIto’s diaryを参照。
バージョン2.00の主な変更点は以下の2点である。
- Xfce4.12の新ウィンドウスイッチャへの対応。
- Gtk3の怒濤の変化への対応。
Xfce tabwin
TrutyのXfceは4.10だったが、Xenialでは4.12になっている。Xfce4.12では、ウィンドウスイッチャ(通称"tabwin")が新しくなり、ウィンドウサムネイルの表示やマウスでのウィンドウ選択が可能になった。
ということで、このtabwin向けの色設定を追加した。
Gtk3の互換性問題
Gtk3は、Trusyではバージョン3.10, Xenialでは3.18になっている。この間、互換性を破壊するものを含む様々な変更が加えられたようで、その対応に追われた。新しく追加されたウィジェットやエフェクトに正常にスタイルが当たらないならともかく、checkboxやspinnerといったごく基本的なウィジェットへのスタイル設定がぶっ壊れているのを見た時には開いた口が塞がらなかった。
Gtk3の連中は後方互換性を維持するつもりがあるのだろうか。あまり好き勝手なことをやっているとユーザ(ここではテーマ開発者)から愛想をつかされ、コミュニティは衰退し、結局自分たちしか使えない何かになり下がりはしないか。実際、Xubuntu 16.04のリリースブログ記事にはこんな文言がある。
Albatross, Bluebird and Orion GTK+ themes have been dropped since they do not support newer GTK3 versions
16.04になってデフォルトのテーマ設定の選択肢が非常に狭まったと感じたが、Gtk3のせいなのだろう。
もちろん、新機能を追加したり既存の問題を解決したりするためには後方互換性を破壊する変更がしばしば必要になるので、なかなか難しいところではある。Gtk3の開発ロードマップがどうなっているのか知らないが、どこかの時点でメンテナンスモードになって、非互換な開発はGtk4で進むようになることを願う。
Gtk3のテーマ開発
Gtk3のテーマ設定は大部分がCSSで書かれているが、自分は以下のような手順で修正を行っている。
- gtk3-demo、gtk3-widget-factory、その他のGtk3アプリを使って問題のある箇所を見つける。
- その箇所に設定されていそうなCSSクラス名を推測し、テーマファイル(ほとんどの場合はgtk-widgets.css)内を探す。
- 修正すべき箇所を同定するため、CSSファイルを書き換えて「マーカースタイル」を設定する。例えば、background-color: redとかborder: solid 3px redとかpadding: 50pxとか、とにかく変化が目立つもの。
- マーカースタイルが狙い通りの場所に現れていればそこが修正すべき場所なので、適宜修正する。マーカースタイルが表れていなければ、別の場所を試す。
このように、いわばprintfデバッグのCSS版のようなことをやっている。実に効率が悪い。
Web開発であればCSSを修正する作業はもう少し楽なはずだ。ドキュメントの構造はHTMLを見れば分かるし、最近のWebブラウザには超優秀なインスペクタがデフォルトで積んであるので、どこに書かれたスタイルがどう効いているのか一目瞭然である。ブラウザ上でリアルタイムに修正することすらできる。
Gtkにそういうものはないのか?
と思って調べたら、まさにGtkInspectorというものがあるそうだ。
Gtk3.14から利用可能とのこと。gtkparasiteというツールが土台となっているらしい。なるほど、これは具合が良さそうだ。今度なにかあったら試してみよう。もっと早く調べるべきだった。
XfceとGtk3
tabwinのスタイル設定をして気付いたのだが、tabwinを含むXfce4.12のアプリはいまだ大部分がGtk2を使っている。テーマ開発者としては安定しているGtk2を使ってくれる方が助かるが、いつまでもGtk2を使っていていいのかという不安もある。
と思っていたら、開発者のブログにちょうどそういった記事が上がっていた。
どうやらXfce4.14の目標は、大部分のアプリをGtk3にポートすることらしい。ほっとしたような気もするし、余計なことを・・ ともちょっと思う。
鼻詰まり時の鼻うがい
前回の慢性上咽頭炎の近況で書き忘れていたが、花粉症などで鼻が詰まっている時の鼻うがいについて記す。
当然だが、鼻の通りが悪い状態では鼻うがいもやりづらいし、鼻が完全に詰まっている状態では鼻うがいはほぼ不可能だ。
そのような時にも鼻うがいをやるテクニックとして鍼灸師さんから教えてもらったのが「スクワットをする」ことである。スクワットをすることで一時的に鼻が通るようになるので、その隙に鼻うがいをするのだとか。花粉症で鼻が詰まっている時に実際にやってみたが、確かに心持ち鼻が通る気がした。
他にも、「ペットボトルか何かで脇の下を圧迫すると、その逆側の鼻が通るようになる」というのをためしてガッテンでやっていた。ただしこれは長時間やり過ぎると腕の血流が悪くなるので要注意である。
また、個人的な経験では、朝、冷水で顔を洗ったり髪を濡らしたりした後に鼻が通りやすくなったと思う。逆に、うがいをすると鼻が詰まった。理由はよく分からない。
ただ、本当に花粉症が厳しい時は何をどうしても完全に鼻を通すのは不可能である。完全に鼻が詰まっている時は無理せず鼻うがいをしない方がいい。一応通るが通りが悪いときも、くれぐれも慎重にするべきである。無理に鼻の中に水を押し込むと、予想外の場所に水圧がかかって(多分)危険だと思う。
GHCにおけるヒープとスタック
先ほど、このような記事をQiitaに書いた。
Haskellを書いていて、再帰関数の終了条件設定をミスって無限ループさせてしまうことが何回かあったのだが、CPUだけでなくメモリも無限に持って行かれ、スワップを使い始めたあたりからキーボードへの応答も著しく鈍くなり、とにかく大変な目にあった。
どうやら再帰関数の実行によってサンクが無限増殖し、そいつがヒープメモリを肥大化させているようだったので、RTSオプションでヒープの上限を与える方法を調べたのだった。
さて、RTSオプションのドキュメントを読むと、以下のような記述があることに気付いた。
-K
size [Default: 80% physical memory size] Set the maximum stack size for an individual thread to size bytes.-M
size [Default: unlimited] Set the maximum heap size to size bytes.
-M
オプションはヒープメモリサイズの上限である。こいつがデフォルトで青天井なので、サンクの無限増殖で大変な目にあったのだった。
一方、-K
オプションはスタックサイズの上限であり、こいつはデフォルトで(物理メモリの8割という大容量とはいえ)上限が設定されている。
では、ヒープとスタックの違いはなんだろうか?と思って調べてみた。
結論から言うと、どうやら以下のようなことらしい。
- ヒープはサンク(など)が置かれる
- スタックはサンクを評価する時に使われる
上記のスレでは定番のfoldl
を用いて説明されている。
遅延評価版のfoldlでは以下のように評価が進む。
foldl add 0 [1,2,3,4] = foldl add (add 0 1) [2,3,4] = foldl add (add (add 0 1) 2) [3,4] = foldl add (add (add (add 0 1) 2) 3) [4] = foldl add (add (add (add (add 0 1) 2) 3) 4) [] = add (add (add (add 0 1) 2) 3) 4
ここで、add
は(+)
の別名とする(見やすさのため)。
上記で積み上がるadd
の列は未評価の式、つまりサンクであり、ヒープに置かれる。よって、例えば
foldl add 0 $ repeat 0
とするとサンクの無限増殖によってヒープが肥大化する。この場合、いくらスタックサイズを制限してもどうしようもない。
さて、foldlの評価ではadd
が積み上がったサンクがさらに評価されていくが、ここでadd
(というか(+)
)は正格評価である。よって、
- (add X 4)を評価する。
- Xを評価する。そのためにaddと4は一旦脇においておく。
- Xは(add Y 3)である。これを評価する。
- Yを評価する。そのためにaddと3は一旦脇においておく。
- ...
となる。ここで、評価を進めるために「一旦脇においておく」場所が(多分)スタックとなる。よって、リストに要素が増えれば増えるほどスタックも深く、大きくなり、そのうち上限に当たって死ぬ。
同様の議論はHaskell-cafe Stack, Heap and GHCでもされていた。
ちなみに正格版のfoldl'では以下のように評価が進む。
foldl' add 0 [1,2,3,4] = foldl' add 1 [2,3,4] -- (add 0 1)が評価される = foldl' add 3 [3,4] -- (add 1 2)が評価される = foldl' add 6 [4] -- (add 3 3)が評価される = foldl' add 10 [] -- (add 6 4)が評価される = 10
正格版ではサンクは肥大化しないし、評価も1ステップごとに行われるのでスタックも肥大化しない。よって、
foldl' add 0 $ repeat 0
としてもメモリは食われない。ただCPUがぶん回るだけである。