全力で怠けたい

怠けるために全力を尽くしたいブログ。

pixela4go が v1.8.0 にバージョンアップしました。

pixela4go が v1.8.0 にバージョンアップしました。

Pixela v1.27.0 に対応するバージョンアップとなります。

v1.8.0 アップデート内容

Pixela v1.27.0/stats API に追加された、maxDate, minDate, yesterdayQuantity の3つのフィールドに対応しています。

Pixela の1ユーザーとしては、maxDate, minDate の2つは便利だなと感じることが多いです。 pixela4go を使われている方には、ぜひ v1.8.0 にアップデートして、Pixela v1.27.0 で追加されたフィールドを試してみていただきたいですね。

現場からは以上です。

GoLand の Find Usages 機能で、プロダクションコード / テストコードだけを検索する方法。

はじめに

JetBrains 製の Go 言語向け IDE の GoLand に搭載されている Find Usages 機能で、プロダクションコード / テストコードだけを検索する方法について記載する。

GoLand の Find Usages 機能は、変数を参照 / 値を設定したり、関数やメソッドを呼び出している箇所を調べられる機能で、とても便利。 しかしながら、変数や関数の参照元 / 呼び出し元を調べる際、デフォルトでは「プロダクションコードからの参照だけを調べたい」「テストコードからの呼び出しだけを調べたい」ということができず、少々不便さを感じる *1

でも、GoLand でも少し設定を追加してあげると、プロダクションコード / テストコードだけを検索できるようになる。 以下、その設定方法について記載していく。

Find Usages 機能で、プロダクションコード / テストコードだけを検索する方法

Preferences > Settings > Appearance & Behavior > Scopes にて、次の2つのスコープを追加する。

  1. プロダクションコードだけを検索するスコープ
    • Name: Project Production Files
    • Pattern: file:*/&&!file:vendor//*&&!file:*_test.go
  2. テストコードだけを検索するスコープ
    • Name: Project Test Files
    • Pattern: file:*/&&!file:vendor//*&&file:*_test.go

あとは、Find Usages 機能を使う際に目的に応じたスコープを指定することによって、プロダクションコード / テストコードだけを検索することができる。

↓スコープ: All Places を指定したとき

↓スコープ: Project Production Files を指定したとき

↓スコープ: Project Test Files を指定したとき

以上。

参考サイト

*1:このあたり、同じ JetBrains 製の IntelliJ IDEA の場合は、プロダクションコードだけ / テストコードだけを調べる、ということがデフォルトでできるので、GoLand でもデフォルトでできるようになるといいな、と思っている

AWS Elastic Beanstalk にて構築した環境にアプリケーションをデプロイする際、更新処理がタイムアウトしてしまう事象の解消方法。

はじめに

AWS の Elastic Beanstalk にて構築した環境にアプリケーションをデプロイする際、Beanstalk 環境の更新処理がタイムアウトしてしまう事象が発生した。

結論としては、アプリケーションサーバーをホストしている EC2 インスタンスを入れ替えることで、事象が解消した。

この事象を解消するために行ったこと、調査したことを記載していく。

事象

AWS の Elastic Beanstalk にて構築した環境にアプリケーションをデプロイする際、Beanstalk 環境の更新処理がタイムアウトしてしまう事象が発生した。

INFO    Environment update is starting.
INFO    Deploying new version to instance(s).
INFO    Environment health has transitioned from Ok to Info. Application update in progress on 1 instance. 0 out of 1 instance completed (running for 30 seconds).
ERROR   During an aborted deployment, some instances may have deployed the new application version. To ensure all instances are running the same version, re-deploy the appropriate application version.
ERROR   Failed to deploy application.
ERROR   Unsuccessful command execution on instance id(s) '<インスタンス ID>'. Aborting the operation.
INFO    Command execution completed on all instances. Summary: [Successful: 0, TimedOut: 1].
WARN    The following instances have not responded in the allowed command timeout time (they might still finish eventually on their own): [<インスタンス ID>].
WARN    Environment health has transitioned from Info to Warning. Application update is aborting (running for 14 minutes).

