2019年2月26日火曜日

node.jsでインストールしたモジュール(knex、mssql)のバージョンによる問題


オフィス狛 技術部のHammarです。

今回はnode.jsのpackage.jsonに記述したモジュールのインストールバージョンによってハマった事象を書こうと思います。 

環境構築時にDBのテーブルを作成したり、テストデータを登録したりすると思いますが、今回そのテーブル作成(migrate)とテストデータ登録(seed)作業をnode.jsとknex.jsというSQL Query Builderのモジュールで実行させます。
また開発の都合上DBはSQL Serverを利用するので、SQLServerに接続するmssqlというモジュールも利用します。 

■実装環境

・node.js(Express)
・knex、mssqlモジュールを利用 

■npmインストール

まずそれぞれのモジュールの最新バージョン(knex:0.16.3 、mssql:4.3.0)をインストールして使おうとしましたが、そもそもknexとmssqlの最新バージョン互換対応がしておらず、怒られます。
Error: This knex version does not support any other mssql version except 4.1.0 (knex patches bug in its implementation)
このバージョン(knex:0.15.2 、mssql:4.1.0)が最終的に対応しているとのことで、バージョンを指定しインストールします。
 

■テーブル作成(migrate)、テストデータ登録(seed)

次にテーブル作成(migrate)とテストデータ登録(seed)の処理を作成します。
テーブル作成(migrate)ではknexをつかったテーブルcreateを、テーブル毎のファイル別に作成し、その各ファイルを読み込んで実行するというような基本的な流れでテーブルを作成します。
データ登録(seed)も基本的には同じで、insert文が書かれた各ファイルを読み込む感じです。 

このmigrate処理を下記のような感じで記述して実行したのですが
// テーブルcreateファイルの読み込み
const createTableA= require('./createTableA');
const createTableB= require('./createTableB');
const createTableC= require('./createTableC');
const migrationOrder = [
  createTableA,
  createTableB,
  createTableC,
];

// 実行
exports.up = async knex => {
  const results = migrationOrder.map(migration => migration.up(knex));
  return Promise.all(results);
};

 なぜか「Can't acquire connection for the request. There is another request in progress.」というエラーがSQLServerから返ってきてしまいテーブルが作成できませんでした。
記述的には特におかしなところはなさそうなので、なぜうまくいかないのかかなりハマりました。
いろいろ調べていくとmssqlのバージョン4系では単一のトランザクション内で複数のクエリ実行はエラーとなるバグ?のようらしくこのときpromise.allは機能しないようです。
https://github.com/tediousjs/node-mssql/issues/491
https://github.com/Vincit/objection.js/issues/671 

■回避方法

1.mssql、knexのバージョンを下げる

mssqlのバージョンを単純に下げると今度はknexのバージョン互換の関係でつかえなくなるので、
  knex: 0.13.0
  mssql: 3.3.0
が現在の記述でうまくいくバージョンの組み合わせとなるようです。

ただ、やはり最新ではないですが新しいバージョンで開発したほうが今後のためにも良いとはおもうので、できるだけバージョンは下げたくないなーという思いで、個人的にはこの回避方法はやめました。

2.for文を使う

GitHubにも対応策として書かれていた下記のように記述しました。
exports.up = async knex => {
  const results = [];
  for (const migration of migrationOrder) {
    results.push(await migration.up(knex));
  }
  return results;
};

promise.allをつかわずに上記のようにfor文でテーブル作成を行うとうまくいきました。
ただ自分の開発環境ではこれにも1つ問題点がありました。 

ESlintをインストールしていて、for文の記述がこのESlintのチェックにひっかかってしまいました。
error   iterators/generators require regenerator-runtime, which is too heavyweight for this guide to allow them. Separately, loops should be avoided in favor of array iterations no-restricted-syntax

現在の開発環境ではgitのcommit時にESlintチェックしていて、もしチェックに引っかかる場合はcommitできないようにしているのでこのままではcommitできません。
うーんこれは困った。。。
このチェックだけESlintから外したりすることも可能ですが、それだと今後開発のときのチェックとしてはすべて外れてしまうしー、
ということで、一旦ここの記述だけをチェックしないようにする、というちょっと強引な感じでひとまず切り抜けることにしました。

記述はこんな感じです。
exports.up = async knex => {
/* eslint-disable */
  const results = [];
  for (const migration of migrationOrder) {
    results.push(await migration.up(knex));
  }
  return results;
/* eslint-enable */
};

れでこの記述だけはチェック除外となるので、ひとまずは切り抜けられました。
データ登録(seed)も同じようなながれで記述することで回避できます。

結局やってみるとちょっと強引な形なので、回避方法1にしてもいい気もしますが、ご参考までに。

ということで、まだ他にもこのようなモジュールのバージョンでなにか事象があるかと思いますが、ハマり次第記載していけたらと思います。

, ,

0 件のコメント:

コメントを投稿