動かざることバグの如し

近づきたいよ 君の理想に

NodejsでFluentdにデータを送信する

答え

公式クライアントの「fluent-logger」を使う。

fluent/fluent-logger-node: A structured logger for Fluentd (Node.js)

やり方

デバッグしやすいように以下のfluendの設定をしたサーバーを用意する。

<source>
  @type forward
  port 24224
</source>

<match *.*>
  @type stdout
</match>

受け取ったデータを全部標準出力に出力するだけのかんたんな設定

そしてnodejsを用意

yarn add fluent-logger

で以下

const logger = require('fluent-logger')
logger.configure('mylog', {
  host: '127.0.0.1',
  port: 24224,
  timeout: 3.0,
  enableReconnect: true,
  reconnectInterval: 600000 // 10 minutes
})

logger.emit('info', {from: 'userA', to: 'userB'})
setTimeout(()=> logger.end(), 1000);

すると以下のように表示されるはず

fluentd_1  | 2020-07-30 02:37:09.000000000 +0900 mylog.info: {"from":"userA","to":"userB"}
  • ここではmylogがタグのprefixでinfoはsuffixなのでmylog.infoになる

注意点

明示的に logger.end() をしないと終了されないのが注意。

ただ、普通に書いてしまうと送信前に終わってしまうので setTimeout() をつけて遅延させて上げる必要がある。

参考リンク

Railsで削除時にネストしたモデルも削除する

環境

やりたいこと

Railsでは 1対多 とか 多対多 の関係のときに親レコードを削除したときにそれに紐づく子レコードも一緒に削除することができる。方法は簡単で has_many(has_one)にdependent: :destroyを追加するだけ

ここで以下のようなparent>child>grandchildのモデルが合ったとする。これでparentレコードを削除したときにchildもgrandchildも自動で削除できるようにしたい。

parent.rb

class Parent < ApplicationRecord
  has_many :childs
end

child.rb

class Child < ApplicationRecord
  belongs_to :parent
  has_many :grandchilds
end

grandchild.rb

class Grandchild < ApplicationRecord
  belongs_to :child
end

やり方

parent.rbdependent: :destroy を追加し、child.rbdependent: :delete_all を追加する。

parent.rb

class Parent < ApplicationRecord
  has_many :childs, dependent: :destroy
end

child.rb

class Child < ApplicationRecord
  belongs_to :parent
  has_many :grandchilds, dependent: :delete_all
end

なんでchild.rbはdelete_allなの

dependent: :destroy だと削除するときActiveRecord経由で削除するのでコールバックも発生するし1レコードずつ削除するので、 モデルchildとgrandchildのレコード数が多いとめっちゃ遅くなる。

dependent: :delete_allにすると直接SQLで削除されるので1SQLで一発で削除される。

参考リンク

ddコマンドを使って簡易ディスク速度を測定する

環境

やりたいこと

ddコマンドはどのOSにも最初からインストールされている。それを使って簡易的なディスクのパフォーマンスを確認したい。

しっかり見るならfioってツール使ったほうがいい。

thr3a.hatenablog.com

コマンド

以下

dd if=/dev/zero of=/tmp/write.tmp ibs=1M obs=1M count=100 conv=fdatasync

ofの /tmp/write.tmp は調査したいパスを指定する。

1M x 100 なので 100Mぶん書き込まれる。

こんな感じ

$ dd if=/dev/zero of=/tmp/write.tmp ibs=1M obs=1M count=1000 conv=fdatasync
1000+0 records in
1000+0 records out
1048576000 bytes (1.0 GB, 1000 MiB) copied, 2.30278 s, 455 MB/s

あと重要なオプションとして fdatasync これがないと書き込みが完了してないのみコマンドが終わってしまうので正確なベンチマークができない。

fdatasync: コマンドを終了する直前に、出力データを同期させる。 すなわち、出力データをディスクに実際に書き込む。

