[FileMaker] グローバル変数による外部データソース指定の謎のファイル破壊で苦しんだ話

FileMaker で外部データソースをグローバル変数に設定していると、希にファイル破壊エラーが起きます。

今宵その話ですが、原因の特定も出来ず解決もしない役立たずな投稿になっております。

原因を特定も出来ず解決もしない投稿に意味あるんか。ありません。でもあります。問題の解決のためにはまず問題が何であるのかを知らなくては何も進みません。また、どこかの賢い人がヒントをくれるかもしれません。あるいは、こうやって書き留めることで頭が整理され何か思い当たる節を発見できるかもしれません。

さて、FileMakerで外部ソースをグローバル変数にしているときに時々発生するファイル破損のエラーです。このエラーを説明する前に、どういう状況であるのかを先に述べなければ何も伝わりません。

外部データソースをグローバル変数に置き切り替える

FileMakerでは他のFMファイルを使ってリレーションしたりします。このとき、目的ファイルが必ず決まった場所にあるのなら、外部データソースにそのパスを記せば良いです。また、リレーションシップ画面でファイルを読み込むと、ここにパスが自動で記述されます。

外部データソース管理

外部データソースが特定一意のファイルならこれで良いわけですが、世の中そうもいきません。外部データソースに自由が必要な場合、事前にパスを決定できません。

そこで、データソースのパスにグローバル変数を置き、ファイルの中でパスを決定させるという小技を駆使します。

外部データソース管理 「メディア管理」の事例

これは メディア管理の作り方 で作っているファイルのデータ参照ですが、データソースがすべてグローバル変数です。

ファイルの開始直後にこれらグローバル変数に目的のパスを割り当て、使用可能にします(正確には、閉じる直前に割り当てる)
これにより、データファイルを切り替えて使用することができます。

データの切り替え
データファイルを切り替えて使い分ける

このやり方には多くの注意点があります。

 

外部データソースにグローバル変数を使う注意事項

パスが決定される前に外部ソースに一切触れてはならない

ファイルがオープンした直後、グローバル変数にパスが割り当てられる前に外部ソースに触れることがないよう細心の注意を払います。

スタートアップ時は外部データソースと一切関連しないテーブルのレイアウトを使うことが必須で、何なら起動用にフィールドがない空のテーブルを用意しておいてもいいくらいです。スタートアップ時に走らせるスクリプトにも気をつけます。外部データソースと一切関わりがないスクリプトであることを徹底します。

スタートアップ時がそうなのだから閉じる時とて同じです。次に開くときに一瞬閉じる前のレイアウトが表示されたらアウトですから。閉じる前には外部データソースと無関係な何もないレイアウトに切り替えて終えることを必須とします。

グローバル変数の値を変更したらファイルを再起動する

グローバル変数の値であるパスを変更したいときは、変更後、即座にファイルを閉じて開き直します。

ファイル使用中に外部ソースを切り替えることはできませんから(できてほしいけどできない)
必ずファイルを再起動します。

FileMakerのファイルを再起動するというスクリプトステップはありませんから、何らかの別の方法でそれを行います。メディア管理ではAppleScriptでターミナルを動かして無理矢理再起動させるスクリプトを書いています。

スクリプトで最後のファイルを閉じる直前にAppleScriptを実行させます。その内容は、ファイルのパスを指定して「3秒後に開く」というシェルスクリプトをターミナルで実行させるという命令です。AppleScript でこれをやってもダメで、do shell script でもダメです。FileMaker が処理の完了を待ってしまいます。ターミナルを使うからこそ処理が継続できます。

スタートアップのスクリプトでは早々にグローパル変数の値にデータソースのパスを記述し、接続を確認します。接続の確認と、様々なエラー処理をスクリプトに作っておくことがとても重要で、予期せぬエラーはいくらでも起きます。いくら「よしOK」と思っていても後日別の問題が浮上したりします。

  • パスに書かれたファイルがちゃんと存在しているか
  • そのファイルがデータソースとして成立しているか
  • グローバル変数の値に確実にパスが入っているか
  • グローバル変数のパスで接続できたか

こう言った事柄すべてに「そうでない場合」の処理を加えていくことをちくちく続けます。

ということで、そういうことです。外部データソースのパスにグローバル変数を置き、値にパスを入れることで接続するシステム。これがエラー発生の状況です。

ファイル破壊を伴うエラー

エラーは突然やってきます。ファイルを開くとエラーダイアログが開き、グローバル変数のファイルが見つかりませんと言われます。

エラーダイアログ: ファイル「〜」を開くことができません。(見つかりません)

スタートアップ時のレイアウトやスクリプトに失敗箇所があるとエラーになります。凡ミスもありますので、フィールドやレイアウトやスクリプトを徹底的に洗い直します。これはこれで奥が深く、デバッグも長年チクチクと続けていますが、しかし、ここでのお話はそれではありません。

ファイルを開く前にデバッガを開いておき、スタートアップから順に何が起きているのか確認していきますと・・・

ファイルを開いたらいきなり「見つかりません」

ファイルを開き、デバッガがスタートアップのスクリプトを表示するより先に「見つかりません」が出ました。

デバッガも確認もくそもなく、ただファイルを開いただけで、OnFirstWindowOpen トリガすら発動していない状況で発生します。デバッガは空白で何も表示していません。当然エラー表示もありません。エラーであることすら認識していないんですね。何これ。

そうか。これはあれだ。きっとファイルを閉じる際のスクリプトに問題があるのかもしれない。と、考えます。グローバル変数の値に、何か予期せぬ変なものが混入したのではないかと。

