認証サーバーの負荷テスト再び
あの後、認証サーバーの負荷テストがなぜリクエストの頻度が落ちた後もエラー率の高いまま推移し、回復しなかったのかサーバーのログを見てみました。
すると、認証サーバーに登録されたアカウント番号が既にトークンを発行しており、そのトークンを認証サーバーが所持している場合は既存のトークンを削除した上で、
新たなトークンを発行するようなシステムだったのですが、アクセスが集中することでトークンの削除がうまくいかなくなると、それ以降の処理が全てエラーになってしまっていることが発覚しました。
具体的に、Djangoのトークン発行の箇所だけを抜き出してコードを以下に示します(バグがあったものです)。
@staticmethod def create(user: UserAccount): # ユーザの既存のトークンを取得 if UserToken.objects.filter(account_no=user.account_no).exists(): # トークンが既に存在している場合は削除する UserToken.objects.get(account_no=user.account_no).delete() # トークン生成(メールアドレス + パスワード + システム日付のハッシュ値とする) dt = timezone.now() original_str = str(user.account_no) + dt.strftime('%Y%m%d%H%M%S%f') hash_str = hashlib.sha1(original_str.encode('utf-8')).hexdigest() # トークンをデータベースに追加 token = UserToken.objects.create( account_no=user.account_no, token=hash_str, access_datetime=dt) return token
この、
if UserToken.objects.filter(account_no=user.account_no).exists(): # トークンが既に存在している場合は削除する UserToken.objects.get(account_no=user.account_no).delete()
コードの部分だけで、重複したトークンを保存しないようにしていたわけですが、トラフィックが増えてきて負荷が掛かると、ある時点で削除に失敗してしまい、
次からは削除しようにも、複数のデータが存在する事を示すエラーを出力して500 Internal Server Errorとなっていました。
この部分を次のように置き換えることで、トラフィックが増えて負荷が上がっても、エラーを起こすことなく最後まで負荷テストを終えることが出来るようになりました。
# ユーザの既存のトークンを取得 if UserToken.objects.filter(account_no=self.account_no).exists(): # トークンが既に存在している場合は削除する UserToken.objects.filter(account_no=self.account_no).delete() # トークン生成(メールアドレス + パスワード + システム日付のハッシュ値とする) dt = timezone.now() original_str = str(self.account_no) + dt.strftime('%Y%m%d%H%M%S%f') # utf-8でエンコード hash_str = hashlib.sha1(original_str.encode('utf-8')).hexdigest() # トークンをデータベースに追加 token = UserToken.objects.create( account_no=str(self.account_no), token=hash_str, access_datetime=dt) try: user_account_update = UserAccount.objects.get(account_no=self.account_no) user_account_update.update_access_datetime() except(): print("トークンを発行したユーザーの最終アクセス日時をアップデートできませんでした。") return token </span>
負荷テストの結果は以下の通りです。