また、アプリケーションのデプロイ以外の操作ではどうなるか? を確認するためにアプリケーションサーバーを再起動したところ、やはり Beanstalk 環境の更新処理がタイムアウトしてしまうことが分かった。 次のような挙動から、アプリケーションサーバーをホストしている EC2 インスタンスにて異常が発生している可能性が高いと想定した。

  • 事象が発生している Beanstalk 環境のアプリケーションサーバーの再起動は、普段であれば数十秒もかからずに完了する
  • Beanstalk 環境の更新ログの内容が、アプリケーションのデプロイに失敗している際のログと同じような内容になっている
INFO    restartAppServer is starting.
INFO    Environment health has transitioned from Ok to Info. Application restart in progress (running for 4 seconds).
ERROR   Unsuccessful command execution on instance id(s) '<インスタンス ID>'. Aborting the operation.
INFO    Command execution completed on all instances. Summary: [Successful: 0, TimedOut: 1].
WARN    The following instances have not responded in the allowed command timeout time (they might still finish eventually on their own): [<インスタンス ID>].
INFO    Environment health has transitioned from Info to Ok. Application restart completed 57 seconds ago and took 14 minutes.

次に、アプリケーションサーバーをホストしている EC2 インスタンスのログを調べた。

アプリケーションサーバーをホストしている EC2 インスタンスSSH ログインし、各種ログを調べたところ、/var/lib/log/message にて、cfn-hup.service が停止 / 起動を繰り返していることを示唆するログが出力されていた。 cfn-hup.service は、Beanstalk の環境を更新する際、ユーザーが指定した操作を実行するデーモンのようで、サーバーのデプロイがタイムアウトしてしまうのは、cfn-hup.service が停止 / 起動を繰り返していて、ユーザーが指定した操作が実行されていない可能性が高そう。

The cfn-hup helper is a daemon that detects changes in resource metadata and runs user-specified actions when a change is detected. This allows you to make configuration updates on your running Amazon EC2 instances through the UpdateStack API action.

/var/lib/log/message に出力されていた、cfn-hup.service が停止 / 起動を繰り返しているログ

systemd: cfn-hup.service: main process exited, code=exited, status=1/FAILURE
systemd: Unit cfn-hup.service entered failed state.
systemd: cfn-hup.service failed.
systemd: cfn-hup.service holdoff time over, scheduling restart.
systemd: Stopped This is cfn-hup daemon.
systemd: Starting This is cfn-hup daemon...
systemd: Started This is cfn-hup daemon.

事象を解消するために行ったこと

ユーザーが指定した操作がタイムアウトしてしまう原因は、cfn-hup.service の異常動作にある可能性が高そう。 ということは、cfn-hup.service を再起動することで事象が解消できそうだが、すでに cfn-hup.service が停止 / 開始を繰り返している状態のため、cfn-hup.service を再起動しても事象が解消されない可能性がある *1

今回は、アプリケーションサーバーをホストしている EC2 インスタンスを入れ替えて、事象を解消した *2

事象が発生していたアプリケーションサーバーは部内でのみ使われているもので、多少ダウンタイムが発生してもまったく問題がないものではあったが、ダウンタイムを極小化するため、次のようにして EC2 インスタンスを入れ替えた。

  1. アプリケーションサーバーをホストする EC2 インスタンスはもともと1台だったが、1台増やして2台にする
  2. 元々の EC2 インスタンスを停止する
    • 新しい EC2 インスタンスが起動してくる
    • 新しく起動した EC2 インスタンスは、cfn-hup.service が問題なく稼働していることを確認
    • つまり、起動している 2台の EC2 インスタンスは、cfn-hup.service が問題なく稼働しているということ
  3. アプリケーションサーバーをホストする EC2 インスタンスを1台減らし、2台→1台にする
    • 残った1台の EC2 インスタンスは、cfn-hup.service は問題なく稼働しているということ

以上。

参考サイト

*1:実際に cfn-hup.service を再起動してみたが、事象が解消されなかった

