RDS(PostgreSQL)のクエリログの有無によるpgbenchの差異

RDS(PostgreSQL)でクエリログをON(log_statement=all)・OFF(log_statement=none)、さらにexportしたときの性能の変化が気になったので、pgbenchで負荷テストを行ってみた。

テスト環境

クライアント

#!/bin/bash
echo "$(date +%FT%TZ): start"

for i in 64 128 192 256; do
  echo "$(date +%FT%TZ): $i"
  pgbench -q -i -s 100
  sleep 10
  pgbench -c $i -T 180

  if [ $i -ne 256 ]; then
    sleep 60
  fi
done

echo "$(date +%FT%TZ): end"

サーバ

  • PostgreSQL 12.8
  • m4.2xlarge(都合により旧世代…)
  • Parameter Groupはデフォルトからlog_statementだけ変更
  • Encryption有効
  • Performance Insights有効
  • Enhanced Monitoring有効
  • Multi-AZ
  • ストレージはgp2/500GB

結果

docs.google.com

tps

latency

CPU使用率

Performance Insights

log_statement=none

log_statement=all

log_statement=all (export)

参考リンク

MySQLでTPC-B likeなテストを実行するツールを作った

MySQLでpgbenchと同じようなTPC-B likeなテストを実行するツールを書いた。

github.com

これは何?

pgbenchと同様のtcpb-likeなテストを実行する負荷テストツール。

github.com

pgbenchとだいたい同じSQLが実行されると思うが、TPC-Bの仕様をきちんと満たしているかは不明。

先日の記事MySQLでの同様のテストを行った場合の結果が気になったので作成してみた。 初期データの投入が遅かったりバグがなにかありそうだけれど、とりあえず簡単なテストではうまく動いた。

使い方

READMEにあるとおりで、pgbenchと似たような感じで使える。

$ qb -d root@/ -i -s 10
dropping old database...
creating database...
creating tables...
generating data...
analyzing tables...

$ qb -d root@/ -n 10
01:00 | 10 agents / run 2603958 queries (2559 tps)

