PowerCMS X ブログ

2023-12-06

SplitPageプラグインで記事の最新 n件を対象にページ分割したい

SplitPageプラグインは記事などの一覧ページを指定件数で分割するオプションプラグインです。

このプラグインを使うと件数の多いページを指定件数で分割するだけでなく、ページネーションを表示させることもできて便利ですが、件数が多くなると再構築の負荷が高くなります。日付アーカイブ(月別)などと比較して効率が悪いことに留意してください。

ページネーションの例

  • 月別アーカイブが更新されるのは、その記事が属している(日付が変更された時はその記事が属していた)月の一覧のみ
  • SplitPageプラグインによって分割されたページは、どの記事が更新・追加・削除されても全てのページが更新(再構築)される

そこで、件数が多い場合、最新の一定の件数分を取得して、それを対象に分割したいケースに対応する方法について紹介します。例として、公開日順に最新100件を対象にページ分割するビューの書き方を考えてみます。

mt:emtries limit="100"指定は SplitPageでは効かない

思いつくのはループタグに limit(lastn)を指定することですが、SplitPageプラグインは条件に合致するものすべてを対象に分割するため、この指定は効きません。

offset, limit指定で101件目の日付を取得してconditions属性を利用する

そこで、conditions属性を指定して101件目の公開日を取得し、その日付以降のものをフィルタリングします。

conditions属性には「カラム名=フィルタ条件,フィルタの値」という形式でハッシュ変数を生成して渡します。

指定可能なフィルタ

フィルタ 説明
ct (like) Contains (含む)
nc Not Contains (含まない)
gt Greater Than (より大きい)
lt Less Than (より小さい)
ge Greater than or Equal (以上)
le Less than or Equal (以下)
eq Equal (等しい)
ne Not Equal (等しくない)
bw Begin with (から始まる)
ew End with (で終わる)
<mt:setvar name="last_update" value="2020-01-01 00:00:00" note="件数に満たなかった時の初期値">
<mt:entries cols="published_on" sort_by="published_on" sort_order="descend" offset="100" limit="1">
    <mt:ignore>日付順で101番目に新しい記事の公開日を取得</mt:ignore>
    <mt:entrydate setvar="last_update">
</mt:entries>
<mt:ignore>gt=より新しい</mt:ignore>
<mt:sethashvars name="conditions">
    published_on=gt,<mt:var name="last_update">
</mt:sethashvars>
<mt:entries conditions="$conditions" _split="1" sort_by="published_on" sort_order="descend">
    ...
</mt:entries>

この方法では、101件目の記事と全く同じ公開日時の記事があった時に件数が合わない(件数が不足する)可能性があります(published_onではなくidを利用すればそのようなことはなくなります)。確実に100件を取得するには「gt」の代わりに「ge(以上)」を指定すると良いでしょう(逆に多くマッチする可能性があります。)。

<mt:setvar name="last_update" value="2020-01-01 00:00:00" note="件数に満たなかった時の初期値">
<mt:entries cols="published_on" sort_by="published_on" sort_order="descend" offset="99" limit="1">
    <mt:ignore>日付順で100番目に新しい記事の公開日を取得</mt:ignore>
    <mt:entrydate setvar="last_update">
</mt:entries>
<mt:ignore>ge=同じか、より新しい</mt:ignore>
<mt:sethashvars name="conditions">
    published_on=ge,<mt:var name="last_update">
</mt:sethashvars>
<mt:entries conditions="$conditions" _split="1" sort_by="published_on" sort_order="descend">
    ...
</mt:entries>

カテゴリー:テンプレート作成Tips | プラグイン | 技術情報

投稿者:Junnama Noda

2023-12-05

PowerCMS Xの CMSサーバーを EC2冗長構成にする

規模の大きなウェブサイトやアプリケーションを構成する場合に冗長化構成を求められるケースがあります。
この記事では、AWSを利用した CMSサーバー*の冗長化構成のポイントについてまとめます。

EC2を冗長化する構成例

※ 公開側については AWS S3に静的ファイルを配信して Amazon CloudFrontで配信するようなケースが多いと思いますが、説明が複雑化するため、この記事では CMSサーバーに焦点を絞ります。

ポイントは 3つです。

  • 共有ディスクを設定する
  • 適切なキャッシュドライバを選択する
  • スケジュールタスクを負荷分散させる

共有ディスクを設定する

ドキュメントルート配下のコンテンツ以外に、以下のディレクトリを共有ディスクに設定します。lsyncdを使わずに共有ディスクにする理由は以下の通りです。

  • 大量のファイルに更新が入った時に高負荷状態になる
  • 双方向同期でデータが壊れることがある(Hyper Estraierのインデックスファイルなど)
  • タイムラグが発生する可能性がある※

※ ただし、EFSをマウントしたとして、書き込みが遅い問題やタイムラグが出ることもあります。