*2:後から思ったことだけど、やっぱりまずは cfn-hup.service の再起動を行えばよかったとは思う

パーセンタイル値を計算する percentile コマンドを作りました。

What is percentile コマンド?

データ分析において、パーセンタイル値はしばしば重要な指標となります。

percentile コマンドは、ファイルや標準入力から数値を読み込み、パーセンタイル値を表示する CLI プログラムです。

github.com

percentile コマンドの特長

percentile コマンドには、次のような特徴があります。

  1. ファイルおよび標準入力から数値を読み込み、読み込んだ数値のパーセンタイル値を表示する
  2. 読み込んだ数値はデフォルトでソートされるため、事前にソートする必要がない
  3. 表示するパーセンタイル値は、デフォルトでは p25, p50, p75, p90, p95, p99 だが、オプションで指定することができる
  4. パーセンタイル値はデフォルトでは切り捨てられるが、切り捨てないようにオプションで指定できる

使い方の実行例

percentile コマンドを実行する例を示します。

ファイルから数値を読み込む

percentile <ファイル> と実行すると、ファイルから読み込んだ数値のパーセンタイルを表示します。

$ percentile <(seq 1 100)
p25: 25
p50: 50
p75: 75
p90: 90
p95: 95
p99: 99

標準入力から数値を読み込む

<他のコマンド> | percentile - と実行すると、標準入力から読み込んだ数値のパーセンタイルを表示します。

$ seq 1 100 | percentile -
p25: 25
p50: 50
p75: 75
p90: 90
p95: 95
p99: 99

表示するパーセンタイル値を指定する

表示するパーセンタイル値は、デフォルトでは p25, p50, p75, p90, p95, p99 ですが、-p オプションで表示するパーセンタイル値を指定できます。 表示するパーセンタイル値が複数ある場合は、-p 25,50 のようにカンマで区切ります。

$ seq 1 100 | percentile -p 25,50 -
p25: 25
p50: 50

パーセンタイル値を切り捨てない

表示するパーセンタイル値は、デフォルトでは切り捨てられて整数になりますが、-r オプションを指定すると小数点第1位までが表示されます。

$ seq 1 100 | percentile -r -
p25: 25.5
p50: 50.5
p75: 75.5
p90: 90.5
p95: 95.5
p99: 99.5

インストール方法

percentile をインストールするには、以下の方法があります。

開発者向け

Go 言語の開発環境が整っている場合は、次のコマンドを実行することにより、percentile コマンドをインストールできます。

$ go install github.com/ebc-2in2crc/percentile/cmd/percentile@latest

手動でのインストール

https://github.com/ebc-2in2crc/percentile/releases にアクセスし、お使いの OS 向けの実行ファイルをダウンロードできます。

Homebrew

Homebrew を使う場合は、次のコマンドを実行することにより、percentile コマンドをインストールできます。

$ brew install ebc-2in2crc/tap/percentile

動機

最近、ちょっとしたデータ分析をする際に、パーセンタイル値を求めることが何回かありました。

CLI でパーセンタイル値を求められるコマンドを探したところ、そういったツールはいくつかあったのですが、次のようなところが自分の使い方にはマッチしませんでした。

  • 数値の読み込み元として、ファイルと標準入力の両方をサポートしていない
  • 表示するパーセンタイル値が固定されてしまっている

そこで、パーセンタイル値を計算 & 表示するコマンドをサッと実装してしまいました *1

まとめ

percentile の開発やバグレポート、質問などがあったら、GitHubリポジトリに issue や PR を作ったり、Twitter でお気軽にお声がけいただけたら嬉しいです。

github.com

ということで、 percentile コマンドの紹介でした。

*1:また、再発明をしてしまいました

ab コマンド (ApacheBench) の自分的チートシート。

ab コマンド (ApacheBench) の自分的チートシートをメモしておく。

ab コマンドのバージョン。

$ ab -V
This is ApacheBench, Version 2.3 <$Revision: 1901567 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, https://www.zeustech.net/
Licensed to The Apache Software Foundation, https://www.apache.org/

このエントリーを書いた動機

