2013年9月18日水曜日

[debug][posix] sleep, usleepの知らないとはまる(かもしれない)恐ろしい仕様

POSIXのAPIであるsleep(), usleep()について、引数で指定した時間だけスリープしないで途中で起きてくることがある、という仕様はご存じでしょうか?
意外と知らずに使っている方が多いのでは、と思います。自分も知りませんでした。シグナルが発生すると引数で指定した時間を無視して関数を抜けてきます。

参考:


"The Open Group Base Specifications"のsleepのページにより詳しい説明が記載されていたので引用しておきます。
The sleep() function shall cause the calling thread to be suspended from execution until either the number of realtime seconds specified by the argument seconds has elapsed or a signal is delivered to the calling thread and its action is to invoke a signal-catching function or to terminate the process. The suspension time may be longer than requested due to the scheduling of other activity by the system.
If a SIGALRM signal is generated for the calling process during execution of sleep() and if the SIGALRM signal is being ignored or blocked from delivery, it is unspecified whether sleep() returns when the SIGALRM signal is scheduled. If the signal is being blocked, it is also unspecified whether it remains pending after sleep() returns or it is discarded.
If a SIGALRM signal is generated for the calling process during execution of sleep(), except as a result of a prior call to alarm(), and if the SIGALRM signal is not being ignored or blocked from delivery, it is unspecified whether that signal has any effect other than causing sleep() to return.
If a signal-catching function interrupts sleep() and examines or changes either the time a SIGALRM is scheduled to be generated, the action associated with the SIGALRM signal, or whether the SIGALRM signal is blocked from delivery, the results are unspecified.


この仕様を知ったきっかけですが、sqliteのsqlite3_busy_timeout()で3秒という時間を設定しているにもかかわらず、数百ミリ秒しかロックが解放されるのを待ってくれない、という不具合を追いかけていました。

このsleep()の仕様を知らず、バグの原因追及に思わぬ時間をとられました・・・。皆さんもsleep()の挙動がおかしい場合には、他のプロセス、スレッドから、シグナルが発射されていないかを疑ってみてください。


2013年9月14日土曜日

[sqlite] sqliteの内部テーブル

sqliteにはスキーマ、データベースファイル、テーブルのメタ情報を管理するための内部テーブルが用意されています。どんなテーブルが用意されていて、どのように活用できるかをまとめておきます。

テーブル一覧:
参考:"The SQLite Database File Format"の2.5 Storage Of The SQL Database Schema


sqlite_master

sqlite_masterテーブルは、データベースファイル毎に一つだけ生成されます。このテーブルの中には、該当データベースファイルの中に生成された、以下の4種類のオブジェクトの情報が格納される。
  • テーブル
  • インデックス
  • ビュー
  • トリガー

スキーマ:

CREATE TABLE sqlite_master(
  type text,
  name text,
  tbl_name text,
  rootpage integer,
  sql text
);
  • type
    • オブジェクトのタイプ。"table", "index", "trigger", "view"のいずれかが格納される。
  • name
    • オブジェクトの名前。typeが"table"もしくは"view"の場合はtbl_nameと一致する。
  • tbl_name
    • オブジェクトが所属するテーブルの名前。
  • rootpage
    • テーブル及びインデックスのroot b-tree pageの番号。ビュー、トリガー、バーチャルテーブルの場合は0もしくはNULLが格納される。
  • sql
    • オブジェクトを生成する際に実行されたSQL文(CREATE TABLE, CREATE INDEX, CREATE VIEW, CREATE TRIGGER等)が格納される。

このテーブルの利用例:

  SELECT name FROM sqlite_master
  WHERE type='table'
  ORDER BY name;
例えば上記のSQLを実行すると、DBファイル中のテーブルの一覧がテーブル名でソートした状態で出力される。


sqlite_temp_master

sqlite_temp_masterテーブルは、テンポラリデータベースにおいて、sqlite_masterテーブルの代わりに生成される。名前が異なる以外はsqlite_masterと仕様は同じ。


sqlite_sequence

sqlite_sequenceテーブルは、データベースファイル毎に一つだけ生成される。データベース内に存在しているテーブルのINTEGER PRIMARY KEYの最大値を保持する。この最大値はAUTOINCREMENTの動作で利用される。

スキーマ:
CREATE TABLE sqlite_sequence(name,seq);


sqlite_stat1

ANALYZEコマンドで生成される内部テーブル。テーブルやインデックスの補助的な情報が格納され、query plannerが効率的なクエリを見つけるために利用される。 アプリケーションからupdate, delete from, insert into, dropを実行できるが、createとalter tableは実行できない。

スキーマ:

CREATE TABLE sqlite_stat1(tbl,idx,stat);


sqlite_stat2

SQLiteのバージョンが3.6.18と3.7.8の間で、かつ、SQLITE_ENABLE_STAT2オプションでコンパイルされている場合にのみ利用される。インデックス中のキーの分散に関する補助的な情報が格納される。

スキーマ:

CREATE TABLE sqlite_stat2(tbl,idx,sampleno,sample);


sqlite_stat3

SQLiteのバージョンが3.7.9以降で、かつ、SQLITE_ENABLE_STAT3オプションでコンパイルされている場合にのみ生成・利用される。インデックス中のキーの分散に関する情報、および、query plannerがよりよいアルゴリズムを生成するための情報が格納される。

スキーマ:

CREATE TABLE sqlite_stat3(tbl,idx,nEq,nLt,nDLt,sample);