震災から22年。

去年の記事から先、何も書いてなかったとは……。
例年通り、非常用のやつのチェックをしておきましょっか。
最近はローリングストック法とゆうのが人気なようで。どうせ使う消耗品ならその方が手頃だからねぇ。更新忘れ対策にはGoogleのリマインダを使えばいい。今持ってるアルファ化米の期限切れ後はウチでもそうしよっかな。

PGroongaのインデックスを常に使う方法。

PGroongaJSON(B)に対しても全文検索できるのが便利ですが、レコード数が少ないとこんな感じで困ったことになる。
チュートリアルにあるサンプルスキーマとデータを投入した状態で、

test=# SELECT record FROM logs WHERE record @@ 'paths @ ".code" && number >= 200 && number < 300';
ERROR:  pgroonga: operator @@ is available only in index scans

レコード数が少ないので全件舐めたほうが速いからプランナがインデックスを使おうとせずにこうなる。

test=# explain SELECT record FROM logs WHERE record @@ 'paths @ ".code" && number >= 200 && number < 300';
                                   QUERY PLAN                                   
--------------------------------------------------------------------------------
 Seq Scan on logs  (cost=0.00..26.38 rows=655 width=32)
   Filter: (record @@ 'paths @ ".code" && number >= 200 && number < 300'::text)
(2 rows)

チュートリアルにあるとおり、

SET enable_seqscan = off;

すれば動くけど、本番でこれやるのはちょっとどうかと。かと言って、レコード数が増えてきてからPGroongaを使うようにアプリケーションを改修するっていうのも乗り気がしない。最初から@@演算子を使ったクエリを書きたい。


ってことでどうするか。要は常にインデックスを使うようにすればいいので、pg_hint_planを使う。
インストールした上で、クエリにヒント句を足してやるだけ。

test=# /*+ IndexScan(logs) */ SELECT record FROM logs WHERE record @@ 'paths @ ".code" && number >= 200 && number < 300';

record                                            
----------------------------------------------------------------------------------------------
 {"code": 200, "host": "www.example.com", "tags": ["web", "example.com"], "message": "GET /"}
(1 row)

これでレコードが1つしかなかろうが常にインデックスが使用され、@@演算子を使ったクエリが常に通るようになる。

BDへの安全なバックアップ方法。


というのを契機にしてバックアップ方法を再考してたんですが、堅牢なBD書き出し方法を考えたので書き留めます。

要求

  • メディアの劣化・破損の対策を講じる
  • メディアが第三者に渡った場合のデータ漏洩を防止する
  • 利便性のため、ディスクから直接ファイルを読み出せる
  • アクセスに必要なツールはすべてフリーソフトで構成されていること

構成

上記の要求を踏まえ、このような構成でディスクを作成することにしました。

  1. ファイルをEXT4フォーマットのイメージに格納(a)
  2. イメージはLUKSにて暗号化
  3. イメージをBDに書き出す際、エラー訂正情報を付与する

依存

  • cryptsetup
  • dvdisaster
  • mdadm

作成手順

EXT4ディスクイメージの作成
cd /tmp
dd if=/dev/zero bs=1M count=3500 of=disk.img.0
dd if=/dev/zero bs=1M count=3500 of=disk.img.1
dd if=/dev/zero bs=1M count=3500 of=disk.img.2
dd if=/dev/zero bs=1M count=3500 of=disk.img.3
dd if=/dev/zero bs=1M count=3500 of=disk.img.4

sudo losetup /dev/loop0 disk.img.0
sudo losetup /dev/loop1 disk.img.1
sudo losetup /dev/loop2 disk.img.2
sudo losetup /dev/loop3 disk.img.3
sudo losetup /dev/loop4 disk.img.4

sudo mdadm --create /dev/md/md0 -l linear -n 5 /dev/loop0 /dev/loop1 /dev/loop2 /dev/loop3 /dev/loop4
sudo cryptsetup luksFormat /dev/md/md0
sudo cryptsetup luksOpen /dev/md/md0 disk
sudo mkfs.ext4 /dev/mapper/disk
mkdir mount
sudo mount /dev/mapper/disk mount
sudo chown USERNAME mount

