コピペコードで快適生活

明日使えるソースを自分のために

Grape+Rspec環境でAPIをテストする

既存RailsアプリにRspecを入れる - コピペコードで快適生活
の続き。

まず、JSONのテストを簡単にするgemを入れる。

gem 'json_expressions'

テストコードはこんな感じで書ける。

require 'rails_helper'
require 'json_expressions/rspec'

describe "GET api/orders/:order_ids" do
  context '存在しないIDを指定するとき' do
    it '空の配列が返る' do
      order_ids = "aaaa,bbbb"
      get "/api/v1/orders/#{order_ids}"
      pattern = {
        orders: [],
        total_count: 0
      }
      expect(response.status).to eq 200
      expect(response.body).to match_json_expression(pattern)
    end
  end
end

describe 'PATCH api/orders/:order_id/cancel' do
  context '存在しないIDを指定するとき' do
    it '204が返る' do
      order_id = "aaaa"
      params = {xxx: 2}
      patch "/api/v1/orders/#{order_id}/cancel", params
      expect(response.status).to eq 204
    end
  end
end

Nodejsでのバイナリの取り扱い

よくわかっていなかったので整理した。

Bufferオブジェクト

Node.jsではバイナリデータはBufferオブジェクトで扱う。

ArrayBuffer

実際のメモリに格納されたバイナリデータを配列で表現したもの。読み込み専用。

Uint8Array

バイナリデータを8bitごとに区切って配列にしたもの。1つの要素には0~255の値が入る。変更可能。

// 128バイトのバッファを作成
// 初期化はされない
let buf = new Buffer(128);
console.log('定義だけ', buf);

// バッファに0を書き込む
buf.fill(0);
console.log('0で初期化', buf);

// 10バイト目から20バイト目まで2を書き込む
// (先頭は0と数える)
buf.fill(2, 10, 30);
console.log('2バイトで一部初期化', buf);

// 文字列をバイナリ化
buf = Buffer.from('テスト文字列', 'utf8');
console.log('文字列バイナリ', buf);
console.log('文字列のバイト数', buf.length);
console.log('元の文字列を出力', buf.toString('utf8'));

// bufferをArrayBufferにする
function toArrayBuffer(buffer) {
  var ab = new ArrayBuffer(buffer.length);
  var view = new Uint8Array(ab);
  for (var i = 0; i < buffer.length; ++i) {
    view[i] = buffer[i];
  }
  return ab;
}
buf = Buffer.from('TEST', 'utf8');
let arrayBuf = toArrayBuffer(buf);
console.log('ArrayBuffer', arrayBuf);

// arrayBufferからBufferを作る
buf = Buffer.from(arrayBuf);
console.log('arrayBufから作成したBuffer', buf);

// ファイルを読み込む
let fs = require('fs');
buf = fs.readFileSync('./buf.js');
console.log('ファイルのバイナリデータ', buf);

// axiosでバイナリデータを取得するときは
// responseTypeに arraybuffer を指定する
// そうすることで、arrayBufferオブジェクトとして受け取れる
// let client = axios.create({
//   'responseType': 'arraybuffer',
// });

// Bufferオブジェクトかどうかのチェック
console.log('Bufferかどうかチェック', Buffer.isBuffer(buf));

補足

html5のJSにはBufferはない。Blobで扱う。
https://www.sejuku.net/blog/67735#Blob-2

ArrayBufferをBlobに変換
http://var.blog.jp/archives/62330155.html

AWS NLBとコネクションプールについて

NLBの後ろに1台足してもそっちに全然クエリが流れてこなかった。
間違っているかもしれないけど、多分こういうことじゃないかな。

コネクションプールについて

接続確立は負荷を伴うので、一度確立した接続を維持して、その中でクエリ発行することで負荷を軽減する仕組み。

クライアントの実装によるが

・プール数上限設定があってそれ以上のクエリ発行をしようとすると待つ。
・接続断が発生したあと、再度クエリを発行すると再接続確立を試みる。
・一定時間使われていないコネクションがあると閉じる(ガベージコレクタに回収させたり)
・なので、クエリ発行が休みなく正常に続いているコネクションはずっと開いたまま。