{
  "DSN": "root@/",
  "StartedAt": "2022-05-08T17:36:46.173678+09:00",
  "FinishedAt": "2022-05-08T17:37:46.177411+09:00",
  "ElapsedTime": 60,
  "NAgents": 10,
…

PostgreSQLMySQLのきちんとした性能の比較にはならないが、同様のテストを両方で実行したい向きには手軽だと思う。

その他

かるーくMySQLエンジンでAuroraとRDSの比較を行ったが、PostgreSQLほど性能に差異がない感じだった。 追記型アーキテクチャはAuroraと相性悪いのかなぁ…よくわからない。

PostgreSQLエンジンでのAuroraとRDSのベンチマーク

PostgreSQLエンジンでAuroraとRDSでpgbenchを使った負荷テストを行った。

テスト環境

クライアント

#!/bin/bash
. .rds
for i in 8 16 32 48 64; do
  echo "--- $(date +%FT%TZ) RDS $i"
  pgbench -i -s 100 -q
  pgbench -c $i -T 210
  sleep 60
done
echo "--- $(date +%FT%TZ) RDS end"

サーバ

  • PostgreSQL 13.6
  • インスタンス数はそれぞれ1台ずつ(冗長化なし)
  • Parameter Groupはデフォルト
  • クライアントインスタンスと同じAZ
  • Encryption有効
  • Performance Insights有効
  • Enhanced Monitoring有効
  • ログはエクスポートしていない
  • RDSのストレージはgp2/1000GB

結果

docs.google.com

tps

latency

CPU使用率

Performance Insights(それぞれdb.r6.2xlarge)

RDS

Aurora

雑感

  • 実際のワークロードに合わせたテストでないとプロダクション投入時の性能はなんともいえないと思う…と前置きしつつ
  • Auroraのtps限界が想定よりずいぶん低い
  • Auroraには可用性やフェイルオーバーの速さなどのメリットがあるが、うーん…
  • Auroraのアーキテクチャ的に細かいトランザクションが多いワークロードでは遅くなりそうな気はする
  • ただ、MySQLエンジンではこんなワークロードでもAurora > RDSだったのでPostgreSQLエンジンでも同様だと思っていた
  • Googleで調べた感じAuroraのほうがtpsが高い結果が見られるので、なにか設定を間違えている気もする…
  • インスタンスクラスをもっと大きくしたら結果が違うのかもしれない
  • RDSのストレージサイズによる性能の違いも調べたいところ

参考リンク

追記

RDSがSingle-AZだったのでMulti-AZで追試。

tps

latency

CPU使用率

Performance Insights(db.r6.2xlarge)

追記2

MySQLエンジンでTPC-B(tpcb-like)を軽く検証したので、結果を張っておく。

ツールが違うのでPostgreSQLエンジンの値とは直接比較できない点に注意のこと

ヒューマンリーダブルなURLでesa.ioの記事を開くやつ

ヒューマンリーダブルなURLでesa.ioの記事を開くやつを作った。

github.com

これはなに?

http://your-esaop.example.com/foo/bar/zoo のような感じの記事のパスを指定したURLを開くと esa.iohttps://[team].esa.io/posts/12345 というURLにリダイレクトするWebアプリ。

記事が存在しなかったときは、新規作成画面になる。

  • https://[team].esa.io/posts/posts/new?category_path=%2Ffoo%2Fbar%2Fzoo

また、末尾が / だとカテゴリ内の記事の一覧のURLにリダイレクトする。

  • http://your-esaop.example.com/foo/https://[team].esa.io/#path=%2Ffoo

要OAuthのアプリケーション登録。 cf. https://docs.esa.io/posts/185

日付・CRONのプレースホルダを使う

Javaの日付フォーマットとCRON式を使ったプレースホルダも使える。 例えばアクセスした日が 2022/03/21 だとして、以下のように記事を開く。

  • http://your-esaop.example.com/日誌/${yyyy/MM/dd}日誌/2022/03/21 を開く
  • http://your-esaop.example.com/週次MTG/${*,*,*,*,5|yyyy/MM/dd}週次MTG/2022/03/25 を開く

apt-transport-s3をGoに移植した

apt-transport-s3をGoに移植してapt-transport-s3-goを作った。

github.com

これは何?

aptのリポジトリとしてS3を利用できるようにするやつ。 すでにapt-transport-s3が存在しているのだがあまりメンテナンスされている様子がなく、またpythonpython-configobjに依存しているのでコンテナから使いづらい。ECSのタスクロールにも対応してなさそう。

あと作ってから気がついたがapt-golang-s3というのもあるが、こちらはソースコードからのインストールがやや手間そう。apt-transport-s3同様にECSのタスクロールに対応していなさそう。

という感じで主に

  • GitHub ActionsでのDockerイメージの作成時のパッケージインストール(OIDCのクレデンシャルの利用)
  • ECS上のコンテナでのパッケージインストール

にS3を利用するために作成した。

※なおS3のバケットに置くリポジトリのファイルはaptlyなどで作成する必要がある*1

使い方

まずS3のリージョンを設定する。

echo 'Acquire::s3::region ap-northeast-1;' > /etc/apt/apt.conf.d/s3

# リージョンはURIに組み込みたかったが良い文法が思いつかなかった

それからsources.listを作成する。

echo 'deb s3://my-bucket/repo/ xenial main' > /etc/apt/sources.list.d/s3.list

これであとはapt update apt installすればS3からパッケージをインストールできる。

なお、LoadDefaultConfig以外でのクレデンシャルのロードは、現状やっていない。

aws.github.io

おまけ: APT Method Interface

APT Method Interfaceというaptコマンド?とapt transport?とのインタフェースの仕様があって、これに従ったプロトコルでapt↔apt transportでメッセージをやりとりする。

標準入出力でやりとりする HTTP/1 みたいなテキストのプロトコルで、標準入力から

601 Configuration
Config-Item: Acquire::http::Proxy=http://example.com

というメッセージを受け取ったら設定を行い

600 URI Acquire
URI: s3://example.com/key
Filename:Packages.downloaded

というメッセージを受け取ったらを行い、最後に

201 URI Done
URI: s3://example.com/key
Filename:Packages.downloaded

というメッセージを返すというなかシンプルなプロトコルでわかりやすかった。

*1:www.aptly.infoが落ちている…(2022/02/26現在)リリースも滞っている感じ

Golang: pkg/errorsのスタックトレースをきれい表示するやつとDatadogのエラートラッキング

三行で

  • pkg/errorsのスタックトレースが見にくい
  • DatadogのError Trackingが見にくい
  • まあまあなんとかするやつを作った

pkg/errors

Goのスタックトレース事情については、以下を参照のこと。

zenn.dev

現状では標準ライブラリにスタックトレースが実装されていないので、とりあえずpkg/errorsを使っている*1が、スタックトレースの表示がめちゃくちゃ見にくい。

package main

import (
    "fmt"
    "os"

    "github.com/pkg/errors"
)

func main() {
    err := f1()
    fmt.Printf("%+v", err)
}

func f1() error {
    return errors.Wrap(f2(), "from f1()")
}

func f2() error {
    return errors.Wrap(f3(), "from f2()")
}

func f3() error {
    _, err := os.Open("not_found")
    return errors.Wrap(err, "from f3()")
}

gist.github.com

なのでもう少しきれいに表示するライブラリを作った。

github.com

pperr.Print(err)

以下のように表示される。

gist.github.com

若干重い処理をしているが、エラー発生時は仕方ないということにした。

Datadogのエラートラッキング

DatadogはAPMに統合されたエラートラッキング機能を提供していて、Golangにも対応している。 APMの1リクエストに紐付いたエラーを表示できるが、スタックトレースの抽出が貧弱であんまりイケてない。

   router.HandleFunc("/hello", func(rw http.ResponseWriter, r *http.Request) {
        span, _ := tracer.SpanFromContext(r.Context())
        err := f1()

        if err != nil {
            rw.WriteHeader(http.StatusInternalServerError)
            span.SetTag(ext.Error, err)
        } else {
            fmt.Fprintln(rw, "hello")
        }
    })

上記のようにspan.SetTag(ext.Error, err)するとこのような表示になる。

f:id:winebarrel:20220220174212p:plain

ext.ErrorDetailsにpkg/errorsのスタックトレースをセットしているが、コンソールに表示されるのはtakeStacktrace()のほうらしい。 なので直接ext.ErrorStackを設定する。

           span.SetTag(ext.Error, err)
            span.SetTag(ext.ErrorStack, pperr.Sprint(err))

一応、スタックトレースが表示されるが、インデントが潰れてあんまり見やすくない。

f:id:winebarrel:20220220175441p:plain

なのでインデントを全角スペースするという微妙なことをする。 さらにErrorTypeとして*errors.withStackが表示されてもうれしくないので、直近のcause error typeを表示するようにする。

            span.SetTag(ext.Error, err)
            span.SetTag(ext.ErrorStack, pperr.SprintFunc(err, pperr.NewPrinterWithIndent("    ")))
            span.SetTag(ext.ErrorType, pperr.CauseType(err))

これでまあまあ見やすくなったと思う。

f:id:winebarrel:20220220175834p:plain

ちなみにext.ErrorTypeスタックトレースをセットするというさらによろしくなさそうなことをすると、全角スペースは不要になる。

           span.SetTag(ext.ErrorType, pperr.Sprint(err))

f:id:winebarrel:20220220180058p:plain

エラー表示の機能的にはSentryと大きな違いはなさそうだけれど、UXがだいぶ劣っているのでDatadogにはもう少し頑張って欲しい。 (フィードバックしたらなんとかなるだろうか)

*1:アーカイブ済みなのは把握しているが、代替を見つけられていないので…