2024.10.01
自社の社内情報を未来の“ゴミ”にしないための備え 「情報量が多すぎる」時代がもたらす課題とは?
POSレジとGo@1019Hiroya(全1記事)
リンクをコピー
記事をブックマーク
照井寛也氏(以下、照井):「POSレジとGo」というタイトルでさっそく発表します。今回話す内容ですが、チャネルとgoroutineを、実際のビジネスロジックでどのように使っているかの事例の共有と、そこから得た学びを共有します。
アジェンダとしてはこのようなかたちになっています。まず自己紹介をし、Showcase Gigが提供する次世代店舗プラットフォーム「O:der(オーダー)」とPOSレジの関係性、そこからチャネルとgoroutineを用いたPOS連携開発について話します。あとはそれらを開発実装したうえでの学びを共有します。
というところで、さっそく自己紹介にいきます。照井寛也と言います。Showcase Gigには2021年2月よりジョインし、POS連携サービスのサービスのバックエンドエンジニアを担当しています。
この会社に入ってからGoを実際に実務として書いたので、Go歴は半年ちょっとではありますが、ありがたいことに、いろいろな機能追加、特に、POS連携サービスの機能追加や設計を行えています。今回はそこのPOS連携の機能追加に関してちょっとお話します。
さっそく「O:der」とPOSレジについて説明に入ります。「O:der」との関係性については、POSレジを紹介した後に説明します。
まずPOSレジですが、みなさん、どうでしょうか。POSレジというものを聞いたことがあったり、馴染みある方はいますでしょうか。ちなみに自分はこの会社に入ってから、やっと仲良くなれたような気がしています。
役割としては、「何の商品を・いつ・いくらで・何個販売したか」という、いわゆる売上管理システムだったり、販売管理システムをPOSシステムと言っていて、そこと連携しているレジになります。また「何の商品を・なんていう名前で・いくらで売ってるか」という、マスタを管理してたりします。
イメージとしては、飲食店によくある、いわゆるあのレジです。(それと)このPOSシステムが連動してるものというところになっています。種類としてはパソコン型だったり、ターミナル型、あと最近タブレット型もよく目にすることがあります。
このPOSレジが、「O:der」とどういう関係性かというところですが、双方向に関係性があります。このPOSレジからマスタを「O:der」に同期するところと、あとは「O:der」からの注文や会計を、POSレジに連携している関係性があります。
今回は、それらの間に立っている、POS連携サービス、自分が所属しているチームで開発していますが、そこについてお話しします。
さっそくGoを用いたPOS連携に入っていきます。今回は特に、POSレジからのマスタを「O:der」に同期するところの部分に関して、追加実装した部分があるので、そこの事例を紹介させていただきます。
では、どういうところ追加実装したかです。もともとマスタ同期という機能は「O:der」にありました。どういう機能かというと、店舗ごと個別にマスタ同期をしていきます。同期のボタンをポチポチと押していくことによって、POSレジにあるマスタを「O:der」に対して同期をする機能になっています。
ただ、店舗数が100店舗、200店舗あったり、チェーン店になればなるほど店舗の数も大きくなっていくので、1個1個に対して同期のボタンをポチポチ押していくのが正直こうしんどいです。そのため、店舗一括でマスタを同期させる機能を作ろうということになりました。
今回は「店舗一括でマスタ同期する」部分にフォーカスを当ててお話しします。
先ほどからマスタ、マスタと言っていますが、「マスタってなんですか?」となります。「O:der」にはカテゴリーとメニューという概念があります。
カテゴリーに対してメニューが紐づいているので、スライドでいうと、「冷菜」に対して「カルパッチョ」や「生ハム盛り合わせ」が紐づいています。
カテゴリーは店舗横断で行われているので、どの店舗でも同じカテゴリーが横断して存在します。メニューは例えば地域によって、東北地方のメニューだったり、店舗個別のメニューだったり、店舗ごとに異なる関係性があります。
このマスタを同期する、仕様はどんなかたちになるでしょうか。まず、マスタ同期のリクエストはリクエストごとに排他制御がされている必要があります。
リクエストが成功したら202のacceptedを返して、実行はgoroutineで行う。既に他のリクエストによって実行中であれば、409のconflictedを返すのが、まず仕様の1個目としてあります。
次は、先ほどマスタ、マスタと言っていた、このマスタ同期のカテゴリーとメニューを、それぞれ同期するところになります。
では、ここからは実際にどのようにマスタ同期するかのフローを図解して見ていきます。まず、カテゴリーに対してメニューが紐づくかたちなので、順番としてはカテゴリー、メニューの順で同期する必要があります。
続いて、マスタ一括同期なので、複数店舗同時に実行する必要があります。直列でどんどん処理を走らせていくこともできますが、100店舗、200店舗と店舗が多くなると処理時間もどんどん長くなっていくところがあるので、そこは同時にやる必要があります。
というところで仕様をまとめると、成功するパターンは、「O:der」からリクエストが来たら202のacceptedを返して、その後にカテゴリー同期、メニュー同期という順番で同期が走っていきます。
一方、すでに実行中だった場合は、リクエストに対して409のconflictedをPOS連携サービスが返すかたちになっています。
では、ここからは、今の見たビジネスロジックをどうGoで実装していくかを見ていきます。
まず、マスタ同期の仕様としてあるカテゴリー同期、メニュー同期の順で実行する必要があるところです。
こちらに関しては、チャネルを用いてカテゴリー同期が終わり次第、メニュー同期の実行をしていくのが実装のかたちとしてあります。
ここからちょっとコードを見ていきます。スライドのようなかたちでチャネルを使っていきます。
まずcategorySyncとmenuSyncというところで、カテゴリー同期、メニュー同期があります。まず、カテゴリー同期が終わった後にdeferでチャネルを送信します。menuSync、メニュー同期のほうはチャネルを受信するのを待ち受けて、送信されたチャネルを受信したら、メニュー同期の処理を行う実装に落ち着きました。
もう1つ、複数店舗同時に同期する必要があります。こちらはその名のとおり、goroutineで並列実行を行っていきます。
実際にどういうコードになるかというと、店舗ごとにgoroutineをどんどん走らせていくというかたちです。
以上をまとめると、カテゴリー同期をまず走らせて、その後にメニュー同期を店舗ごとにgoroutineで走らせていくかたちを採ります。カテゴリー同期が終わり次第チャネルを送信するので、受信したメニュー同期がどんどん実行されていくかたちを取ります。
実装したところで、学びのセクションに入っていきます。実装できたので、レビューを貰いに行ったんですが、処理の中でこのアトミックではない部分が実は存在してました。この「アトミックではない」というところ、自分も初めて聞いたのでちょっとググるところから始まったんですが。
アトミック、不可分性といわれるものになっていて、いろいろ書いていますが、「リクエストは、他のリクエストが何をしているか観測ができない」ところが、「アトミックである」部分になっています。
実際にどこかというと、先ほど冒頭でお伝えした仕様の、すでに実行中の場合、409のconflictedを返すところです。ここが仕様を守れていませんでした。
実際どういうところかをフローで見ていきます。同期を始めてロックの確認をして、ロック中であれば409のconflictedを返して、ロック中でなければそのまま処理を進めていくかたちを採っていますが、よく見ると、このロックの確認と取得までに間が空いてしまっています。
これによって何が起こるかというと、複数リクエストが走った場合、リクエストAだとまずロックの確認をしますが、その場合はまだロックがかかっていないので、処理が進んでいきます。
リクエストAがロックを取得する前にリクエストBのほうでロックを確認されると、ここでもリクエストBがロックがかかっていないと判断されます。なので、本当はリクエストBが409を返さないといけないところですが、リクエストAとリクエストBが共に202を返してしまうので、排他制御がされていないということになりました。
ではロックの確認と取得を確認した直後に取得すればいいんじゃないかというところで、ロジックを変えられるとは思っています。しかし、ここでも確認と取得、つまりselectとupdateの間に入られる可能性は0ではないです。
こちらはすでに他のいろいろなコードでも利用されているロジックを使いました。stateのfromとtoがあると思いますが、そちらを利用することにしました。
これをすると、例えばロックを解放されている状態から取得する状態の遷移を指定できるので、「どこからどこまで」の状態を持てます。selectの部分でロックを確認した時にロックがかかっていなかったとして、updateをかけにいった時に、すでに他のものによってロックがかかっている場合は、「更新行がなし」として、affectedの部分が1ではなくなります。つまり他のプロセスで変更されたと判断できるようになります。
不可分性、アトミックな部分を担保したうえで、どんどん負荷試験などを行いましたが、同時並列数が多すぎると高負荷になる、こちらが観点として抜けてました。
これどういうことかというと、POSレジと「O:der」に対してそれぞれリクエスト、POSレジに対してはマスタの取得、「O:der」に対してはマスタの更新というリクエストを送りますが、自分がgoroutineで同時並列に流していたために、短期間にリクエストが集中することが起こりました。
そのため、リクエストを分散させる、いわゆるtimeSleepを使うことによってリクエストを分散させ、POSレジ、「O:der」それぞれに対して負荷を分散させることを実現しました。
最後まとめにいきます。まず実装をとおしての学びです。自分がGoを学ぶ中で、チャネルだったりgoroutineはけっこう知っていましたが、それをどうビジネスロジックに当てはめて実装するかはあまりイメージできなかった部分があったので、ここは実務経験をとおしてできた初の経験だったと思っています。
また、チャネルというところで、struct{}を用いることによってメモリが削減になるTIPSも1つ学びとしてありました。
ロックの確認・取得・開放ですが、ここはやはりアトミックな部分を意識して今後も行いたいなと思っています。ロックだけではなくDBの値、つまり、テーブルの値の奪い合いだったり、更新がどんどんいろいろなリクエストでされてしまって、値がおかしくなるところは気をつけていきたいと思っています。
最後に負荷分散です。自分がやってるPOS連携サービスだからこそ、連携する元と連携する先、それぞれに対して優しいサービスとなりたいところ。負荷が一気にかかりすぎないようにというところが、この実装をとおして学んだことになっています。
自分からは以上です。ご清聴ありがとうございました。
関連タグ:
2024.10.29
5〜10万円の低単価案件の受注をやめたら労働生産性が劇的に向上 相見積もり案件には提案書を出さないことで見えた“意外な効果”
2024.10.24
パワポ資料の「手戻り」が多すぎる問題の解消法 資料作成のプロが語る、修正の無限ループから抜け出す4つのコツ
2024.10.28
スキル重視の採用を続けた結果、早期離職が増え社員が1人に… 下半期の退職者ゼロを達成した「関係の質」向上の取り組み
2024.10.22
気づかぬうちに評価を下げる「ダメな口癖」3選 デキる人はやっている、上司の指摘に対する上手な返し方
2024.10.24
リスクを取らない人が多い日本は、むしろ稼ぐチャンス? 日本のGDP4位転落の今、個人に必要なマインドとは
2024.10.23
「初任給40万円時代」が、比較的早いうちにやってくる? これから淘汰される会社・生き残る会社の分かれ目
2024.10.23
「どうしてもあなたから買いたい」と言われる営業になるには 『無敗営業』著者が教える、納得感を高める商談の進め方
2024.10.28
“力を抜くこと”がリーダーにとって重要な理由 「人間の達人」タモリさんから学んだ自然体の大切さ
2024.10.29
「テスラの何がすごいのか」がわからない学生たち 起業率2年連続日本一の大学で「Appleのフレームワーク」を教えるわけ
2024.10.30
職場にいる「困った部下」への対処法 上司・部下間で生まれる“常識のズレ”を解消するには