単一のrawイメージではなく複数のイメージをRAID0で束ねているのは、ISO9660フォーマットの仕様上4GB以上のファイルを格納できないため。単一イメージにしてしまうとファイル分割の必要がある。

ディスクイメージにファイルを投入

/tmp/mountに書き出したいファイルをコピーする

ISOイメージを作成
sudo umount mount
sudo cryptsetup luksClose disk
sudo mdadm --stop /dev/md/md0
sudo losetup -d /dev/loop0
sudo losetup -d /dev/loop1
sudo losetup -d /dev/loop2
sudo losetup -d /dev/loop3
sudo losetup -d /dev/loop4
mkisofs -L -R -o disk.iso -V DISKNAME disk.img.*
dvdisaster -c -i disk.iso -mRS02 -n BD

上記のパラメータだと、30%ほどの冗長性が確保できる。あとは作成したISOイメージをBD-Rに書き出せばよい。

マウント方法
sudo losetup /dev/loop0 /path/to/disk.img.0
sudo losetup /dev/loop1 /path/to/disk.img.1
sudo losetup /dev/loop1 /path/to/disk.img.2
sudo losetup /dev/loop1 /path/to/disk.img.3
sudo losetup /dev/loop1 /path/to/disk.img.4
sudo mdadm --assemble --force /dev/md/md0 /dev/loop0 /dev/loop1 /dev/loop2 /dev/loop3 /dev/loop4
sudo cryptsetup luksOpen /dev/md/md0 disk
mkdir /tmp/mount
sudo mount /dev/mapper/disk /tmp/mount

PGroonga on Travis CI

2015年2月13日追記: 公式な使い方が発表されました。以下の手順は用済みです。

今回はTravis CIを使うので、無理矢理ですがPGroongaを使えるようにしてみました。
最初は.travis.ymlのbefore_installでpgroongaのドキュメントにあるようにソースからのインストールをやろうとしたんですが、makeに必要なpostgresql-server-dev-9.4をaddonsに書いて入れてやろうとしても、
Disallowing packages: postgresql-server-dev-9.4
と言われてmake出来ないので、checkinstallで.debを作ってやることにしました。


Travis CIのワーカはUbuntu 12.04 LTS 64bitなので、同じ環境でVMを作り、それにPostgreSQL 9.4をインストール。その上でPGroongaをソースからインストールする手順を流し、

$ sudo make install

の代わりに

$ sudo checkinstall

をやってpgroonga_0.3.0-1_amd64.debを作ります。
作ったら、それを適当な場所に置き、.travis.ymlのbefore_installに

  - wget http://path/to/pgroonga_0.3.0-1_amd64.deb
  - sudo dpkg -i pgroonga_0.3.0-1_amd64.deb

と記述してやります。これでPGroongaのインストールができるので、あとはbefore_scriptで

  - psql -d データベース名 --command 'CREATE EXTENSION pgroonga;' -U postgres

と書いてやれば準備完了。あとはマイグレーション定義中でusing pgroongaでcreate indexしてやれば使えるはずです。

PGroongaを使うための調査。

こないだリニューアルした読書びよりでは全文検索エンジンGroongaを使用していて、Rroonga gemを使って全文検索用の処理を実装してあります。ただ、この実装だとPostgresに格納されている商品情報と、Groongaが使う検索用のインデックスが別管理になるので扱いが煩雑なんですよね。そこで、現在開発中の次期カケラの樹 "Sone"ではPGroongaを使おうかなと考えてます。これならActiveRecordから%%演算子を使ったクエリを投げてやれば全文検索できて、実装も管理も簡単になりそうなので。
使うにあたって、レプリケーションに非対応という点が検証必要だったので、試してみました。

前提

  • PostgreSQLで、streaming replicationを使ってアクティブ/スタンバイ構成を組む
  • 障害発生時、インデックスの再生成が必要なのは許容する

小さな個人サービスで、アップタイムを追わないぬるい環境なのでこの程度で大丈夫です。

結果

active側
test=# select * from memos;
-[ RECORD 1 ]----------------------------------------------------------------------
id      | 1
content | PostgreSQLはリレーショナル・データベース管理システムです。
-[ RECORD 2 ]----------------------------------------------------------------------
id      | 2
content | Groongaは日本語対応の高速な全文検索エンジンです。
-[ RECORD 3 ]----------------------------------------------------------------------
id      | 3
content | PGroongaはインデックスとしてGroongaを使うためのPostgreSQLの拡張機能です。

