キャッシュと同期

このセクションでは、Curl アプレットおよびアプレットのサポート ファイルのキャッシュと同期について説明します。
以下のセクションで、Curlアプリケーションでの効率的で効果的なキャッシュと同期の推奨設定を説明します。

同期とキャッシュの概要

同期の対象は、クライアント マシンのローカルにキャッシュされているコンテンツだけです。Curl のアプレットでは、このようなコンテンツには、パッケージ、マニフェスト、HTTP を通じてインターネット上からロードされたすべてのコンテンツなどが含まれます。「ディプロイメントの問題」のセクションには、キャッシュと最小スタイルのブラウザ常駐 HTTP を使用した時の再同期についての説明があります。
Curl のコンテンツに影響するキャッシュには、次の 3 つのレベルがあります。
  1. HTTP キャッシュ は、HTTP プロトコル (たとえば、プレフィックスが "http:""https:" の URL) を使用してダウンロードされたすべてのコンテンツに影響します。HTTP キャッシュは、パッケージやマニフェストのインポートの結果として間接的にロードされるファイルだけでなく、read-open を使用して直接ロードされるファイルと、関連する API にも適用されます。
    HTTP キャッシュでは、コンテンツがキャッシュ内に既に存在している場合、コンテンツをサーバーからダウンロードする必要がないため、コンテンツ全体のロード時間が短縮されます。ファイルは、新しいバージョンで置き換えられるか、キャッシュが一杯になった場合に領域確保のため削除されるか、または、ファイルの HTTP ヘッダーに示されているキャッシュ ディレクティブの 1 つに従い、期限切れになるまでHTTP キャッシュ内に保持されます。一般に、この期限は動的に生成される HTTP コンテンツ (CGI スクリプトによって生成されるコンテンツなど) に対してのみ設定され、静的に展開されるファイルではあまり使用されません。キャッシュと同期に影響するヘッダーの詳細については、「RFC 2616:Hypertext Transfer Protocol—HTTP/1.1」を参照してください。
    Windows では、Curl RTE は Internet Explorer と同じ HTTP キャッシュを使用し (アプレットが Mozilla Firefox などの異なるブラウザでロードされる場合であっても)、コントロール パネルの [インターネットのプロパティ] でユーザーが設定したインターネット一時ファイルの設定の影響を受けます。
  2. メモリ内キャッシュ はパッケージおよびマニフェストに対して使用されます。メモリ内キャッシュは、Curl RTE がシャットダウンされるまで、RTE の1つのセッションの継続を維持します。
    パッケージおよびマニフェストのメモリ内キャッシュでは、複数のアプレット インスタンスまたは同じアプレットの一連のロードで、既にロードされているコンポーネントを共有できます。これにより、RTE で既に使用されているパッケージの再コンパイルおよび再ロードにかかる負担を削減できます。RTE は未使用のパッケージおよびマニフェストを、アプレットがロードを終了した数秒後といったタイミングでフラッシュします。
    image テキスト プロシージャもデータをキャッシュしますが、1つのプロセス内だけです。複数のアプレット間でデータを共有することはありません。
  3. パーシスタント キャッシュは バージョン 3.0 の API で初めて導入され、パッケージ専用に使用されます。パッケージは、バイナリ形式でディスクにキャッシュされ、ロード時の再コンパイルは不要です。パーシスタント キャッシュされたパッケージは、内容が最新でないことが確認された場合、またはキャッシュが一杯のため領域を確保する場合に、キャッシュから削除されます。
    既定では、すべてのパッケージがパーシスタント キャッシュされます。パーシスタント キャッシュは applet または script 宣言に package-caching-style 仕様を追加することにより制御したり無効にしたりできます。
