尋常でないもふもふ

a software engineer blog

ioredisで謎のエラー

ioredis で謎のエラーがでてハマった。

[ioredis] Unhandled error event: ParserError: Protocol error, got "J" as reply type byte. Please report this.
    at handleError (/Users/jnst/go/src/github.com/Translimit/park-server/node_modules/redis-parser/lib/parser.js:190:15)
    at parseType (/Users/jnst/go/src/github.com/Translimit/park-server/node_modules/redis-parser/lib/parser.js:304:14)

エラーがでないコード

空のコンストラクタを使うとデフォルトで 127.0.0.1 に接続する。

import * as Redis from 'ioredis';

const redis = new Redis();

エラーがでるコード

hostport を指定するとエラー。

import * as Redis from 'ioredis';

const redis = new Redis(3306, '127.0.0.1');

原因

Redis の 6379 番ポートではなく、MySQL の 3306 番ポートにアクセスしてた。
このエラーだけならすぐ気づいたかもだけど、別のエラーとの複合技だったので解決に時間がかかってしまった。疲れてるようだ…。

TypeScriptとioredisのコードをJestでテストしたらコンストラクタじゃないと言われた件

TypeScript と ioredis のソースコード

TypeScript で Redis 使いたい場合は ioredis で書いてこんな感じのコードになる。

sample.ts

import * as Redis from 'ioredis';

(async () => {
  const redis = new Redis();
  const pong = await redis.ping(); // => PONG
})();

Jest のテストコード

ところが Jest でテスト書いたら実行できなくてハマってしまった。

sample.spec.ts

import * as Redis from 'ioredis';

describe('Redis', () => {

  it('should be return pong', async () => {
    const redis = new Redis();
    const pong = await redis.ping();
    redis.disconnect();
    expect(pong).toBe('PONG');
  })

});

.ts ファイルを直接実行するので ts-jest を入れている。

$ jest
    TypeError: Redis is not a constructor

      4 |
      5 |   it('should be return pong', async () => {
    > 6 |     const redis = new Redis();
        |                   ^
      7 |     const pong = await redis.ping();
      8 |     redis.disconnect();
      9 |     expect(pong).toBe('PONG');

      at Object.it (src/sample.spec.ts:6:19)

TypeError: Redis is not a constructor

なんでやねん

import * as Redis from 'ioredis';
const redis = new Redis();

のところでコンストラクタじゃないと言われる。書き方は正しいのに。

import Redis from 'ioredis';

に直したら動いた。間違ってるのに。
まぁそんなこともあるのかなと思ったりもしたけど、テストファイルをコンパイルして .js ファイルにしてテスト実行してみるとエラーになる。

    TypeError: ioredis_1.default is not a constructor

      4 | describe('Redis', () => {
      5 |     it('should be return pong', async () => {
    > 6 |         const redis = new ioredis_1.default();
        |                       ^
      7 |         const pong = await redis.ping();
      8 |         redis.disconnect();
      9 |         expect(pong).toBe('PONG');

      at Object.it (dist/sample.spec.js:6:23)

TypeError: ioredis_1.default is not a constructor
やっぱり書き方が間違ってるいるわけだ。

原因

ts-jest にバグがあったようだ。

"devDependencies": {
    "@types/ioredis": "^4.0.3",
    "@types/jest": "^23.3.5",
    "jest": "^23.6.0",
    "ts-jest": "^21.2.4",
    "typescript": "^3.1.3"
  }

yarn upgrade --latest ts-jest して 23.10.4 にしたら治った。

awslogsはpython2系依存なのでgolang実装のcwをつかう

AWS の Lambda や ECS の需要が増えたことにより、CloudWatch Logs の需要も増えてると思う。
でも AWS コンソールから CloudWatch Logs はとても見にくい。そこで awslogs を使ってる人が多いと思う。

🌋 awslogs の問題点

Python2系への依存、これが非常にだるい。Pythonpyenv で 3 系いれてたりするし。

$ brew info awslogs
awslogs: stable 0.10 (bottled), HEAD
Simple command-line tool to read AWS CloudWatch logs

==> Dependencies
Required: python@2 ✔

🐾 Golang 実装の cw

www.lucagrulla.com

$ brew info cw
lucagrulla/cw/cw: stable 1.7.2
The best way to tail AWS Cloudwatch Logs from your terminal

バイナリ配布が得意な Golang 実装なので依存関係はゼロ。

📝 使い方

Homebrew からインストールできる。

$ brew tap lucagrulla/cw
$ brew install cw

-p~/.aws/credentials のプロファイルを指定可能。tail -f でリアルタイム監視できる。その後ろの第1引数で ロググループ名、第2引数で ログストリーム名 を指定。第3引数で日時指定を UTC で指定できる。

$ cw -p <my_aws_profile> tail -f <my-log-group> <my-log-stream-prefix> <start-time>
$ cw -p jnst tail -f /ecs/my-server ecs/my-server/**** 2018-10-01T08:10:10