A Day In The Boy's Life -30ページ目

A Day In The Boy's Life

とあるエンジニアのとある1日のつぶやき。

Linux上でファイルのバックアップしようと思って一番シンプルにするとなるとcpコマンドを使うことになるかと思います。

まぁ、単純に


$ cp foo bar

みたいなことをするんですが、GNUオプションをみているとバックアップする用途なら結構便利そうなオプションが幾つかあります。



コピーしたファイルに番号をつける


良くありがちなのが末尾に番号をつけていくというものです。


$ cp foo foo.1

みたいな。
ただ、番号(バージョン)をきちんと管理したいなら手動でやるとミスるので下記の--backupオプションが便利かもしれません。


$ cp --backup=numbered -f foo foo
$ ls
foo foo.~1~
$ cp --backup=numbered -f foo foo
$ ls
foo foo.~1~ foo.~2~

2回実行すると番号がインクリメントされていきます。
cpはコピー元とコピー先に同じファイル名を指定すると怒られるので-f(--force)オプションをつけてあげる必要があります。

--backupオプションはnumberedをつけなければ単純に末尾にチルダ(~)がついたファイルが作られます。


$ cp --backup -f foo foo
$ ls
foo foo~


コピーしたファイルに日付をつける


特にオプションをつけなくても


$ cp foo `date +foo_%Y%m%d`
$ ls
foo foo_20140513

のようなことをすれば日付がついたバックアップファイルをつけることができますが、--suffixオプションをつけることでも日付をつけたファイルを作ることができたりします。


$ cp --suffix=_`date +%Y%m%d` -f foo foo
$ ls
foo foo_20140513

この--suffixオプションは省略した場合、環境変数のSIMPLE_BACKUP_SUFFIXが使われ、デフォルトではチルダとなっています。


export SIMPLE_BACKUP_SUFFIX=_

などとして、サフィックスをアンダーバーに変更しておき、


$ cp --backup -f foo foo
$ ls
foo foo_

と実行すると、チルダではなくアンダーバーが付加されたバックアップファイルが作成されます。
常に日付をつけたければ


export SIMPLE_BACKUP_SUFFIX=`date +_%Y%m%d`

としておけば、--suffixオプションを指定せずに日付つきのバックアップファイルが作られます。


$ cp --backup -f foo foo
$ ls
foo foo_20140513


コピー先のバックアップファイル名の指定を省略する


通常、cpコマンドはコピー元とコピー先の2つを指定しなくてはいけません。


$ cp foo
cp: missing destination file operand after `foo'
詳しくは `cp --help' を実行して下さい.

ただ、下記のようにするとコピー先のファイル名を省略することが可能です。


$ cp foo{,.bk}
$ ls
foo foo.bk

日付を入れたい場合は、{}内にdateコマンドを差し込むことで可能です。


$ cp foo{,`date +_%Y%m%d`}
$ ls
foo foo_20140513

まぁ、コピー先のファイル名を指定したほうが早いという場合がありますが、ファイル名が長い場合やシェルスクリプトにcpコマンドを組み込みたいといった場合は便利かもしれません。





CakePHPのとあるアクションにアクセスしたら別ページに転送したくって下記のようなコードを書いてたんです。


public function index() {
    $location = "http://example.com/app/index";
    header("HTTP/1.1 302 Found");
    header("Location: $location");
}

で、何故かうまくいかなかったんで、Viewファイルも用意してそちらでもHTMLで転送するように書いていたんです。


<html>
<head>
<meta http-equiv="refresh" content="0;URL=http://example.com/app/index">
</head>
<body></body>
</html>

これでうまく転送されたんでほっといたんですが、よくよく見るとHTTPステータスが200を返されているわけです。

で、もう一度ちゃんとCakePHPのマニュアル読んだら色々間違っていたことがわかったんで、リダイレクト処理をする時のまとめのお話です。



CakePHPでページをリダイレクトする


最初のコードの問題の1つは、header 関数は呼び出した時点でヘッダを出力するのではないということです。

header()を呼び出してもヘッダ情報がセットされるだけで実際に出力されず処理が継続されるため(今まであまり意識することが無かったので気がつかなかったんですけど)、CakePHPはそのままViewを呼び出す処理を行います。