OnLastWindowClose スクリプトを丹念にチェックしたり、作り直したり、安全に安全を重ねた念入りな「何もない」状態を作り上げても何をやっても、ファイルを開くとエラーがでます。

エラーダイアログ: ファイル「〜」を開くことができません。(見つかりません)

手動でファイルを指定しても無駄

このエラーダイアログのあと、ファイル選択を促すダイアログが出てきます。そこで正しいファイルを指定してやるとスタートアップスクリプトの処理が続きますが、実際には選択したファイルは接続されておらず、結局外部データソースが「不明」のままファイルが開き、使い物になりません。

何をどう弄くっても無駄

データなしの別名保存で治るという話もありましたが治りません。データソースの管理で一旦普通のパスに設定して開き直しても結局治りません。一度このエラーに遭遇すると、何をどう弄くっても無駄で、二度とそのファイルは使い物になりません。つまり完全に壊れており捨てるしかない状態です。

対策はバックアップすることのみ

結局、どうしたかというと、頻繁にバックアップ保存をするようにしただけです。ファイルを開く際に「見つかりません」が出た時点でもうアウト。ファイルを捨ててバックアップからやり直します。

このエラーは何なのか

この絶望的なエラーが出る状況やタイミングに法則は見当たりません。ある日突然壊れます。原因不明です。

ただ、エラーを誘発しやすい原因のひとつのようなものはあります。

原因の一つのようなものと対策

ミスがまだ多かった頃の特徴として「外部データソースを変更したあと」に、このエラーが起こることが多いように感じていました。

外部データソースの変更、即ちグローバル変数の値をファイル起動時のパスから別のパスに入れ替えるという行為を行いますが、明らかな失敗があると、ファイルが壊れる可能性が高まります。

クローズ時の挙動

ファイルを閉じるときに何もないレイアウトに戻るようにしていましたが、複数ウインドウが一斉に閉じるような場合に破損エラーが起きやすいことがわかりました。

そこで、クローズ時の挙動をさらに徹底させます。例えば最後のウインドウ状態を少しの時間キープさせるなどです。これは僅かに効果が感じられました。

指定したパスのデータソースが間違っている

これは初期の頃よくやるポカでした。データファイルを切り替えるのに、そのデータファイル内のフィールドが揃っていなかったりフィールド名が変更されていたりすると当然上手く動きません。データファイル同士は一貫性というか、フィールド構成などがまったく同じものを使わないと意味を成しません。

これを防ぐには、常にデータファイルの同一性を管理しなければならないので、ファイルバージョンの概念を持ち込んできちんと揃えることを行いました。

ただ、この失敗があったからと言って、エラーにはなってもファイルが壊れることはありません。でもたまにありましたので気をつけるに越したことはありません。

指定したパスが存在しない

ボリュームがマウントされていないなど、指定パスにファイルが存在しない場合も具合が悪いです。グローバル変数にセットするパスは必ず存在チェックを行います。

指定したパスが共有ボリューム

ファイル破壊エラーを誘発する可能性が最も高いのが、指定パスが共有ボリューム内にある場合です。

実は、FileMaker がファイルを壊してしまうことはよく起こります。それは共有ボリューム内のファイルを直接開いたときです。一見、普通に使えますが、そのファイルを閉じて、本来そのボリュームが直接接続されているデバイスで同じファイルを開くと、二度と開くことができない破損が起きています。

FileMaker も「共有されたボリューム内のファイルを直接開いてはいけない」と警告しています。これがそれほど大事だとは以前は思っていませんでした。でも大事です。完全に壊れます。

これと同じことが起きるので、グローバル変数に共有ボリュームのパスを絶対に指定してはいけません。

これを防ぐために、パスを指定する際「直接接続されたボリュームかどうか」をテストするコマンドを走らせるようにしました。FileMakerの機能では実現できないので、外部コマンドラインを使用します。

ということで、これこそがファイル破損エラーの最大の原因だ!と、思ったこともありました。確かに、手立てを行ってからは破損の頻度は劇的に減りました。

ただ、まだたまに起こります。

クラウドストレージ

FileMaker ファイルをクラウドストレージに置いて使うことを推奨されていません。推奨されていないというのがどういうレベルのお話なのか判りませんが、クラウドストレージに置いて使いますよ。そして多分、この事が破損エラーと無関係ではないと半ば確信しています。

関連データファイルがたくさんあって、それなりにデータ量がある場合、クラウドストレージの同期に僅かに時間差があり、その時間差の最中に一部ファイルが変更されて保存された場合など、破損が起きやすい状況というものが想像できます。

LastWindowClose スクリプトで時間をキープする手立てが有効なのも、このことと無関係ではないでしょう。

最初この投稿を書き始めたときは「原因も対策もさっぱりわからない」と思っていましたが、案の定書いているうちに核心に迫ってきていると自覚しつつあります。

ということは、対策としてはクラウドストレージに置いて使わないことが有効なのかもしれません。でもごめんね、自分には必要なのでクラウドストレージに置いて使います。最大効果的な対策、こまめなバックアップで乗り切ります。

疑い

外部データソースのパスにグローバル変数を使えるようになったのは FileMaker 16 からだそうです。歴史が非常に浅いです。事例もほとんど見当たりません。この機能について掘り下げた研究を見かけたこともありませんでした。

最近のバージョンではスクリプトにトランザクションが加わりましたが、FileMaker そのもののトランザクション処理と言いますか、ファイルの関連付けや温存・保存のタイミングなど、設計が古いままだとクラウドストレージに対応できないことがあるのかもしれません。もしかしたらトランザクションステップが追加された以降のバージョンではこの部分に改良があったかもしれませんが判りません。

 

コメント

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください