sqlite on nfs

nfssqliteで、is lockedとなりました。

調査中です。

SQLite3 bindings for CleanをLinuxで動かそうとしているのですが、そのまえに、SQLite3そのものが動作しない。

「database is locked」というメッセージを表示して、何の処理も受け付けてくれない。

      • -

ファイルシステムNFSであることが何か関係していそうな気がする。

      • -

ビンゴっぽい。

同じ現象だ。

今動かしてるサービスをSolaris上のSQLiteで組んでるんだけど、DBはNFS上に置いてても普通に動いてた。
で、SolarisLinuxにリプレースすることになったので、LinuxからNFS上のSQLiteファイルアクセスしてみたら、データベースが全く開けない。
いろいろ調べてみると、別にDBD::SQLiteのインストールに失敗したというわけではなさそうで、ローカルディスク上のSQLiteファイルならいくらでもアクセスできる。
なもんでNFS上に置いてるのが原因かと思って調べてみたら、案の定だった。

Solarissqlite使ったシステム。
nfs上にデータベース置いてても動作してた。
システムをLinuxにリプレース。
nfs上のデータベースにアクセスしたら、全く開けない。
ローカルディスクのデータベースなら問題なく動作する。
nfs上のデータベースが問題。

NFSパーティションにマウントされたSQLiteデータベースを処理すること は推奨されません。ロックに関してNFSは著しい問題があるので、 データベースを全くオープンすることさえできない可能性があります。ま た、成功した場合でも、ロックに関する動作は予測できない結果を生む可 能性があります。

nfsのロックには問題あり。
データベースのオープンに成功しても、動作は保証できない。

上記にあるように、複数のプロセスで同時に1つのデータベース(ファイル)を操作することは可能とのこと。但し、データベースを変更できるプロセスは瞬間的には1つだけ、という制限があるようです。しかも、NFSのような仕組みを介すると一貫性が保たれない。

sqliteにはロック機構あり。
複数プロセスから同時アクセスも可能。
nfs上のデータベースについては、一貫性が保たれない。

解決の糸口が

> SQLiteNFS上に置けない
> 情報としてはこの辺。

より正確に言うと「ファイルのロックができない場所には置けない」ですね。
SQLite を普通に使うと、同じファイルに対して各クライアントがそれぞれ直接読み書きすることになるので、ファイルのロックができない環境では排他制御ができないので使えません。
以前は「NFS ではロックはできない」のが常識だったので、NFS 上ではそもそも無理でした。
今時は NFS でもロックができる実装が増えていますが、ロックが信用できない実装もあるので、その情報のようなことになっています。

逆に言うと、きちんとファイルのロックができるシステムであれば、NFS 上に SQLite のデータベースを置いても問題ありません。
(きちんとロックができるかどうかは、NFS サーバ/クライアントの実装依存です。LinuxSolaris の実装がきちんとしているのかどうかは、私は知りません ← じゃあ役に立たないじゃねーか)

> LinuxからNFS上のSQLiteファイルアクセスしてみたら、データベースが全く開けない。

どのような Linux の環境でしょうか?
NFS サーバ/クライアント共に、Vine Linux 4.1, カーネル 2.6.16-0vl76.3, nfs-utils-1.0.9-0vl3, DBD-SQLite-1.13 の環境で rpc.statd (init スクリプトの名前は nfslock) が動いている状態であれば、NFS 経由で SQLite の読み書きができました。
# 正しく排他制御できるのかどうかまでは不明。
(rpc.statd が動いていない状態の場合、マウントしてファイルの読み書きはできますが、ロックができないため SQLite は使えないので注意。)

あと、SolarisLinuxNFS のロックの違いとしては、Solaris ではサーバ側だけのデーモンを使ってロックを管理していたような気がしますが、Linux の rpc.statd はサーバ/クライアント双方で動いている必要があるようです。

ロックについて

nfsというよりも、ファイルのロックができない場所には置けない。
sqliteは複数のプロセスが同じファイル(データベース)にアクセスする。
ファイルのロックができないと、排他制御ができないので使えない。


以前はNFSではロックできないのが常識。
なので、sqlitenfsでは使えないということに。
今では、nfsでもロックは可能。
ロックの挙動が怪しい場合もあるので、
nfsではsqliteは使えないということに。

rpc.statd

nfs-utils
rpc.statd
nfslock


rpc.statdが動いてない場合はロックができないのでsqliteは使えない


solarisはサーバ側のデーモンを使ってロックを管理
linuxはサーバ/クライアント両方動いている必要あり

ローカルのcolinuxを2台立てて実験してみる