Curl RTE の同期メカニズムでは、これら 3 つのすべてのレイヤのキャッシュが対象になります。
RTE は、すべてのコンテンツを process-resync-as-of から返される時刻で同期し、この時刻はプロセスの開始時刻よりも後にはなりません。この時刻は、appletscript、あるいは manifest 宣言内の resync-as-of キーワードによって指定された時刻に設定することが可能です。またこの時刻は、「強制的な同期」で説明されている強制的な再同期のメカニズムの1つによって、プロセスの開始時刻に設定することも可能です。
パッケージおよびマニフェストは、 package もしくは manifest 宣言内で cache-duration を宣言することにより再同期をせずにキャッシュできる最長時間を指定できます。
コンテンツが最新であるかどうかは、元のファイルのキャッシュ変更時のタイムスタンプに基づいて判別されます。ファイルの変更時刻を変更すると、コンテンツが変更されていない場合であっても、ファイルは最新でないと見なされる可能性があります。場合によっては、RTE がパッケージまたはマニフェストを再ロードした後、内容に変更がないことを検知し、引き続き前のバージョンのキャッシュを使用することもあります。

強制的な同期

process-resync-as-of がプロセスの開始時刻に設定されている場合、同期が強制的に行われます。この強制的な同期は、さまざまな方法で実行されます。
自動同期
Curl コントロール パネルの [全般] タブには、アプレットの強制同期に影響する設定が含まれています。ユーザーは、すべてのアプレットをロード時に再同期するか、デバッグ可能なアプレットだけを強制同期の対象にする (これが既定の設定) か、または自動強制同期を無効にするかを指定できます。
ローカル ファイル システムにあるデバッグ可能アプレットは、この設定とは無関係に常に同期されます。
Curl IDE の [実行] メニューから起動されるアプレットも、自動的に同期されます。
プログラムによる同期
applet または script 宣言の resync-as-of に開始時刻よりも後の時刻を指定すると、同期が強制されます。例として、次の宣言が指定されたアプレットを見てみましょう。
{applet resync-as-of = {utc-date-time "3000-01-01"}}
これは、プロセスの開始時刻に常に同期されます。ただし、同期の実行ではロード時に非常に多大なオーバーヘッドが要求されるため、アプレットの resync-as-of 宣言ではアプレットによって直前に使用されたコンテンツが http サーバーにディプロイされた時刻を参照するよう指定することをお勧めします。
より良い技術が、Curl 7.0で導入されました。それは、その最終更新時刻が同期時刻として使用される ファイルを記述することです。同期を強制する為には、そのファイルだけ修正する必要があります。 例えば、以下の宣言は、マニフェストの最終更新時刻がアプレットの同期時刻として使用されるように します。
{applet
    resync-file = "manifest.mcurl",
    manifest = "manifest.mcurl"
}


アプレットやスクリプトが、resync-as-of または resync-fileを直接宣言していないが、マニフェストを記述している場合には、RTEは、マニフェストのresync-as-ofがあれば、それを使用して process-resync-as-of時刻を設定します。
アプレット内に resync-as-of/resync-file 宣言がない場合は、マニフェスト自体が同期されず、したがってこの手法を使用する場合は、マニフェストは cache-duration を指定して有効なウィンドウ内で確実に同期されるようにしてください。cache-duration 属性についての詳細は、「パッケージおよびマニフェストでの cache-duration の使用」を参照してください。
また、request-resync-on-reload 関数を使用して、アプレットが次にロードされるときに強制同期を要求できます。
手動による強制同期
ユーザーは、上記で説明した Curl コントロール パネルの設定を変更して、アプレットを手動で強制同期できます。ただし、同期を頻繁に行う必要がない場合、この方法は便利ではありません。
代わりに、ブラウザで実行しているアプレットを右クリックして表示されるコンテキスト メニューから、[再ロード時に再同期する] オプションを選択し、アプレットをブラウザに再ロードします。このコンテキスト メニューはアプレットによってオーバーライドされている可能性があるため、一部のアプレットではこのオプションを使用できない場合があります。ただし、Curl IDE がインストールされている場合は、[Ctrl] キーを押しながら右クリックすると、コンテキスト メニューで同じオプションを使用できます。
Curl スクリプトを使用する場合は、--resync フラグを使用して、コマンド ラインから同期を強制できます。たとえば、次のようにします。
> curl --resync my-script.xcurl