/home/がマウントされたディスクだとして、アプリケーションディレクトリを以下に設定します。

/home/www/PowerCMSX

他、以下のディレクトリを共有ディスク配下に設定します。

config.json

        "temp_dir": "/home/www/powercmsx-files/tmp",
        "cache_dir": "/home/www/powercmsx-files/cache",
        "support_dir": "/home/www/powercmsx-files/support",
        "assets_c": "/home/www/powercmsx-files/assets_c",
        "assets_c_path": "/assets_c/",
        "tmpl_paths": [
            "/home/www/powercmsx-files/user-customize-files/alt-tmpl"
        ],
        "plugin_paths": [
            "/home/www/powercmsx-files/user-customize-files/plugins"
        ],
        "model_paths": [
            "/home/www/powercmsx-files/user-customize-files/models"
        ],
        "theme_paths": [
            "/home/www/powercmsx-files/user-customize-files/themes"
        ],
        "import_paths": [
            "/home/www/powercmsx-files/user-customize-files/import"
        ],
        "htmlimporter_settings_paths": [
            "/home/www/powercmsx-files/user-customize-files/import_settings"
        ],
        "log_dir": "/home/www/powercmsx-files/logs",

/home/www/powercmsx-files/user-customize-files/ というやたらと長いパスが指定されているのは、代替テンプレートやモデル定義ファイルなどは、PowerCMS Xではパスの長いものが優先されるためです。

/home/www/PowerCMSX/Plugins/VideoCaptions/alt-tmpl/include/edit/upload_file/column_extra_path.tmpl
/home/www/powercmsx-files/user-customize-files/alt-tmpl/include/edit/upload_file/column_extra_path.tmpl //こちらが優先される
/home/www/PowerCMSX/lib/PAML/models/entry.json
/home/www/powercmsx-files/user-customize-files/models/entry.json //こちらが優先される

EFSが遅い対策

あらゆるファイルの読み書きのシーンで EFSが遅いと感じるかもしれませんが、スケジュールタスクについては実行時の一時ディレクトリをローカルの速いディスクに変更できます。「SearchEstraier」や「DocumentSearch」で大量のコンテンツの検索インデックスを洗い替えする時など、ディスクの速度によって実行時間に大きな差が出ます。

config.json

        "work_dir": "/var/www/tmp",

※ temp_dirを共有しなければならない理由は、例えばファイルアップロード後に「保存」ボタンをクリックしてオブジェクトを保存する時、違うインスタンスにリクエストが向いてしまうとファイルを保存できないなど、リクエストをまたがって利用されるファイルがあるからです。

適切なキャッシュドライバを選択する

PowerCMS Xではデータベースへのクエリのみならず、テンプレートのコンパイル結果など様々なデータをキャッシュすることで高速に動作するようになっています。
望ましいのは Amazon Elastic Cacheを利用することです。PowerCMS Xのキャッシュドライバは「Redis」「Memcacached」に対応しています。

避けるべきは EFS配下にキャッシュディレクトリがある場合にキャッシュドライバに「File」を指定することです。読み書きの遅さの影響を受けて遅くなるからです。

Amazon Elastic Cacheが利用できない場合、「MySQL」を指定してください。最新版では、「MySQL」キャッシュの性能が向上しています。

config.json

        "cache_driver": "Redis",

 

あるいは

        "cache_driver": "MySQL",

スケジュールタスクを負荷分散させる

通常スケジュールタスク(worker.phpなどによって実行されるバッチ処理)は一台で実行するように考えていると思いますが、冗長化構成の時、各々のマシンで異なるタスクを処理するような設定が可能です。

*/10 * * * * apache cd /home/www/PowerCMSX/ && /usr/bin/php ./tools/worker.php --exclude_ids searchestraier_update_idx,scheduled_publish,scheduled_replacement,scheduled_unpublish
*/10 * * * * apache cd /home/www/PowerCMSX/ && /usr/bin/php ./tools/worker.php --exclude_ids searchestraier_update_idx --task_ids scheduled_publish,scheduled_replacement,scheduled_unpublish
0    3 * * * apache cd /home/www/PowerCMSX/ && /usr/bin/php ./tools/rebuildFiles.php archive --urlmapping_ids 1,2,45,46
0    3 * * * apache cd /home/www/PowerCMSX/ && /usr/bin/php ./tools/worker.php --task_ids searchestraier_update_idx

worker.php 実行時には temp_dir配下に「.pid」ファイル(e12d4d581bc48366a79acf9f5a8b0206.pidなどのハッシュ値)が生成され、次回実行時にファイルが存在する時は実行がスキップされます。
この「.pid」ファイルは worker.php に渡す引数によって異なるファイル名になるため、上記のように実行するタスクを分けて cronに設定することができます。

