技術ネタはQiitaに移りました。壁もどこぞに。

DBUnit をそのまま使うと ORACLE 相手のテストが遅い

表題通りである。ORACLE データベースを相手に DBUnit を使用すると、とにかく遅い。

ただ、これには理由があり、解消方法も存在するが、これに気がつくまでかなり時間をかけてしまったので、残しておくことにする。

まず解消方法から。以下のように、テストデータを挿入するテーブルのスキーマを指定するだけで良い。(コードは Spring Boot で使用している場合の Bean 定義より抜粋)

// 中略

  @Bean
  @Autowired
  public DataSourceDatabaseTester tester(DataSource dataSource,
      @Value("${spring.datasource.schema}") String schema) throws Exception {

    DataSourceDatabaseTester tester = new DataSourceDatabaseTester(dataSource);

    tester.setSchema(schema);
    tester.setSetUpOperation(DatabaseOperation.CLEAN_INSERT);
    return tester;
  }

// 中略

これ、何が起きているかを説明するには、はじめに DBUnit がテストデータの挿入前に、何をしているかを知る必要がある。

  1. まず、DBUnit はテストデータを挿入する前に、与えられたテストデータを読み取る。ここまでは誰でも予想がつく動作だろう。
  2. つぎに、DBUnit はテストデータから得られた、ターゲットテーブル、ターゲットカラムが、対象 DB 内に存在するかどうかを確認する。
  3. このとき、対象 DB 内に該当するメタデータが存在しない場合、DBUnit は例外をスローして終了する。
  4. ここまでのチェックが完了して、はじめて SetUpOperation で指定された通りのテストデータ処理に入る。

以上が DBUnit のおこなう、テストデータに関する処理の流れなのだが、(3) で挙げた、対象 DB メタデータと、テストデータの照合の際に問題が生じるため、ORACLE 相手のテストで著しい遅延が発生する。(環境にもよるが手持ちの環境では、メタデータ取得だけで、10~20秒程度の待機が発生していた。)

問題とはどのようなものかというと、以下のようなものになる。

ORACLE データベースの JDBC ドライバでは、メタデータの取得時に、all_objects というシステムテーブルに問い合わせをかける。このテーブルではログインしているユーザーがアクセス可能な、あらゆるオブジェクトの情報が返されるのだが、ここでスキーマ指定がないと、本来テスト対象とはならないスキーマも含めた問い合わせがかけられ、これが遅延につながってしまう。

all_objects で返されるメタデータの中には、アクセス可能なだけで、ログイン中のユーザーでは操作自体は不可能な情報も多分に含まれているため、テストデータを用意するというオペレーションに対しては過大な情報といえる。そこで、ログインユーザーと同様のスキーマ情報を指定してやることで、ドライバ内で組み立てられるSQL で、all_objects に対する WHERE 条件が追加され、遅延は大きく改善されるようになる。(遅延改善後の待機時間は、0~2秒程度。)

ORACLE 相手に DBUnit を使用したいと考えたとき、気になる遅延が発生した場合には、試してみてほしい。

また、参考までにこれらの挙動の詳細を確認したい場合は、JAD などを使用して oracle.jdbc.OracleDatabaseMetaData の内容を確認してみるのも良いかと思う。