- ベストアンサー
Perlの変数に文字数制限(容量制限)はあるか
- Perlの変数には文字数制限(容量制限)はありますか?
- Perlで書かれた自作の掲示板で、突然データが欠けてしまいました。データ容量が1.5MBほどあり、容量制限が関係しているのか疑問です。
- 他にも似たような問題が発生した経験がある方、アドバイスをお願いします。
- みんなの回答 (8)
- 専門家の回答
質問者が選んだベストアンサー
No.3&6です。 > flock してません・・・・・・。 > つまり、読み込み時に競合があって書き込まれたため、開いたままのデータが > 変なところで書き込み処理に割り込まれて、おかしくなったということでしょうか。 > でしょうか、というか、その可能性があった、というところですかね? ちょっと説明が足りなかったので補足します。 読み込み時にflock()しなかった場合ですが、書き込みが純粋に追記だけの場合は(記事表示で途中で切れることはあるかもしれませんが)データファイルの破損までは至らないかなと思います。もちろん、読み込み内容が壊れるには違いないですので、読み込みルーチンにもflock()は追加すべきです。 データファイルが壊れるケースですが、既存記事の修正機能が実装されている場合などで、読み込みルーチンで取得した@BbsDataの値を使った書き込みルーチンが別にあった場合には、たとえその書き込みでファイルロックがされていたとしてもデータファイルが壊れることになります。 もっともその場合、データ変更に関する一連のロジック自体を見直す必要があるかもしれません。 既存記事の変更を行う場合には、「一度のファイルオープン+ファイルロックの間に、読み書きを全て済ませる」という処理に置き換えないと、ほぼ同時に複数の書き込みが発生した場合に一部のデータが失われるという別の不具合が発生する恐れがあります。ご参考まで。
その他の回答 (7)
- netaninja
- ベストアンサー率66% (2/3)
CGIを動かしているのがレンタルサーバ(などの第三者が設定した環境)であって、 壊れたファイルが0や8192バイトなどの切りの良い(?)サイズで切れているなら、 ファイル書き込み中にシグナルで殺されている可能性も考えられそうです。 時間のかかるCGIを制限しているレンタルサーバだと、 比較的短い時間でタイムアウトして、CGIにSIGTERMやSIGILLLが飛んできて、 プロセスが殺されることがあります。 ファイルサイズが増えれば、書き込みに要する時間も長くなるので。 もしそうだとすると、解決策は・・・ SIGTERMを無視してみても、最後にはSIGKILLが飛んでくると思うので、 処理を早く終わらせるしかなさそうです。 例えば、一定行数ごとにファイルをわけて、表示時はページ制御するとか。 表示&保存する件数を、全部はあきらめて最新何件などに減らすとか。 他には、limitでメモリ制限されてる、なんて可能性もありますが、 それだとファイル書き出しより前の時点で落ちるでしょうから、 今回の原因としては考えにくいですね。
補足
回答ありがとうございます。 サーバーはレンタルサーバーですが、壊れたファイルは 114,898バイトでした。 キリのいい数値ではなさそうです(たぶん)。 一応サーバーに、そういうことをすることもあるか聞いてみます。 動いていた時も書き込みに1秒ぐらいかかっていたので、これまでの累積を いっぺんに開いている状態はやめて、もうちょっとすっきりさせたいと思います。
- N60-BASIC
- ベストアンサー率80% (17/21)
No.3で回答した者です。 追加書き込みのロジック自体は問題ないように見えます。 # 強いて指摘するなら、追記のみの場合はファイルサイズが小さくなることはありませんので、truncate()によるファイル全消去が不要なくらいでしょうか。 # seek(WRITE, 0, 0)だけで十分です。 # 余談ですが、ファイルサイズが減る可能性がある場合においても # ファイルを全消去してから書き込むよりは、先頭へシーク+全データ書き込み後に # truncate(WRITE, tell(WRITE)); # とした方が、ディスク処理のオーバーヘッドが少ないです。 で、書き込みが問題ないとなると、やはり読み込み処理に問題があるのではないでしょうか。 公開して頂いたサブルーチンを見る限り、おそらく「読み込み用」サブルーチンが別にあるのではないかと推測します。 # でないと、追記しない限り読めないことになりますから・・ Perlのflock()による排他処理は(OSにもよりますが)flock()を利用したファイルオープンに対してしか機能しません。従って、ファイル読み込み時にも open(HANDLE, ...); flock(HANDLE, 1); # リードオンリーモードでロック といった処理が必要になります。 これを忘れた場合、質問者さんのような症状が発生する可能性があります。
補足
おっしゃる通り、全書き換えの時のロジックを、追記の時も使い回して、 まあ、余計な動きをしているけど動くからいいか、とそのままにしていました。 余計な部分は省くように参考にさせていただきます。 読み込み部分ですが、そのまま貼り付けます。 1ページに20件表示とかする用に行数($BbsCount)も取得しています。 # 書き込みデータの読み込み open( BBS, "< ../data/bbs.dat" ); @BbsData = <BBS>; $BbsCount = @BbsData; close ( BBS ); flock してません・・・・・・。 つまり、読み込み時に競合があって書き込まれたため、開いたままのデータが 変なところで書き込み処理に割り込まれて、おかしくなったということでしょうか。 でしょうか、というか、その可能性があった、というところですかね?
- Tacosan
- ベストアンサー率23% (3656/15482)
現在の Perl の実装では, 文字列を「長さ+内容」という形で記憶します (C とは異なり「終端に特定の文字がある」という形ではない). この「長さ」を確か int で記憶しているはずなので, 「1つのスカラーに記憶できる文字列の長さ」は限界があります. 現状ではシステムによらず 32ビット符号付きで上限は 2GiB-1 だったかな? 配列やハッシュの要素数にも同じような制限があるはず. ただ, #2 の最後にもちょろっと書いたけどそれとは無関係で #3 にいわれる排他制御の問題だと思いますよ. 「競合はしていませんでした」と書かれていますが, 「複数人が (ほぼ) 同時に掲示板に書き込む」状況を想定して調べましたか? ああ, もう 1つ「遅延書き込み」という問題もあり得るか. 「同期書き込み不可能」とかいう腐った OS だと考えられなくもない.
補足
たびたびありがとうございます。 やはり聞く限り、2ギガビットとか全然行ってないですし、その辺の容量オーバーは 違う感じですね。排他制御ですが、本に書かれていたロックをかけるやり方をしていた だけです。回答番号:No.3の補足にその部分を載せました。 同時書き込みや非常に重い時にも大丈夫かと言われると、flock に任せているだけで 少なくともテストはしていませんし、実行環境もそんな混み合ってはいませんでした。 そういう状況に耐えうる堅牢なロジックがあるなら念のため入れ替えておきたいところです。 でもその前に今回のデバッグですね・・・・・・。
- mtaka2
- ベストアンサー率73% (867/1179)
ファイルの排他制御に失敗した可能性が高いんじゃないかと思います。 2つのプロセスが同時に書き込んだりしたら、例えば、 あるプロセスが書き込んでいる途中のタイミングで、次のプロセスだデータ読み込みを実行 →書き込み途中までのでデータしかしか読み込めない ということになります。その後、最初のプロセス側では書き込み完了すれば、ファイルは正しい状態になりますが、 後のプロセス側で読み込んだデータは、その中途半端に読み込んだ壊れた状態になっているので、 後のプロセス側が書き込みを行えば、ファイルが壊れた状態に更新されることになります。 排他処理はファイルサイズに依存した問題ではありませんが、 ファイルサイズが大きくなると読み書きに時間がかかるようになるので、 タイミング的に問題が発生する確率は高くなります。
補足
回答ありがとうございます。 書き込み処理を行っている部分のソースを、回答番号:No.3 さんへの補足に載せました。 詳しく知識があるわけではありませんが、「flockでロックしているから大丈夫」、 という認識でした。解説本にもそうあったと思います。 それで、それがデータサイズが大きすぎて時間がかかった時とかのイレギュラー時の 処理なんですよね。。。
- N60-BASIC
- ベストアンサー率80% (17/21)
ファイル読み書き時の排他制御が適切でなかった場合によく起こる症状に見えます。 flock()などでファイルロックをかけるのが一般的な排他制御ですが、書き込み時にopen(HANDLE, '>', ...)で新規書き込みオープンしてしまうのが最も典型的なミスです。 この場合、ロックがかかる直前に他のプロセスから読み込み処理が行われると空ファイルが読まれてしまうので、意図した動作にならずにファイル内容が失われる恐れがあります。 読み込み/書き込みのファイルオープン・クローズ処理に問題がないか確認されてはいかがでしょうか。 該当部分のスクリプトを公開して頂ければ、具体的なアドバイスが出来ると思います。
補足
回答ありがとうございます。 取り急ぎ書き込み部分を載せます。紙谷歌寿彦氏の本のやり方だったと思います。 $WriteComment に今回の書き込みデータが入っている状態です。 # ファイルの追加書き込み処理 open( WRITE, "+< ../data/bbs.dat" ); # 開く flock( WRITE, 2 ); # ロック @OldBbsData = <WRITE>; unshift( @OldBbsData, $WriteComment ); truncate( WRITE, 0 ); # サイズを0に変更する(消す) seek( WRITE, 0, 0 ); # ファイルのカーソル位置の変更 print WRITE @OldBbsData; close( WRITE );
- Tacosan
- ベストアンサー率23% (3656/15482)
「ファイル容量の制限」は OS やら設定やらによるけど Perl は無関係. 「変数の容量制限」は, 「Perl言語」としては「int の大きさ」が影響したはず. あと, 当然だけど「1つのプロセスで使うことのできるメモリ量」にも依存する. だから, Perl処理系に (この辺の) バグがなければ 1.5 MB くらいでは死なない. 手元では Perl 5.10.1 だけど, $a = join('', 'a' .. 'z') x 1000000; くらいならまったく問題なく動きます. ファイルの読み書きで競合しているって可能性はないか?
補足
回答ありがとうございます。 ファイルハンドラを見てみましたけど競合はしていませんでした。 ずっと問題なく動いていたんですよね。それでいきなり壊れたので 「ついに処理できる容量が限界を超えた」のかと思ったのですが。 「ファイル容量の制限」は違うんですね。 となると変数かメモリなのですが、変数の「intの大きさ」というのは どの部分のことでしょうか? 書き込みデータ処理には、変数や配列を使っていますが結構そのまま ガンガン入れている状態です。
変数なら、型により長さが規定されているでしょうね。 型で規定されている長さを超えるとダメでしょう。 Perl5.6.1 言語の仕様では?
補足
回答ありがとうございます。 型は……、ないです(よね?)。 その仕様がちょっと見つけられなくて。 今、教えてgoo内で「文字数の規定はありません」と答えている回答を 見つけました。メモリ関連は影響するようです。
お礼
別の質問になってしまったので立て直します。 回答してくれた皆様、ありがとうございました。
補足
読み込み時、flock処理を追加してみます。 # 書き込みデータの読み込み open( BBS, "< ../data/bbs.dat" ); flock( BBS, 1 );# ロック @BbsData = <BBS>; $BbsCount = @BbsData; close ( BBS ); 修正機能は付いていません。 でもそのうち付けようと思っていたので、付ける時にロック系、気を付けたいと思います。 とりあえず再開に向けて、処理するデータを小さくし、読み込み時に flock処理を追加してみます。 あと今回のことを一応、サーバー会社に聞いて返事を待つと。 それからもうちょっと頻繁にバックアップを取ります。。。 Googleにキャッシュが残っていたので少しサルベージしましたが、抜けが出てしまいました。 Google以外でそういうキャッシュが自動的に残っている可能性のあるサイトってありませんかね? xxx.cgi に最初の20件、 xxx.cgi?page=1 だと21件~40件、 xxx.cgi?page=2 だと41件~60件、 xxx.cgi?page=3 だと61件~80件 という感じで表示させていたのですが、別ページ扱いなのでキャッシュが残ってたりするんですよね。