個々のコンポーネントおよびファイルの同期

process-resync-as-of 設定は、プロセスでロードされるすべてのファイルおよびコンポーネントに適用されますが、プロセスで使用されるファイルやコンポーネントのサブセットだけを同期する場合にも便利です。ここでは、この操作を行うためのメカニズムについて説明します。

パッケージおよびマニフェストでの cache-duration の使用

マニフェストで cache-duration 宣言を使用すると、マニフェストが同期されます。cache-duration 属性は、パッケージやマニフェストなどのキャッシュされたコンポーネントを再同期せずに使用できる時間を指定します。たとえば、パッケージがインポートされてから1時間以内に確実に同期されるためには次のように宣言します。
{package ALPHA,
    cache-duration = 1hour
}
{let public constant alpha:int = 1}
アプレットで resync-as-of が指定されていない場合に、マニフェストを強制的に同期させる場合を除き、cache-duration 属性の設定は非常に慎重に使用しなければなりません。まれにしか変更されない特定のパッケージやマニフェストに依存し、かつ変更がないと想定されるその他のコンポーネントよりも、頻繁に同期する必要がある状況でのみ使用するよう設定します。たとえば、上記の ALPHA パッケージとそれをインポートする別のパッケージがあるとします。
{package BETA}
{import public alpha from ALPHA}
{let public constant beta:int = 1}
前回の同期から 1 時間以上経過した後でアプレットが ALPHA をインポートすると、このパッケージは再同期されます。つまり、alpha に加えられた変更は、変更から 1 時間以上経過した後でこのパッケージをインポートするアプレットに確実に反映されます。これに対して、アプレットが BETA をインポートする場合は、BETAALPHA に依存する場合であってもパッケージを同期する必要はありません。ALPHA が変更されると、BETA は最新バージョンの ALPHA を使用するために再コンパイルされますが、BETA 自体は再同期されません。このため、アプレットは BETA の元の HTTP ファイルに加えた変更を反映しません。
このため、次のような状況では cache-duration を使用すると問題が発生します。
たとえば、alphaALPHABETA の両方で alpha-constant に名前を変更された場合、キャッシュ期限が切れた後で ALPHA パッケージへの変更だけが通知され、BETA はコンパイルに失敗します。

with-file-caching-style を使用した HTTP ファイルの同期

with-file-caching-style ステートメントを使用すると、このステートメント範囲内で HTTP コンテンツを動的に読み込む際に使用されるファイルキャッシュの設定を変更できます。
たとえば、次のステートメントはファイルを読み込む際にそのファイルを強制的に同期します。
let content:StringBuf =
    {with-file-caching-style FileCachingStyle.resynchronize do
        {read-from {choose-location}}
    }
HTTP 同期の各種設定の説明は FileCachingStyle を、より広範な実行可能例については with-file-caching-style を参照してください。
この方法で元の import-package ファイルと import-manifest ファイルに対して HTTP 同期を強制的に実行できますが、パッケージまたはマニフェストが内部メモリまたはパーシスタント キャッシュ内に存在しない場合に限られます。
with-file-caching-style ステートメントを使用して、image テキスト プロシージャで使用するデータのキャッシュを無効にすることもできます。
ただし、with-file-caching-style は、トップレベルの include ステートメントまたは import ステートメントの前後で使用できない場合があることに注意してください。

動的にインポートされるマニフェストの同期

通常、メイン マニフェストとそのデリゲート マニフェストは、applet 宣言または script 宣言の manifest ステートメントに基づいて暗黙にロードされます。ただし、import-manifest を使用してマニフェストを動的にロードすることもできます。import-manifest の既定の引数では、上記で説明した標準プロセスとコンポーネントの同期設定を使用できます。ただし、呼び出し元が check-out-of-date? フラグを true に設定し、with-file-caching-style に呼び出しをラップして HTTP ファイルの同期を強制的に行うことにより、プロセスの開始時刻の後での同期を明示的に強制できます。
let manifest:ComponentManifest =
    {with-file-caching-style FileCachingStyle.resynchronize do
        {import-manifest
            manifest-url,
            check-out-of-date? = true
        }
    }
