読者です 読者をやめる 読者になる 読者になる

はやさがたりない。

へっぽこぷろぐらまのメモログ

Amazon SQSのテストでelasticMQを使おうとしてちょっとハマった話

最近はもっぱら AWS を使ったサービスの開発をしている。
あいかわらず、サーバサイド(Java)の開発+ちょっとインフラも。

さてさて、今回は Amazon SQS を使うことになった。
SQS周りの単体テストをしょうということで elasticMQ っていう子を使うことになりました。

adamw/elasticmq · GitHub

今回はそのelasticMQの使い方でちょっとハマっている話。

elasticMQはAmazonSQS互換のインターフェースを持っている子で起動の仕方は大きく2種類ある。
Readmeにも書いてあるけども

  • プロセスとして起動する方法
    java -jar elasticmq-server-0.8.8.jar

  • JUnitとかのテスト内で起動する方法

val server = SQSRestServerBuilder.start()
// ... use ...
server.stopAndWait()

ローカル環境で開発するときにはプロセスとして起動しながらサーブレット起動して動作確認したり。
JUnitでの単体テストのときはテスト内で起動したり。そんな感じで使ってる。

で、CI環境では(ちょっとテストがいけていないっていうのもあるけども)
プロセスが起動してあることが前提で動くAPIのRESTテストと
テスト内で起動して動作確認をする単体テスト両方が動く。

この時にテストの中でキューのメッセージを消しているはずなのに消せない!って問題が起きた。
というかそもそもキューがない!って言われる。

流れはこんな感じ。
maven testを流すと下の流れでテストが動く。

  1. RESTのテスト
    1. APIのテストを実施(この中でプロセスElasticMQにメッセージを発行)
  2. 単体テスト
    1. テスト内でElasticMQ起動
    2. ElasticMQに接続してキューを削除(しているつもりだった)
    3. テストで起動したElasticMQにメッセージを発行
    4. メッッセージの確認(APIテストのメッセージが残ってる!!)

ちゃんとした原因は、まだわかっていないんだけども

$ netstat -an | grep 9324

tcp46      0      0  *.9324                 *.*                    LISTEN     <---- プロセスで起動したElasticMQ
tcp4       0      0  127.0.0.1.9324         *.*                    LISTEN     <----テスト内で起動したElasticMQ

このように起動したプロセスが同じポート使って別ヤツもので動くっぽい。
ポートってぶつかってたらダメなやつだと思っていたけども
「tcp46」と「tcp4」だったら別ヤツでうごけるの?
それともアドレスが「*」と「127.0.0.1」 だから?<--多分こっち (ここわかってないのでインフラ先生に教えを乞おう)

キューへのアクセッサーはシングルトンなので接続状態とかテストをどうやって実行するかも関係してて

  1. RESTのテスト
    1. APIのテストを実施(この中でプロセスElasticMQにメッセージを発行)
      (ここで「*.9324」へのコネクションを確立している)
  2. 単体テスト
    1. テスト内でElasticMQ起動
    2. ElasticMQに接続してキューを削除(しているつもりだった)
      (ここは「127.0.0.1.9324」へ接続、消しているつもりになっていた)
    3. テストで起動したElasticMQにメッセージを発行
      (こっちは「*.9324」に接続している)
    4. メッセージの確認(APIテストのメッセージが残ってる!!)
      (こっちは「*.9324」に接続している)

ということで失敗してしまっていた様子。

今回の対処方法は2つ?

1つ目

  • プロセス起動側の設定ファイルを指定する(elasticMQのReadme 参照)
    bind-hostname = "127.0.0.1"
  • テスト内の起動時にすでにプロセス側が起動していたら(java.lang.RuntimeException: Binding failed.例外が発生したら)スルーする。

2つ目

  • テスト内の起動時にインターフェースを「"0.0.0.0"」で設定して、 すでにプロセス側が起動していたら(java.lang.RuntimeException: Binding failed.例外が発生したら)スルーする。
SQSRestServerBuilder.withInterface("0.0.0.0").start();

1つ目のやり方だと他の人にも影響がでてしまうので2つ目のやり方で試してみようと思う。