ですので、Viewファイルを作らなかったらMissingViewExceptionの例外が発生します。

一方でViewファイルを作ってしまったら正常にViewファイル内のHTMLが出力されるためHTTPステータスが200としてクライアントに返されます。

ってことで、アクションのコードだとheader()を出力した後にexitで抜けるとViewファイルを作らなくてもちゃんとリダイレクトされます。


もう1つの問題は、CakePHPはヘッダ出力用のメソッドが用意されていたということです。

header()でもリダイレクトはできるのですが、フレームワークを使っている以上、CakePHPのお作法に従ったほうがよさそうです。

ってことで、コードを書きのように書き換えればお作法に則ってリダイレクトさせることができます。


public function index() {
    $this->autoRender = false;
    $this->response->header('Location', "http://example.com/app/index");
}

autoRenderのメンバ変数はFALSEをセットすればViewを呼び出さないのでアクション内だけで処理を完結させることができます。

そして、$this->response->headerによってヘッダを制御 できます。

マニュアルを見ると


CakeResponse::header() が呼び出されなければヘッダは送られません。これらのヘッダはレスポンスが実際に送られるまでバッファリングされます。

って書いているので、ちゃんとCakePHPのお作法に従ってヘッダを送信したい場合は、CakeResponse::header()を使う必要があるようですね。


2014.05.14追記

CakeResponse::header()を使わなくてもController::redirectでできるよ!ってコメントを頂きました。

確かに下記のように書けばHTTPステータスコード付き(第2引数)でリダイレクト処理を行うことができます。


$this->redirect("/app/index", 303);

第3引数はexitフラグになっているようでFALSE(デフォルト)を指定すればリダイレクト後にexitで処理を抜けてくれます。

ですので、autoRenderの指定も必要なくなります。

また、コントローラやアクション名、パラメータなど細かい指定をしながらリダイレクトもできますので、プログラムから動的に制御したいといった場合などはこちらの方が便利かもしれませんね。

詳細は、マニュアル を参照してください。





Solrのデータの取り込み・インデックス作成は実行コストがかなり大きく、実行中に検索サービスが正常に提供できないことがあったりします。

そういった事態を避けるために、Solrのデータ管理を分散し、マスターにてデータ更新を行い、その反映データをスレーブにすばやく反映するためのレプリケーション機能がSolrには標準で用意されています。


Solrが差分の更新をうまくしてくれるので、データの取り込みをするよりレプリケーション機能を使った方がかなり高速にデータの反映が行えます。



Apache Solrでレプリケーションを利用する(マスターサイト設定)


ここではまず、レプリケーション機能を使うために2つのサイトを用意します。

1つ目がマスターサイトでデータの更新管理を行うサーバーになります。

もう一方がスレーブサイトで、基本的にマスターサイトからの更新データを受け取って反映するだけのサイトにはなりますが、サービス提供はスレーブ側で行います。

データの流れは常にマスターからスレーブに行うので、スレーブサイトでのデータ更新は行わず、ある意味Read Onlyで運用するイメージとなります。


まずは、マスターサイトでSolrの設定ファイル(solrconfig.xml)にて、レプリケーション時の挙動やレプリケーション対象の各種ファイルを設定します。

該当箇所は初期値ではコメントアウトされているのでコメントアウトを解除し、適宜編集します。


<requestHandler name="/replication" class="solr.ReplicationHandler" >
  <!--
     To enable simple master/slave replication, uncomment one of the
     sections below, depending on whether this solr instance should be
     the "master" or a "slave".  If this instance is a "slave" you will
     also need to fill in the masterUrl to point to a real machine.
  -->
     <lst name="master">
       <str name="replicateAfter">commit</str>
       <str name="replicateAfter">startup</str>
       <str name="replicateAfter">optimize</str>
       <str name="confFiles">schema.xml,stopwords.txt,mapping.txt,synonyms_index.txt,synonyms_query.txt,lang/userdict_ja.txt</str>
     </lst>
</requestHandler>

replicateAfterの設定は、レプリケーションを行うタイミングを指定します。

それぞれ、コミット時、Solr起動時、最適化実行時にレプリケーションが行われます。