import-package 構文には、import-manifest 関数の check-out-of-date? キーワードに類似したオプションはなく、インポートされた個々のパッケージを強制的に同期する直接的な方法がありません。ただし、パッケージは既定のマニフェストの ID に依存するため (get-default-manifest を参照)、パッケージの場所が含まれたマニフェストを変更し、それを使用してパッケージをインポートすると、新しく同期されたバージョンのパッケージとなります。
let package:Package =
    {with-file-caching-style FileCachingStyle.resynchronize do
        {import-package
            package-selector,
            manifest = manifest
        }
    }
このやり方がうまくいくためには、インポート対象のパッケージを検索するためのエントリがマニフェストに含まれており、またマニフェストのコンテンツを何らかの方法で変更できる必要があります。
パッケージとマニフェストはそれらを使用するすべてのプロセスが終了しない限りメモリから解放されないため、この方法でパッケージとマニフェストを強制的に再ロードするとメモリが無駄に消費される可能性があります。さらに、このような強制的な再ロードは、パッケージが互換性のない API と混合される可能性もあり、結果的にさまざまなエラーが発生することになりかねません。これらの問題により、絶対的に必要である場合を除いて、個々のマニフェストおよびパッケージをこの方法で強制同期することは避けてください。この方法を使用する必要があると思われる場合は、代わりに、頻繁に変更されるコードを別のファイルに入れ、evaluate を使用して別の OpenPackage にロードすることをお勧めします。

推奨設定の概要