たまにこれつけずにddでベンチマークを解説してるブログあるけど大丈夫か(

コピペで使えるcurlでHTTPリクエストする.php

使用頻度高いのでテンプレートってことでメモ

確認環境

コード

<?php
// GETパラメータ
$params = [
  'key' => 'value'
];
$url = 'https://httpbin.org/get?' . http_build_query($params);
// $url = 'https://httpbin.org/status/500';
$curl = curl_init();
$curl_options = [
  CURLOPT_URL => $url,
  // データを取得できるように
  CURLOPT_RETURNTRANSFER => true,
  // リダイレクト許可
  CURLOPT_FOLLOWLOCATION => true,
  // status_code > 400の場合エラーに
  CURLOPT_FAILONERROR => true
];
curl_setopt_array($curl, $curl_options);
$response = curl_exec($curl);
$info = curl_getinfo($curl);

if (curl_errno($curl)) {
  $error = curl_error($curl);
  echo $error;
}
curl_close($curl);

if(!isset($error)) {
  $json = json_decode($response);
  var_export($json);
}

エラーの場合、$errorにエラーメッセージがセットされる。

詳しいレスポンス情報を見たい

curl_close() する前に curl_getinfo() すると詳細を取得できる。

<?php
$info = curl_getinfo($curl);
var_export($info);

すると以下のようなデータを取得できる

array (
  'url' => 'https://httpbin.org/status/500?key=value',
  'content_type' => NULL,
  'http_code' => 500,
  'header_size' => 0,
  'request_size' => 68,
  'filetime' => -1,
  'ssl_verify_result' => 0,
  'redirect_count' => 0,
  'total_time' => 0.705413,
  'namelookup_time' => 0.001785,
  'connect_time' => 0.175847,
  'pretransfer_time' => 0.518781,
  'size_upload' => 0.0,
  'size_download' => 0.0,
  'speed_download' => 0.0,
  'speed_upload' => 0.0,
  'download_content_length' => -1.0,
  'upload_content_length' => -1.0,
  'starttransfer_time' => 0.705382,
  'redirect_time' => 0.0,
  'redirect_url' => '',
  'primary_ip' => '3.220.112.94',
  'certinfo' => 
  array (
  ),
  'primary_port' => 443,
  'local_ip' => '172.17.0.3',
  'local_port' => 45232,
  'http_version' => 3,
  'protocol' => 2,
  'ssl_verifyresult' => 0,
  'scheme' => 'HTTPS',
  'appconnect_time_us' => 518414,
  'connect_time_us' => 175847,
  'namelookup_time_us' => 1785,
  'pretransfer_time_us' => 518781,
  'redirect_time_us' => 0,
  'starttransfer_time_us' => 705382,
  'total_time_us' => 705413,
)

RspecでRaw post dataなPOSTを実行する

環境

経緯

そもそもRaw post dataとはなんぞや、から。

RailsでRequest Specを書く場合、当然POSTメソッドのテストも書くケースがある。

そんなときはこんな感じになると思う。

# spec/requests/logins_spec.rb
require "rails_helper"

RSpec.describe "Sessions", type: :request do
  describe "POST /login" do

    context "xxxなとき" do

      before do
        post login_path, params: { email: "aaa@example.com" }
      end

      it "xxxすること" do
        # 略
      end
    end
  end
end

重要なのは post login_path, params: { email: "aaa@example.com" } で、これでPOST送信できる。

このときのPOST送信をcurlに書き換えてみると

curl -X POST -F 'email=aaa@example.com’ http://example.com/login

となり、よくある<form>のHTMLフォームからsubmitした挙動と同じパラメータで渡すことができる。

このときのContent-Typeはmultipart/form-dataになる。

が、歴史的経緯によりraw post dataなPOSTで受け取る場合、上のテストが使えない。

具体的にcurlで表すと

curl -X POST -d 'email=aaa@example.com' http://example.com/login

の場合。

やり方

params引数に文字列を渡すと暗黙的にRaw post dataなPOSTになる

post '/path', params: '{"foo": "bar", "bool": true}', headers: { 'Content-Type' => 'application/json' }

これで Controller側では request.body.read で中身が受け取れると思う。

使うケースは少ないかもだが、、

参考リンク