---
# System prepended metadata

title: チームA b3m1 最終発表

---

# チームA b3m1 最終発表


# まなと

## 概要
30000rpsを捌く計測サーバー **hakaru** を作成するために、現状の課題の確認、改善を行った。開発を円滑に進めるため、ロギング改善や自動デプロイなどの作業も進めた。
その他、DBとアプリケーションの設定やハンドラ処理の変更などを行い、無事30000rpsを達成することができた。

## 最初の状態　
1000rps 15sec
error rate 50% を超えるとテストが終了する
![](https://i.imgur.com/D5eF56L.png)
loader.ioのテストが最後まで実行されず、
はじめのうちは200を返せていたが、実行時間が長くなるにつれ500sエラーを多く返すようになった

CloudWatchを確認したところ、アプリケーション側で
`http: panic serving 10.1.3.59:34482: Error 1040: Too many connections`
のエラーが大量に発生していた

## 課題
`http: panic serving 10.1.3.59:34482: Error 1040: Too many connections`エラーとタイムアウトによるテストの強制終了

## やったこと
### Too many connectionsの調査
DBの同時接続数の上限を超えるとRDSが出す
Too many connections が発生すると、データをDBに保存することができずhakaruが500エラーを返す

### 原因調査
RDB側で同時接続数の最大値を保持しているので確認

``` sql
show variables like "%max_connections%" -> 65
```
手軽に実行できそうなためRDB側のmax_connectionsを増やそうとしたが、[ドキュメント](https://aws.amazon.com/jp/premiumsupport/knowledge-center/rds-mysql-max-connections/)に非推奨とあったため断念

## 結果
> max_connections の値をデフォルト値より大きくすることはできますが、ベストプラクティスではありません。これは、ワークロードが増加し、より多くのメモリが必要になると、インスタンスに問題が発生する可能性があるためです。接続数を増やすと、メモリ使用量が増加する可能性があります。メモリが不足しているインスタンスは、クラッシュする可能性があります。

→ 非推奨のため、RDB側でmax_connectionsを操作しない

# たくみ

## やったこと
### Open関数をハンドラの外に　
[ドキュメント](https://pkg.go.dev/database/sql#DB)によると、Openはアプリケーションで一度だけ開けば良い
リクエストのたびにOpenしていた実装を一度だけ実行するように変更

**テスト結果**
1000rps 15sec
![](https://user-images.githubusercontent.com/35529908/197369482-1b29a3c4-d5c8-4878-8c7d-b57cd81f2f64.png)
- はじめの状態ではテストがAbortしていたが、最後まで実施できた
- まだタイムアウトと500エラーは残っていた

### ロギング改善　
現状のエラーログではGoのコードのどこでハンドリングしたエラーかの特定ができない
-> エラーログの文言をこちらで設定

# いなたつ
### 自動デプロイ　
デプロイ手順が煩雑なことや、手元の環境に依存するため、デプロイするにあたってバージョンのミスなどが考えらるためテストを行う度に手動でデプロイ作業を行うのは非効率的であると考えた、そのためデプロイ自動化による効率化を図る。


デプロイ自動化の方法として、github actions の設定ファイルがリポジトリにあったため活用し、S3へのアーティファクトのアップロードをmainにpushされた時にgithub actionsを用いて自動的に行うようにする。

以下の権限を与えたIAMユーザーを作成し、トークンをgithub actions の secretへと追加し、自動アップロードを行なった。
- S3 ListBucket
- S3 GetObject
- S3 PutObject

# たくみ
### アプリケーション側のDB設定の変更　
Go側の仕様ではmax_connectionsの設定はデフォルトでは無制限だった
https://pkg.go.dev/database/sql#DB.SetMaxOpenConns
> if n <= 0, then there is no limit on the number of open connections. The default is 0 (unlimited).

-> DBの設定と合ってない

- `db.SetMaxIdleConns(n)` 
- `db.SetMaxOpenConns(n)` 

より効果がありそうな`db.SetMaxOpenConns(30)`をdb.closeの後に追記
(数値は他グループを参考にした)

**テスト結果**
- 1000 rps では500エラーが発生しなくなった
- Too many connectionsも発生せず
- タイムアウトは健在

# りんりん

## 課題　
- loader.ioとDBのレスポンスの合計が合わない
redashで確認したInsertされた数より、Successが返った数が少ない

→ALBのリクエスト数を確認したところ、サーバーはすべてのリクエストに対してレスポンスを返していて、DBにも保存されているため、それほど重要な問題ではないと判断

- 今後の方針決め

処理のどの部分に注力するか、決める

## やったこと
## 重い処理の同定
### 目的
方針を決めるために実行時間を測定する。
その集計に用いるクエリの作成とログの構造化を行う。

### 行ったこと

- ログの構造化 https://github.com/VG-Tech-Dojo/hakaru2022-a/pull/14
クエリにて集計するため
ログをJSON形式で出力した。

- log's insightでのクエリ作成
実行時間を測定するため、クエリを作成した。
→ **実行時間の大半がExec部分であることが判明**

# 　いなたつ
## やったこと
### Goの理解
バルクインサートの要素であるキューイングのテスト
（channel、goroutineを使用）

## キューイング、バルクインサート

### 課題
RDBへのinsertがリクエスト毎に実行されており、
アプリケーションとDBのネットワークを貼る時間が大きなボトルネックになっている

### 解決策
DBへのネットワークを張る回数を減らさなければいけない
→ まとめてinsertする


### 実装案
キューイングの実装で非同期にDBに書き込む処理を行なったので、
それを一度にまとめて書き込めるようにする

sqlxを用いる
現状のドライバーを使ってもまとめてinsertすることはできた。

insertするためのクエリを文字列を結合して作成することで実装できるが、
SQLインジェクションの対策を自身で検討する必要があるが、時間がない

sqlxというパッケージに差し替えて実装

採用理由
- まとめてinsertするための関数が用意されている
- SQLインジェクションの対策がなされている

#### 挿入条件
- キューに100個のeventが溜まる
- 1秒経過

30000rps 1min
![](https://i.imgur.com/0I6mrgI.png)

問題なく捌けている


捌けるリクエスト数を増やすという新たな目標をもとに、検証を行なった

50000rps 20sec
![](https://i.imgur.com/CIGFMC5.png)

現状は100個のイベントが集まったら流していたが、それを10000個に変更


```
http: Accept error: accept tcp [::]:8081: accept4: too many open files; retrying in XXms
```