上から

  • HyperEstraierのインデックスの洗い替え、時限公開・公開停止・差し替えを除くスケジュールタスクを10分ごとに実行
  • HyperEstraierのインデックスの洗い替えを除外、時限公開・公開停止・差し替えの時限公開・公開停止・差し替え(他のタスクの実行時間の影響を受けずに時限公開を反映できる)
  • 特定の URLマップに対するアーカイブを毎朝 3時に再構築
  • HyperEstraierのインデックスの洗い替えを毎朝 3時に再構築

となります。これを 2台に分散させます。

EC2 マシンA

*/10 * * * * apache cd /home/www/PowerCMSX/ && /usr/bin/php ./tools/worker.php --exclude_ids searchestraier_update_idx,scheduled_publish,scheduled_replacement,scheduled_unpublish
0    3 * * * apache cd /home/www/PowerCMSX/ && /usr/bin/php ./tools/rebuildFiles.php archive --urlmapping_ids 1,2,45,46

EC2 マシンB

*/10 * * * * apache cd /home/www/PowerCMSX/ && /usr/bin/php ./tools/worker.php --exclude_ids searchestraier_update_idx --task_ids scheduled_publish,scheduled_replacement,scheduled_unpublish
0    3 * * * apache cd /home/www/PowerCMSX/ && /usr/bin/php ./tools/worker.php --task_ids searchestraier_update_idx

カテゴリー:サイト制作全般 | 技術情報

投稿者:Junnama Noda

2023-12-03

運用ミスを防ぐ観点から機能や設定を考える

PowerCMS Xではアセット(ファイルアップロード)機能について、ステータス管理が標準で可能になっていて、下書き(非公開)、公開、日時指定公開、時限公開(日時指定で非公開)ができます。シビアに適時開示しなければならないIR情報や事前に漏洩しては困る新商品情報などを管理するために役立ちます。

しかしながら、実際のウェブサイトの運用においては、それだけでは事足りず、色々な要求が出てきます。その一つが「記事に添付したアセットの同時公開」ですが、PowerCMS Xにはそれを実現するためのいくつかの方法が提供されています。

バイナリカラムや添付ファイルは「オブジェクトとファイルが1対1」で関連付いていてオブジェクトが公開されれば同時に公開され、削除や非公開化されると非公開になります。

問題は「UploadUtilities」プラグインの「ステータス同期」機能なのですが、添付ファイルやバイナリカラムのファイルと違い、記事アセットやページアセットなど複数のオブジェクトと関連付けされていることから、単純に参照元のオブジェクトのステータスと連動させるわけにはいきません。

そこで、「すべての参照元オブジェクトのステータスが同じ場合のみ」アセットのステータスが同期する、がこれまでの仕様でした。

しかし、実際の運用では上手くいかないケースが多発します。

  • 記事を公開したのにアセットが公開されない
  • 意図せずアセットが非公開にされた

自分としてはこのタイミングで公開されると思っていたのに、他の未公開の記事にも関連付けされていた、というケースで、この問題が起こりえます。自分がアップロードした画像が他の記事に使われていた、など、気付かないことがあるからです。

そこで、(互換性を維持した上で)機能を変更できるようにしました。

UploadUtilitiesプラグインに機能を追加

UploadUtilitiesのプラグイン設定画面

考え方としては

  • アセットとリンクしているオブジェクトがどれか一つでも公開されたら、アセットは公開される
  • 一度公開されたアセットのステータスは変更しない

というものです。後者は「一度公開されたものはインターネット上に公開されたものなのだから非公開にする意味はなかろう」という考え方によります。ただ、それはウェブサイトを運用する側のポリシーによりますから、以下のように機能追加しました。もちろん互換性を保った上で。

参照しているオブジェクトが一つでも公開されたら関連付けされているアセットは公開される

この時、オブジェクトが非公開にされたときは、参照元オブジェクトのステータスのうちのステータスの最大値になる(つまり、公開されているオブジェクトと関連付いているのなら非公開にはならない)ようにします。

公開済みのアセットは非公開にしない

もしくは、公開済みのアセットは非公開にならない「一度公開されたアセットは、参照元オブジェクトのステータス変更の影響を受けない」というものです。考え方によるのですが、「一度インターネット上に公開されたものは、非公開にしたところで意味はない」と考えれば、それはそれで合理的にも思えます。

設定の組み合わせによって、以下のようにできるようになりました。

  • 他のオブジェクトで利用されている場合、すべてのオブジェクトのステータスが一致した時のみ同期します。
  • オブジェクトの公開時はアセットのステータスを公開、非公開時は関連オブジェクトのうち終了以外の最も値の大きなステータスにセットします。
  • 他のオブジェクトで利用されている場合、すべてのオブジェクトのステータスが一致した時のみ同期します。一度公開されたアセットのステータスは変更しません。
  • オブジェクトの公開時はアセットのステータスを公開しますが、一度公開されたアセットのステータスは変更しません。

 

カテゴリー:プラグイン | サイト制作全般

投稿者:Junnama Noda

ブログ内検索

アーカイブ