ただし、レプリケーションをキックするのはスレーブ側で、そのレプリケーションタイミングはチェックする間隔を指定できるので、そのタイミングが来たときにマスター側で設定したレプリケーションすべきかどうかの判断基準を元に実行されるようです(後述)。


confFilesの設定は、インデックスデータのレプリケーションと一緒に同期する設定ファイルを記述しておきます。

これにより、マスター側でスキーマ情報(schema.xml)が変わったり、辞書データ(userdict_ja.txt)を変更してもスレーブ側に同時に反映させることが可能です。



Apache Solrでレプリケーションを利用する(スレーブサイト設定)


マスター側の設定が終わったら次はスレーブ側の設定です。

スレーブ側のSolrからマスターのデータを取りに行くので実質スレーブは幾つでも作ることができます。

スレーブ側の設定ファイルもマスターと同様にsolrconfig.xmlを編集します。


<requestHandler name="/replication" class="solr.ReplicationHandler" >
  <!--
     To enable simple master/slave replication, uncomment one of the
     sections below, depending on whether this solr instance should be
     the "master" or a "slave".  If this instance is a "slave" you will
     also need to fill in the masterUrl to point to a real machine.
  -->
     <lst name="slave">
       <str name="masterUrl">http://master.example.com:8983/solr/collection</str>
       <str name="pollInterval">08:00:00</str>
     </lst>
</requestHandler>

masterUrlにて、マスター側のSolrのコレクションへのURLを指定します。

pollIntervalは同期をキックする時間間隔です。

この場合、8時間ごとにマスターサイトに変更があるかチェックし、変更がある場合は同期が開始されます。


もし、マスター側のSolrにBasic認証をかけている場合は、上記に


<str name="httpBasicAuthUser">Username</str>
<str name="httpBasicAuthPassword">Password</str>

を追加します。

セキュリティのためのApache Solrアクセス制限あれこれ 」に書いたようにマスターとスレーブ側で接続を許可するIPアドレスを追加して互いに信頼関係を設定しておいた方がよいかもしれませんが。



マスターとスレーブのデータを同期する


設定が完了したら早速レプリケーションを実行してみます。

まずは、マスター側のSolr管理サイトから設定が正しくできているか確認してみます。

管理サイトへアクセスし、左メニューからコレクションを選択してReplicatonメニューを開きます。


SolrReplication-1


レプリケーションのタイミングや同期する設定ファイルなど、設定した内容が正しく表示されていれば問題ありません。

次に、スレーブ側の管理サイトを開きます(同じReplicationメニューです)


SolrReplication-2


スレーブ側では、設定内容だけでなくレプリケーションの状況なども表示されます。

レプリケーションは、スレーブ側で設定した同期の間隔(pollInterval)でチェックされ、マスター側に変更が加わっていた場合(で、replicateAfterの要件が満たされている)に同期されます。

同期の間隔は左上に「Next Run」としてカウントダウン表示されていますので、次の同期のタイミングもわかります。

すぐに同期したい場合は、その下の「Replicate now」ボタンをクリックすると同期がすぐに開始されます。

ただ、どうも指定の時間にレプリケーションを実行するという機能は無いみたいです。

ですので、例えばサービスに影響が少ない深夜3時にレプリケーションをしたいという場合は、その時間帯に管理サイトから手動でレプリケーションを実行するか、同期間隔をうまいこと3時に実行するように調整して、その後に実行間隔を24時間ごとに変更するといった工夫が必要そうです。


同期のステータスは一番上の「Iterations」の項目に表示され、展開するとレプリケーションを何時実行し、それが成功したのか失敗したのかがわかります。

または、その下にマスターとスレーブのVersionやGen、Sizeの項目の数字が一致していればデータも揃っているという確認になります。


初回の同期は、全てのデータが転送されるためそれなりに時間がかかりますが、次回以降は差分での更新になるため、かなり高速にデータが同期されます(もちろん、マスター側でデータの全消し・全投入のようなことをした場合は、レプリケーションに時間がかかることになってしまうでしょうけど)。


設定もかなり簡単に行えますし、ユーザーへのサービス提供とデータ更新の管理作業を分散できるので、バックアップの意味だけでなく、レプリケーション機能を活用した方がサービスを提供しやすいかと思います。