AWS NLB

NLBは単にバランシングしているだけで、実際にはその後ろにあるサーバとコネクションが確立されている。
なので、
・クエリ発行が休みなく正常に続いているコネクションはずっと同じサーバとつながったまま。
・NLBの後ろに1台足しても、クエリ発行が休みなく続く限り、新しいサーバには通信はいかない。
・クライアント側を再起動してコネクションを再接続すると、新しいサーバにも通信がいくようになる。

シェルスクリプトで対話的に選択肢を選ばせる

よく忘れるのでメモしておく。

#
# 入力値チェック/セット
#
echo 'CloudFrontログを抽出します。'
echo '対象ドメインを番号で指定してください'
echo '
1) a.example.com
2) b.example.com
3) c.example.com
'
read num
case ${num} in
  1)
    DOMAIN='a.example.com'
    CF_ID='xxxxx'
    ;;
  2)
    DOMAIN='b.example.com'
    CF_ID='xxxxx'
    ;;
  3)
    DOMAIN='c.example.com'
    CF_ID='xxxxx'
    ;;
  *)
    echo '番号が不正です'
    exit
    ;;
esac

PostgreSQLでプロセスとロック状況を確認する

## 実行中のSQLを確認する。
SELECT * FROM pg_stat_activity;

## 実行中のSQLを確認する
## スロークエリとか主に見る分だけ
SELECT pid, query_start, substr(query, 0, 80) AS query FROM pg_stat_activity WHERE state='active' ORDER BY query_start;

## ロック状況の確認
SELECT l.pid, db.datname, c.relname, l.locktype, l.mode
FROM pg_locks l
LEFT JOIN pg_class c ON l.relation=c.relfilenode
LEFT JOIN pg_database db ON l.database = db.oid
ORDER BY l.pid;

## AccessExclusiveLockロックがかかっているか
SELECT l.pid, db.datname, c.relname, l.locktype, l.mode
FROM pg_locks l
LEFT JOIN pg_class c ON l.relation=c.relfilenode
LEFT JOIN pg_database db ON l.database = db.oid
WHERE l.mode = 'AccessExclusiveLock'
ORDER BY l.pid;

## Railsのマイグレーションでロックをかけない呪文
## デフォだとSELECTも通さないAccessExclusiveLockがかかる
class AddStatusToUsers < ActiveRecord::Migration
  disable_ddl_transaction!

  def up
    # 略
    Users.find_each do |user|
      user.update_columns(status: 'xxx')
    end
  end

  def down
    # 略
  end
end

## インデックス追加中にロックかけない
## (concurrentlyオプションを有効にする)
## https://thoughtbot.com/blog/how-to-create-postgres-indexes-concurrently-in
class AddIndexToAsksActive < ActiveRecord::Migration
  disable_ddl_transaction!

  def change
    add_index :asks, :active, algorithm: :concurrently
  end
end

メモ

PostgreSQL×Rails環境でのマイグレーションで、バカでかいテーブルに対して、add_column + eachまわしてupdate ということをやると、こうなる。

・add_columnsで、AccessExclusiveLockかかる。
・AccessExclusiveLockの中でeachでUPDATE走る。
・AccessExclusiveLockはSELECTも通さないロックなので、
 該当テーブルに対するSELECTがすべて待ちになる。
・待ちのSELECTでDBプロセス食い尽くす。
・DBが応答なくなる。
・障害発生 :unko:

Imagemagickコマンドメモ

## 黒塗り / 中央寄せ / リサイズせず正方形に整える
convert -background black -gravity center -extent 960x960 tamanegi.png _tamanegi.png

## 白色を透過に変える(transparent) / 白から5%くらいずれていても透過させる(fuzz)
convert input.png -fuzz 5% -transparent white output.png

## 複数の画像をマージする。
## 横につなげる
convert -append 01.png 02.png out.png

## 縦につなげる
convert +append 01.png 02.png out.png

## 横につなげる + 上下左右に30の余白を開けて連結する
convert -append -border 30x30 01.png 02.png out.png

## 文字画像を作る
convert -size "256x128" -background "#f5f5f5" -fill "#dc143c" caption:"image\nmagick\n" output.png