AWSの話題を中心に、日々の業務やプログラミングの徒然を綴るエンジニアブログです。

HANDS LAB

HANDS LAB ENGINEERS BLOG

ハンズラボエンジニアブログ

ヘテロジニアスデータベースによるECサイトの在庫管理

Pocket

この記事は、ハンズラボ Advent Calendar 2019 8日目の記事です。

ヘテロジニアスってかっこいい単語だなーと思ったので使ってみました。弊社ECサイトhands.netの在庫管理はDynamoDBとAurora MySQL(以下、Aurora)の2種の異種データベースを利用しています。
CQRSです、と言えるとかっこいいのですが、業務要件と歴史的経緯でこの様になっています。

前提として、hands.netの在庫は主に東急ハンズ新宿店の店頭の商品を在庫としています。他に、メーカーからの直送商品や、後述するハンズメッセ用に外部で倉庫を借りて使うケースもあるのですが、煩雑なので割愛します。
そして、東急ハンズは外部モールへの出店も数多く行っています。
Yahooショッピング、楽天市場、Ponpareモール、Amazonマーケットプレイス、au Wowma!などです。こちらとも在庫数を同期する必要があり、各モールのAPIを使用して注文を取得、在庫を連携する、ということをしています。

まずはAuroraの方から。
日次で、全店の帳簿在庫数を基幹システムからもらってきます。
1時間に数回、基幹システムから当日の売れ数と仕入れ数をもらってきます。帳簿在庫数から売れ数を引き、当日の仕入れ数を足し込んで在庫数を計算します。
更に、店頭の見本の分や、ECサイト・外部モールで注文が入ってまだ出荷されていない分を引き、新宿店分を抜き取ってネット在庫テーブルを作ります。

そしてDynamoDBへ。
Auroraのネット在庫テーブルの在庫数をDynamoDB側のネット在庫テーブルに同期します。
で、hands.netで注文が入ったら、まずはDynamoDB側のネット在庫テーブルから在庫数を引きます。
その上で、Aurora側の注文テーブルに書き込みます。
Aurora側はhands.netだけでなく他の外部サイトの分も商品ごとの注文数を集計、未出荷在庫数を計算し、最新の新宿店の店舗在庫数から再度ネット在庫数を計算して、改めてDynamoDB側のネット在庫テーブルと同期します。

なんでこんなことをやってるか、というと理由は2つあります。
1つ目は、ハンズメッセで売り越しを発生させないため。
2つ目は、注文処理の途中で決済エラーが発生した場合に、速やかに在庫数を戻すためです。
順に説明します。

ハンズメッセは、夏に行う年次のセールで、セール期間の1週間で年間の10%くらいの売上が発生します。当然トラフィックが通常とは全く違うものになります。
売り越し、というのは在庫数以上の注文を受けてしまうことです。これが発生してしまうと、お客様へのお届けに通常より長い時間がかかってしまうため、避けねばならない事態です。
通常であれば、Auroraで注文数からネット在庫数を計算してDynamoDBに同期するのにかかる時間は数秒〜十数秒ですが、ハンズメッセ時のトラフィックではこの数秒の間に注文が多数入るため、在庫数が巻き戻ってしまい、売り越しにつながってしまいます。
そのため、ハンズメッセでも特にトラフィックの大きい時間帯はAuroraとDynamoDBの在庫同期を止め、DynamoDB側のみでネット在庫数を計算することで高トラフィックをさばくようにしています。その間は外部モールでは販売ができなくなってしまいます。ハンズメッセ期間中に外部モールへの出品が一時的に停止する日があるのはこんな理由からです。

注文処理の途中でエラーになる原因は複数あります。クレジットカードの期限切れや、決済事業者のメンテナンスなどです。この際に、ネット在庫数を素早く戻せないと、売り逃しにつながってしまいます。
ですので、注文処理の最初の方でネット在庫数を減らしたあと、途中でエラーになったらすぐにネット在庫数を足し込んで戻す必要があります。
このため、DynamoDB側はネット在庫テーブルとネット在庫差分テーブルに分かれており、ネット在庫差分テーブルでは注文処理ごとにIDを振って、当該IDの注文で決済エラーが起きたらそのID分を削除しています。ネット在庫数への加算で戻しすぎて在庫が誤って増えてしまう、という事態を防いでいます。

このように、2種の異種データベースを用いて高負荷時にも売り越しを発生させず、かつ平常時含めて売り逃しを最小限にするための構成を実現しています。

Pocket