test=# explain analyze SELECT * FROM memos WHERE content %% '全文検索';
-[ RECORD 1 ]-------------------------------------------------------------------------------------------------------------------------------
QUERY PLAN | Bitmap Heap Scan on memos  (cost=8.91..26.60 rows=615 width=36) (actual time=1.332..1.333 rows=1 loops=1)
-[ RECORD 2 ]-------------------------------------------------------------------------------------------------------------------------------
QUERY PLAN |   Recheck Cond: (content %% '全文検索'::text)
-[ RECORD 3 ]-------------------------------------------------------------------------------------------------------------------------------
QUERY PLAN |   Heap Blocks: exact=1
-[ RECORD 4 ]-------------------------------------------------------------------------------------------------------------------------------
QUERY PLAN |   ->  Bitmap Index Scan on pgroonga_content_index  (cost=0.00..8.76 rows=615 width=0) (actual time=1.318..1.318 rows=1 loops=1)
-[ RECORD 5 ]-------------------------------------------------------------------------------------------------------------------------------
QUERY PLAN |         Index Cond: (content %% '全文検索'::text)
-[ RECORD 6 ]-------------------------------------------------------------------------------------------------------------------------------
QUERY PLAN | Planning time: 0.428 ms
-[ RECORD 7 ]-------------------------------------------------------------------------------------------------------------------------------
QUERY PLAN | Execution time: 1.740 ms

test=# SELECT * FROM memos WHERE content %% '全文検索';
-[ RECORD 1 ]----------------------------------------------
id      | 2
content | Groongaは日本語対応の高速な全文検索エンジンです。

当然ながら、全文検索可能。

standby側
test=# select * from memos;
-[ RECORD 1 ]----------------------------------------------------------------------
id      | 1
content | PostgreSQLはリレーショナル・データベース管理システムです。
-[ RECORD 2 ]----------------------------------------------------------------------
id      | 2
content | Groongaは日本語対応の高速な全文検索エンジンです。
-[ RECORD 3 ]----------------------------------------------------------------------
id      | 3
content | PGroongaはインデックスとしてGroongaを使うためのPostgreSQLの拡張機能です。

test=# explain analyze SELECT * FROM memos WHERE content %% '全文検索';
ERROR:  pgroonga: object isn't found: <IDs16537>

test=# SELECT * FROM memos WHERE content %% '全文検索';
ERROR:  pgroonga: object isn't found: <IDs16537>

レコード自体はレプリケートされている。全文検索は出来ない。

pg_ctl promote後のstandby
test=# SELECT * FROM memos WHERE content %% '全文検索';
ERROR:  pgroonga: object isn't found: <IDs16537>

test=# drop index pgroonga_content_index;
DROP INDEX

test=# CREATE INDEX pgroonga_content_index ON memos USING pgroonga (content);
CREATE INDEX

test=# explain analyze SELECT * FROM memos WHERE content %% '全文検索';
-[ RECORD 1 ]-----------------------------------------------------------------------------------------------------------------------------
QUERY PLAN | Index Scan using pgroonga_content_index on memos  (cost=0.13..8.17 rows=2 width=36) (actual time=0.390..0.391 rows=1 loops=1)
-[ RECORD 2 ]-----------------------------------------------------------------------------------------------------------------------------
QUERY PLAN |   Index Cond: (content %% '全文検索'::text)
-[ RECORD 3 ]-----------------------------------------------------------------------------------------------------------------------------
QUERY PLAN | Planning time: 0.175 ms
-[ RECORD 4 ]-----------------------------------------------------------------------------------------------------------------------------
QUERY PLAN | Execution time: 0.546 ms

test=# SELECT * FROM memos WHERE content %% '全文検索';
-[ RECORD 1 ]----------------------------------------------
id      | 2
content | Groongaは日本語対応の高速な全文検索エンジンです。

障害発生時にstandbyを昇格させ、インデックスを張り直せば全文検索が可能になる。


ウチの環境では問題なさそうなので、使ってみることにします。