システムの性能を測定するときは k6 を使うことが多く、ApacheBench はほとんど使ったことがなかった。 しかしながら、雑にシステムに負荷をかけたり、非常にざっくりとした性能指標を見る程度であれば、ApacheBench で事足りることもあったりするし、なにより手軽に利用できるのがよいなー、と思った。 でも、めったに使わないとも思ったので、次に使うときに使い方をさっと調べられるように、備忘録代わりに書いた。

ab コマンドのチートシート

基本的な使い方

$ ab -c <並列数> -n <リクエスト数> <URL>

たとえば、次のコマンドでは、https://localhost:8080/ に対して10並列でリクエストを1000回送信する。

$ ab -c 10 -n 1000 https://localhost:8080/

リクエスト数を指定する

-n オプションにて、リクエスト数を指定できる。

# リクエストを100回送信する
$ ab -n 100 https://localhost:8080/

# デフォルトは1回だけ送信する。次の2つのコマンドは、どちらもリクエストを1回だけ送信する
$ ab https://localhost:8080/
$ ab -n 1 https://localhost:8080/

並列数を指定する

-c オプションにて、リクエストの並列数を指定できる。

# 10並列でリクエストを1000回送信する
$ ab -c 10 -n 1000 https://localhost:8080/

# デフォルトは1並列。次の2つのコマンドの並列数はどちらも1となる
$ ab https://localhost:8080/
$ ab -c 1 https://localhost:8080/

タイムアウトを指定する

-t オプションにて、ベンチマークの最大実行時間を秒単位で指定できる。

-t オプションを指定した場合、所定の回数分のリクエストを送信するか、あるいは指定した時間が過ぎるとコマンドの実行が終了する。 なお、-n オプションを指定していない場合は -n 50000 が指定されたものとして動作する。

# リクエストを10000回送信する
# 60秒が経過すると、リクエストの送信が完了していなくても終了する
$ ab -n 10000 -t 60 https://localhost:8080/

一定時間、負荷をかけ続ける

-n オプションと -t オプションを組み合わせて、一定時間、負荷をかけ続けることができる。

-n オプションで非常に大きなリクエスト数を指定するのがポイント。

# 60秒間、100並列でリクエストを送信し続ける
# ただし、リクエストを 10000000 回送信したらコマンドの実行が完了してしまう
# その場合は -n オプションにもっと大きな値を指定するとよい
$ ab -c 100 -n 10000000 -t 60 https://localhost:8080/

HTTP ヘッダーを指定する

-H オプションにて、HTTP ヘッダーを指定できる。

# Accept-Encoding を指定する
$ ab -H 'Accept-Encoding: gzip, deflate' https://localhost:8080/

# 複数の HTTP ヘッダーも指定できる
$ ab -H 'Accept-Encoding: gzip, deflate' -H 'Cache-Control: max-age=0' https://localhost:8080/

HTTP Keep-Alive を有効にする

-k オプションを指定すると HTTP Keep-Alive を有効にできる。

# HTTP Keep-Alive を有効にする
$ ab -k https://localhost:8080/

# デフォルトは HTTP Keep-Alive は無効となる
$ ab https://localhost:8080/

出力のフォーマット

ab コマンドを実行すると次のように出力される。

$ ab -n 10 https://localhost:8080/
This is ApacheBench, Version 2.3 <$Revision: 1901567 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, https://www.zeustech.net/
Licensed to The Apache Software Foundation, https://www.apache.org/

Benchmarking localhost:8080 (be patient).....done

Server Software:
Server Hostname:        localhost:8080
Server Port:            443
SSL/TLS Protocol:       TLSv1.2,ECDHE-ECDSA-CHACHA20-POLY1305,384,256
Server Temp Key:        ECDH X25519 253 bits
TLS Server Name:        localhost:8080

Document Path:          /
Document Length:        4 bytes