ここを参考に

サーバの設定は 2 つのステップからなります。 まず NFS の設定ファイルを編集し、 次に NFS サービスを実際に起動します。

centos5をサーバに

設定を書いた

# cat /etc/exports
/home 192.168.52.9(rw)

nfs-utilsパッケージをインストール

# yum install nfs-utils
Loading "fastestmirror" plugin
Loading "installonlyn" plugin
Setting up Install Process
Setting up repositories
extras                    100% |=========================| 1.1 kB    00:00
updates                   100% |=========================|  951 B    00:00
base                      100% |=========================| 1.1 kB    00:00
addons                    100% |=========================|  951 B    00:00
Loading mirror speeds from cached hostfile
Reading repository metadata in from local files
Parsing package install arguments
Resolving Dependencies
--> Populating transaction set with selected packages. Please wait.
---> Downloading header for nfs-utils to pack into transaction set.
nfs-utils-1.0.9-40.el5.i3 100% |=========================|  35 kB    00:00
---> Package nfs-utils.i386 1:1.0.9-40.el5 set to be updated
--> Running transaction check
--> Processing Dependency: libgssapi.so.2 for package: nfs-utils
--> Processing Dependency: libnfsidmap.so.0 for package: nfs-utils
--> Processing Dependency: libgssapi for package: nfs-utils
--> Processing Dependency: libgssapi.so.2(libgssapi_CITI_2) for package: nfs-utils
--> Processing Dependency: librpcsecgss.so.2 for package: nfs-utils
--> Processing Dependency: nfs-utils-lib >= 1.0.8-2 for package: nfs-utils
--> Restarting Dependency Resolution with new changes.
--> Populating transaction set with selected packages. Please wait.
---> Downloading header for libgssapi to pack into transaction set.
libgssapi-0.10-2.i386.rpm 100% |=========================| 4.1 kB    00:00
---> Package libgssapi.i386 0:0.10-2 set to be updated
---> Downloading header for nfs-utils-lib to pack into transaction set.
nfs-utils-lib-1.0.8-7.2.z 100% |=========================| 5.5 kB    00:00
---> Package nfs-utils-lib.i386 0:1.0.8-7.2.z2 set to be updated
--> Running transaction check

Dependencies Resolved

=============================================================================
 Package                 Arch       Version          Repository        Size
=============================================================================
Installing:
 nfs-utils               i386       1:1.0.9-40.el5   base              379 k
Installing for dependencies:
 libgssapi               i386       0.10-2           base               22 k
 nfs-utils-lib           i386       1.0.8-7.2.z2     base               55 k

Transaction Summary
=============================================================================
Install      3 Package(s)
Update       0 Package(s)
Remove       0 Package(s)

Total download size: 456 k
Is this ok [y/N]: y
Downloading Packages:
(1/3): libgssapi-0.10-2.i 100% |=========================|  22 kB    00:00
(2/3): nfs-utils-1.0.9-40 100% |=========================| 379 kB    00:00
(3/3): nfs-utils-lib-1.0. 100% |=========================|  55 kB    00:00
Running Transaction Test
Finished Transaction Test
Transaction Test Succeeded
Running Transaction
  Installing: libgssapi                    ######################### [1/3]
  Installing: nfs-utils-lib                ######################### [2/3]
  Installing: nfs-utils                    ######################### [3/3]

Installed: nfs-utils.i386 1:1.0.9-40.el5
Dependency Installed: libgssapi.i386 0:0.10-2 nfs-utils-lib.i386 0:1.0.8-7.2.z2
Complete!


nfsサーバの設定はここを参考に
http://www.server-world.info/note?os=ce5&p=nfs

portmapを起動してみる

# /etc/init.d/portmap start
portmap を起動中:                                          [  OK  ]

# /usr/sbin/rpcinfo -p
   プログラム バージョン プロトコル ポート
    100000    2   tcp    111  portmapper
    100000    2   udp    111  portmapper

nfsを起動してみます

# /etc/init.d/nfs start
NFS サービスを起動中:                                      [  OK  ]
NFS デーモンを起動中:                                      [  OK  ]
NFS mountd を起動中:                                       [  OK  ]
RPC idmapd を起動中:                                       [  OK  ]