このセクションでは、キャッシュと同期を効果的に行い、プログラムの機能を正常な状態に維持するための Curl アプリケーションの推奨設定について説明します。ここで要約されたポイントは「推奨設定の詳細」で詳しく説明されています。
  • ファイル変更時刻の管理
  • アプレット
  • 推奨設定の詳細

    サーバーの設定

    以下のことを推奨します。
    Curl のアプリケーションの開発、ディプロイ、および配信に関わるすべてのマシン(サーバー、開発用マシンともに)の時刻設定を常に同期させた状態にしてください。Curl RTE の同期メカニズムは Web ブラウザのメカニズムと同様に、静的なコンテンツのファイル変更時刻に依存しています。
    開発およびディプロイメントのためのマシンの日時を常に同期させることが重要です。それらのマシンのファイル更新日時が、HTTP サーバーにディプロイされるファイルの更新日時に影響を与えるからです。また、サーバー マシン同士を常に同期させることも重要です。そのことが低レベルの HTTP キャッシュ動作に影響を与えるからです。サーバーと開発用マシンを同期させておくことで、以下に説明するようなサーバーのファイルの将来の更新日時が間違ったキャッシュの結果を引き起こす可能性を軽減することができます。
    マシンを同期させる適切な方法はプラットフォームによって異なりますが、その方法は難しくありません。Windows では、[日付と時刻] のコントロール パネルで時刻の同期を設定できます。サーバー マシンをタイム サーバーとして設定しますが、ファイアウォールが外部のタイム サーバーに対して NTP リクエストを許可する設定が必要であるかもしれません。Linux 上では、NTP サービスのセットアップが必要となります。
    HTTP サーバーで、アプレットの起動ファイルの有効期限を短くすることを推奨します。ただし、アプレットの起動ファイルから直接または間接的に呼び出される更新の必要がないその他のファイルに対しては、この設定はしないでください。アプレットの resync-as-of 宣言によって、Curl RTE がロードするファイルに関しては同期されているか確認できますが、アプレットの起動ファイル自体の同期は Web ブラウザによって行われているため、Curl RTE では制御できません。エンドユーザーは [Ctrl] キー(IE の場合)または [Shift] キー(Netscape/Mozilla の場合)を押しながら、再ロードボタン(通常は F5 キーに割り当てられています)を押すことにより、ブラウザで強制同期ができますが、これにはエンドユーザーがいつ強制同期をすべきか知っている必要があります。有効期限が切れる時期はアプリケーションの性質によって異なりますが、アプレットが非常に大きい場合や、ユーザーが変更にすぐに気付く必要がない場合を除き、有効期限がすぐに切れるよう設定することが理にかなっています。アプレットが使用する他のファイルに有効期限を設定すると不必要な HTTP の同期が発生するので、推奨できません。

    ファイル変更時刻の管理

    以下のことを推奨します。
    最新のタイムスタンプが重要なファイル属性となり、これが Curl RTE と、基礎となる HTTP レイヤの中で静的に生成されたコンテンツのキャッシュと同期を制御します。サーバー上のコンテンツを配信する際に、Web ブラウザによってファイルの最新のタイムスタンプが HTTP ヘッダーの Last-Modified に設定されます。この値はファイルのコンテンツをキャッシュするときは、HTTP キャッシュによって保存され、また、Curl のパッケージをキャッシュするときは、Curl RTE によって保存されます。この振る舞いはファイルによって異なります。ファイル更新日時(タイムスタンプ)が変わると、コンテンツが変更され再ロードが必要であると解釈されます。
    HTTP の標準では、サーバーが将来の時刻の Last-Modified ヘッダーを返さないように既定されています。ファイルのタイムスタンプが将来の場合、サーバーは Last-Modified ヘッダーを設定する前に、更新日時を現在の日時に変換しなければなりません。その結果として、サーバーに将来日時のファイルを置いた場合、サーバーの設定日時が現在の日時となるまで、サーバーからファイルがロードされるたびに異なる Last-Modified となってしまいます。Curl のパッケージやマニフェストでこの状況が起こると、Curl RTE はコンテンツには実際にはなかった変更があったとみなし、不必要な再コンパイルが実行されます。これは大きなアプリケーションではロード時間が大幅に増加することになるので、この状況を避けることが重要になります。Curl のアプリケーションの開発、ディプロイおよび配信に関わるすべてのマシンの時刻設定の同期がとれていれば、このようなことが起こる可能性は低いでしょう。
    同様の理由で、すべてのミラーサーバー上の同じファイルが同じタイムスタンプであることが非常に重要となります。そうでない場合、クライアントが1つのサーバーからファイルを読み込み、その後別のサーバーから同じファイルを読み込むと、Last-Modified が異なるためにファイルが期限切れだと判断してファイルの再ロードや、それに依存するすべてのコンポーネントの再構築を要求します。この振る舞いによって、付加分散のために複数設置されている HTTP サーバーからロードする利点がなくなってしまいます。
    Curl パッケージの不必要な再ロードや再コンパイルを最小限に抑えるために、サーバーに配置されたときからコンテンツに変更がない場合は、そのファイルの最終更新日時(タイムスタンプ)を変更しないでください。
    サーバー上のファイルを現在よりも古い更新日時のファイルと置き換えないでください。クライアントがそれらのファイルを適切に更新しない可能性があります。HTTP プロトコルの設計は、Last-Modified の日時が修正される直前のファイルの更新日時よりも先に進むことを前提としています。したがって、ほとんどの HTTP クライアントとサーバーは更新日時が古いものに変更されても、コンテンツを更新しないように実装されています。サーバー上のファイルの更新日時が古い場合、Web ブラウザと Curl RTE はクライアント上のブラウザ HTTP キャッシュがクリアされるまでサーバー上の変更に気づかない可能性があります。
    このような状況はほとんどの場合、サーバー上のコンテンツを古いものに戻すときに起こると思われます。このような変更は、新しいファイルを古いタイムスタンプを持つ古いファイルと入れ替えてしまう可能性があります。この問題を避けるために、サーバーにディプロイする前に、常にファイルの更新日時を最新のものにしてください。

    アプレット

    以下のことを推奨します。
    Web ブラウザがメインのアプレットの起動ファイルをロードし、そのコンテンツが Curl RTE に渡されると、Curl RTE がアプレットで使用される残りのファイルの同期を制御します。この RTE による同期は、以下のようなアプレットのヘッダーの resync-file 宣言によって制御することができます。
    {curl 8.0 applet}
            {applet
                manifest = "manifest.mcurl",
                resync-file = ""
            }
        
    この宣言は、Curl RTEにアプレットで使用される全てのファイルと、キャッシュされているコンテンツ(Curl パッケージやマニフェストのような)をアプレット ファイルの最終更新時刻で同期させるようにします。その時刻は、クライアントとサーバー間で観測された時差で調整されています。resync-fileの値の他の合理的な選択は、マニフェスト ファイルまたは、この目的の為だけに使われる空のファイルです。ファイルと他のキャッシュされたコンテンツ全てが、指定された時刻よりも最近に同期されている場合、Curl RTEは、アプリケーションによって要求されない限り、サーバーとコミュニケーションする必要がありません。この技術を使用して、Webサーバー上のコンテンツを更新した時、同期ファイルの修正時刻を更新を忘れずに行うことは重要です。
    非常に稀ですが、アプレットが、間違った時刻が設定されているWebサーバー によってホスティングされ、それが制御ず、resync ファイルの修正時刻が 正しく設定された(例えば、Webサーバー上で設定されなかった)時、追加の 宣言による自動的な時刻調整を望まないでしょう。
    resync-adjust? = false
    
    同期の制御に resync-as-of コマンドの使用は推奨しません。 何故なら、これは更新が難しく、クライアントとサーバー間の時刻の差に 適合できないからです。遠い将来の日付を設定することで、常にアプレットの 同期を強制するような場合には、便利です。
    Curl RTE は、コントロールパネルの [全般] タブでアプレットの強制再同期設定を提供していますが、この設定を使用したクライアント マシンに同期の制御を依存することは推奨しません。アプリケーションを提供する側が、すべてのクライアントでこの強制再同期の設定がなされているかを確かめる方法がなく、エンドユーザーは容易に設定を変えることができてしまいます。クライアントの設定を変更することなく宣言を変更できるので、アプレットに resync-file 宣言を記述することを推奨します。アプレットを修正する際に実際のディプロイメント日時に近い値を resync-as-of の日時に設定したくない場合は、かなり先の将来の日時を設定することができます。将来の日時は再ロードのたびに再同期を強制します。これは将来、より正確な宣言を生成する選択肢を残しています。
    また、Curl RTE のバージョン 4.0 と 5.0 には既知のバグがあります。コントロールパネルで[すべてのアプレットの再同期を強制する。]を指定すると、バックグラウンドでパッケージのキャッシュを行う際に余分な同期が実行されてしまい、パフォーマンスの低下を招きます。このバグは、4.0.4 パッチと 5.0.2 パッチのリリースにより解決されました。
    アプレットの起動ファイルの有効期限を短く設定し頻繁に更新させたい場合、ダウンロードの負荷を最小限に抑えるためにファイルのサイズを小さくしておく必要があります。多くのアプレットの機能は Curl のパッケージの中に含めてください。Curl のパッケージはバイナリ形式でキャッシュされるので、2回目以降のアプレットのロード時間がかなり短縮されます。また、applet 宣言以下に記述されているコンテンツを他のファイルに分散し、そのアプレットでインクルードすることも可能です。これら方法の結果として、アプレットが再ロードされても再同期の必要がない場合は、メインのアプレットの起動ファイルだけは Web ブラウザによってサーバーから取得される必要がありますが、その他のファイルはクライアントの HTTP キャッシュから取得できます。

    キャッシュと同期の API の概要

    次の API 要素は、Curl のアプレットのキャッシュと同期に関連するものです。

    リソース

    HTTP 監視ツール

    リファレンス

    HTTP プロトコル

    Apache HTTP サーバー