Concurrency Level:      1
Time taken for tests:   0.293 seconds
Complete requests:      10
Failed requests:        0
Total transferred:      248 bytes
HTML transferred:       4 bytes
Requests per second:    3.41 [#/sec] (mean)
Time per request:       293.165 [ms] (mean)
Time per request:       293.165 [ms] (mean, across all concurrent requests)
Transfer rate:          0.83 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:      190  190   0.0    190     190
Processing:   103  103   0.0    103     103
Waiting:      102  102   0.0    102     102
Total:        293  293   0.0    293     293

Percentage of the requests served within a certain time (ms)
  50%    231
  66%    233
  75%    234
  80%    236
  90%    258
  95%    258
  98%    258
  99%    258
 100%    258 (longest request)

この出力のうち、性能指標に関わる項目の意味は次のとおり。

項目 説明
Concurrency Level 並列数
Time taken for tests テストにかかった時間 (秒)
Complete requests 成功したリクエスト数
Failed requests 失敗したリクエスト数
Total transferred 合計転送量 (バイト)
HTML transferred HTML ドキュメントの転送量 (バイト)
Requests per second 1秒あたりの平均リクエスト数
Time per request 1リクエストあたりの平均所要時間 (ミリ秒)
Transfer rate 転送速度 (KB/秒)

Connection Times (ms) の部分はリクエストの処理に要した時間の内訳。 項目ごとに左から、最小値、平均値、標準偏差、中央値、最大値となっている。

項目 説明
Connect クライアントがサーバーに接続するのにかかった時間
Processing リクエストが送信されてから、サーバーの応答が完了するまでの時間
Waiting リクエストが送信されてから、サーバーから最初のバイトが受信されるまでの時間

Percentage of the requests 〜 の部分は、パーセンタイルでのリクエストの応答時間

実行結果を CSV ファイルに出力する

-e オプションで出力先のファイル名を指定し、実行結果を CSV ファイルに出力できる。

$ ab -n 10 -e result.csv https://localhost:8080/
... 省略 ...

CSV ファイルは次のようなフォーマットで出力される。

$ cat result.csv
Percentage served,Time in ms
0,184.886
1,184.886
2,184.886
... 省略 ...
99,0.000
100,241.682

実行結果を TSV ファイルに出力する

-g オプションで出力先のファイル名を指定し、実行結果を CSV ファイルに出力できる。

$ ab -n 10 -g result.tsv https://localhost:8080/
... 省略 ...

TSV ファイルは次のようなフォーマットで出力される。

$ cat result.tsv
starttime   seconds ctime   dtime   ttime   wait
Thu May 18 19:50:48 2023    1684407048  96  100 196 100
Thu May 18 19:50:48 2023    1684407048  105 109 215 109
Thu May 18 19:50:48 2023    1684407048  107 109 216 109
Thu May 18 19:50:47 2023    1684407047  107 109 216 109
Thu May 18 19:50:47 2023    1684407047  110 112 222 112
Thu May 18 19:50:48 2023    1684407048  113 114 227 114
Thu May 18 19:50:48 2023    1684407048  115 116 231 116
Thu May 18 19:50:46 2023    1684407046  115 117 232 117
Thu May 18 19:50:47 2023    1684407047  116 119 235 119
Thu May 18 19:50:47 2023    1684407047  126 127 253 126

コマンド、キーボードショートカットで Mac を再起動する方法。

マウスを使わずに、コマンド、キーボードショートカットで Mac を再起動する方法をメモしておく。

shutdown コマンドで Mac を再起動する

ターミナルソフトで次のコマンドを入力する。 大概はこれでなんとかなる。

$ sudo shutdown -r now

キーボードショートカットで Mac を再起動する

キーボードショートカットで Mac を再起動するには、control ボタン、command ボタン、電源ボタンを同時押しする。 このキーボードショートカットは、誤操作を避けるためにある程度長めに押す必要がある。実際に Mac が再起動するまで押し続けるとよい。

大概は shutdown コマンドを使う方法でなんとかなるが、ユーザーリソースの設定を誤ってしまう等でシステム管理用のコマンドすら実行できなくなってしまった場合は、キーボードショートカットで Mac を再起動するしかない。

参考サイト

sed コマンドを使って cat, grep, tr, wc, head, tail コマンドを再発明してみた。

社内でシェルスクリプトの活用が増えるといいなー、と思って、とっかかりになるようなパズル的なものを考えてみた。 シェルスクリプトに興味をもってもらうことが目的なので、いわゆる「シェル芸」のような結構手応えがあるものではなく、一部を除いてはごく簡単なものになっていると思う。

sed コマンドのバージョン

macOSGNU sed をインストールして使っている。

$ sed --version
sed (GNU sed) 4.8

sed コマンドでいろんなコマンドを再発明する

cat

cat コマンドの再発明、そして他のコマンドの再発明には、次のファイルを入力として使う。

$ cat numbers.txt
1 2 3
4 5 6
7 8 9
10 11 12
13 14 15
16 17 18

sed コマンドは式を与えられない場合、入力をそのまま出力するので、それを利用する。

$ sed '' numbers.txt
1 2 3
4 5 6
7 8 9
10 11 12
13 14 15
16 17 18

grep

次のようにすると grep コマンドを再発明できる。

  1. -n オプションを指定し、出力を抑制する
  2. 出力したい行 (アドレス) を正規表現で指定し、マッチしたら p コマンドで出力する
$ grep 3 numbers.txt
1 2 3
13 14 15

$ sed -n /3/p numbers.txt
1 2 3
13 14 15

tr

次のようにすると tr コマンドを再発明できる。

  1. y コマンドを使って文字列内の文字を変換する
$ tr [123] [abc] < numbers.txt
a b c
4 5 6
7 8 9
a0 aa ab
ac a4 a5
a6 a7 a8

$ sed 'y/123/abc/' numbers.txt
a b c
4 5 6
7 8 9
a0 aa ab
ac a4 a5
a6 a7 a8

wc

次のようにすると wc -l コマンドを再発明できる。

  1. -n オプションを指定し、出力を抑制する
  2. 入力の最終行をマッチさせて = コマンドを実行し、マッチした行つまり最終行の行番号を出力する
$ wc -l numbers.txt
       6 numbers.txt

$ sed -n '$=' numbers.txt
6

次のようにすると head コマンドを再発明できる。

  1. 表示したい最終行 (入力全体での2行目) をマッチさせて q コマンドを実行し、そこでスクリプトを終了する
$ head -n 2 numbers.txt
1 2 3
4 5 6

$ sed '2q' numbers.txt
1 2 3
4 5 6

別解。

  1. -n オプションを指定し、出力を抑制する
  2. 表示したい最初の行から最終行までをマッチさせて、p コマンドで出力する
$ sed -n '1,2p' numbers.txt
1 2 3
4 5 6

tail

次のようにすると tail コマンドを再発明できる *1 ここでは、ファイルの末尾2行を表示させている。

  1. :a にてラベルを定義する。あとでジャンプのターゲットとして使う
  2. $q;N;3,$D;ba: この部分には以下の4つのコマンドが含まれている
    1. $q: 直前に読み込んだ行が入力の最終行である場合、それまでの行を出力して q コマンドでスクリプトを終了する。 これにより、最後の2行まで読み込んだ後に出力されるようになる。
    2. N: 次の行を読み込んでパターンスペースに追加する。 これにより、複数の行をパターンスペースに保持させ、最終的には最後の2行がパターンスペースに格納される。
    3. 3,$D: パターンスペース内の行が3行以上になった場合、最も古い行 (つまり先頭の行) を削除する。 さらに D コマンドの副作用として新しい入力行を読み込み、スクリプトが最初から実行される。 これにより、最後の2行だけがパターンスペースに保持されるようになる。
    4. ba: b コマンド (分岐コマンド) を実行して定義済みのラベル a にジャンプする。 これにより、スクリプトが最初から繰り返されるようになる。
$ tail -n 2 numbers.txt
13 14 15
16 17 18

$ sed ':a;$q;N;3,$D;ba' numbers.txt
13 14 15
16 17 18

以上。

*1:パッと思いつかなくて、コマンドのリファレンスを調べたり ChatGPT に相談しながらやってみた