# /usr/sbin/rpcinfo -p
   プログラム バージョン プロトコル ポート
    100000    2   tcp    111  portmapper
    100000    2   udp    111  portmapper
    100021    1   udp  32775  nlockmgr
    100021    3   udp  32775  nlockmgr
    100021    4   udp  32775  nlockmgr
    100003    2   udp   2049  nfs
    100003    3   udp   2049  nfs
    100003    4   udp   2049  nfs
    100021    1   tcp  40066  nlockmgr
    100021    3   tcp  40066  nlockmgr
    100021    4   tcp  40066  nlockmgr
    100003    2   tcp   2049  nfs
    100003    3   tcp   2049  nfs
    100003    4   tcp   2049  nfs
    100005    1   udp    989  mountd
    100005    1   tcp    992  mountd
    100005    2   udp    989  mountd
    100005    2   tcp    992  mountd
    100005    3   udp    989  mountd
    100005    3   tcp    992  mountd

# ps aux | grep nfs
root      6736  0.0  0.0      0     0 ?        S<   20:06   0:00 [nfsd4]
root      6739  0.0  0.0      0     0 ?        S    20:06   0:00 [nfsd]
root      6740  0.0  0.0      0     0 ?        S    20:06   0:00 [nfsd]
root      6741  0.0  0.0      0     0 ?        S    20:06   0:00 [nfsd]
root      6742  0.0  0.0      0     0 ?        S    20:06   0:00 [nfsd]
root      6743  0.0  0.0      0     0 ?        S    20:06   0:00 [nfsd]
root      6744  0.0  0.0      0     0 ?        S    20:06   0:00 [nfsd]
root      6745  0.0  0.0      0     0 ?        S    20:06   0:00 [nfsd]
root      6746  0.0  0.0      0     0 ?        S    20:06   0:00 [nfsd]
root      6776  0.0  0.5   4960   740 pts/0    R+   20:07   0:00 grep nfs

nfslockを起動してみます

# /etc/init.d/nfslock start
NFS statd を起動中:                                        [  OK  ]
debianをクライアントに
# cat /etc/debian_version
4.0


ここを参考に

4. NFS クライアントの設定
4.1. リモートのディレクトリをマウントする


クライアントからつないでみます

# mkdir /mnt/home

# mount 192.168.52.12:/home /mnt/home
mount: 192.168.52.12:/home: can't read superblock


エラーが表示されてつながりません。

NFSでは、サーバ側にもクライアント側にも、portmapが必須な模様。今回は、そのportmapがクライアントで動いていなかった(インストールされていなかった)という話。

クライアント側にもportmapが必要だそうで。


debianにportmapをインストールします。

# apt-cache search portmap
portmap - The RPC portmapper

# apt-get install portmap
パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています... 完了
以下のパッケージが新たにインストールされます:
  portmap
アップグレード: 0 個、新規インストール: 1 個、削除: 0 個、保留: 80 個。
35.5kB のアーカイブを取得する必要があります。
展開後に追加で 156kB のディスク容量が消費されます。
取得:1 http://ftp2.jp.debian.org etch/main portmap 5-26 [35.5kB]
35.5kB を 0s で取得しました (51.5kB/s)
Illegal character in prototype for bytes::length : _ at /home/hoge/perl/lib/5.10.0/bytes.pm line 22.
Illegal character in prototype for bytes::chr : _ at /home/hoge/perl/lib/5.10.0/bytes.pm line 23.
Illegal character in prototype for bytes::ord : _ at /home/hoge/perl/lib/5.10.0/bytes.pm line 24.
パッケージを事前設定しています ...
未選択パッケージ portmap を選択しています。
(データベースを読み込んでいます ... 現在 26840 個のファイルとディレクトリがインストールされています。)
(.../archives/portmap_5-26_i386.deb から) portmap を展開しています...
portmap (5-26) を設定しています ...
Illegal character in prototype for bytes::length : _ at /home/hoge/perl/lib/5.10.0/bytes.pm line 22.
Illegal character in prototype for bytes::chr : _ at /home/hoge/perl/lib/5.10.0/bytes.pm line 23.
Illegal character in prototype for bytes::ord : _ at /home/hoge/perl/lib/5.10.0/bytes.pm line 24.
Starting portmap daemon....

あらためてmountしてみます

# mount 192.168.52.12:/home /mnt/home

# df -h
Filesystem          サイズ  使用  残り 使用% マウント位置
/dev/cobd0           1007M  122M  835M  13% /
tmpfs                  62M     0   62M   0% /lib/init/rw
udev                   10M   28K   10M   1% /dev
tmpfs                  62M     0   62M   0% /dev/shm
/dev/cobd4           1008M  426M  532M  45% /var
/dev/cobd5           1008M  722M  235M  76% /usr
/dev/cobd6            3.0G  1.9G  982M  66% /home
192.168.52.12:/home   2.0G  165M  1.8G   9% /mnt/home

あっさりmountできました。