
目次
「ボタンを1回押しただけなのに、データが2行書き込まれていた」——AppSheetとGASを組み合わせた仕組みで、こうした相談を受けることがあります。
AppSheetのBotからGAS(Google Apps Script)を呼び出す構成は、現場アプリと自動処理をつなぐ便利な方法です。ただし実案件(匿名)では、BotがGASを多重に呼び出すことで、同じ処理が二重に走る事態が起きました。請求や在庫のような業務でこれが起きると、数字が合わなくなります。
この記事では、呼び出し側ではなく受け側のGASで重複を止める設計と、途中で失敗した方式から学んだ設計原則を整理します。
この記事の結論
- 外部から呼ばれるGASは、多重に呼ばれる前提で作る
- 本データにステータス列を持たせ、入口で処理済みを判定する
- 状態は一時データではなく、本データに持たせる
冪等性とは:何度呼ばれても結果が変わらない性質
冪等性(べきとうせい)とは、同じ処理を何度実行しても、結果が1回実行したときと変わらない性質のことです。
たとえば「この申請を承認済みにする」という処理は、2回実行しても状態は同じ「承認済み」です。一方、「明細を1行追加する」処理は、2回実行すれば2行になります。後者を外部から呼ばれる形で作ると、呼び出しが重なったときに二重書き込みが起きます。
外部から呼ばれるGASでは、「呼び出しは必ず1回だけ来る」と考えないことが出発点です。多重に来ても困らないよう、受け側で重複を吸収する設計にします。
GASの二重実行の原因:呼び出し側を信じすぎる設計
今回のケースでは、AppSheetのBotがGASを多重に呼び出しました。GAS側は「呼ばれたら処理する」作りだったため、来た回数だけそのまま書き込んでいました。
ここで大事なのは、呼び出し側の挙動を完全には制御できないという前提です。Botの設定や通信の状況など、受け側から見えない要因で呼び出しが重なることはあり得ます。呼び出し側を直して終わりにすると、別の要因で同じことが再発します。なお、AppSheetやGASの仕様は変わるため、最新の挙動は公式ドキュメントで確認してください。
だからこそ、最後の砦は受け側のGASに置きます。
失敗例:仮レコードで処理中を表示した結果、3つの障害が起きた
対策を固める前に、一度うまくいかなかった方式があります。「処理中です」と画面に表示するために、UI用の一時レコード(仮レコード)を作る方式です。処理が始まったら仮レコードを作り、終わったら消す。一見わかりやすい作りですが、運用すると次の3つの障害が起きました。
| 障害 | 現場で起きたこと |
|---|---|
| 表示消失 | 処理中の表示が途中で消えてしまう |
| 無言空振り | 処理が実行されないのに、エラーも出ない |
| 二重処理 | 防ぎたかった重複が、結局起きる |
根本の原因は、状態が2か所に分かれていたことです。本データと仮レコードという別々の場所で「いま処理中か」を管理していたため、両者がずれた瞬間に表示と実態が食い違いました。表示のための仕組みが、かえって状態管理を複雑にしていたのです。
二重書き込みの対策:本データの処理済みフラグで判定する
この反省から、対策を2つに絞りました。
- 本データにステータス列を持たせ、入口で判定する:GASは処理を始める前に、対象データのステータス列を確認します。すでに処理済みなら、何もせず終了します。
- 状態は本データのフラグ列で管理する:処理中・処理済みの表示も、仮レコードではなく本データの列を見る形にします。
こうすると、2回目以降の呼び出しは入口で止まり、二重書き込みが起きません。状態の置き場所が1つなので、表示と実態がずれる余地もなくなります。
処理済みフラグ設計の3原則
最終的に、次の3つを設計原則としました。
1. 本データのフラグ列で状態を持つ
状態の「正」は本データ1か所だけにします。画面表示もGASの判定も、同じ列を見ます。ここを分けると、先ほどの3障害が再発します。
2. 時間ベースで復帰できるようにする
処理が途中で止まると、フラグが「処理中」のまま残り、二度と処理されない状態になり得ます。そこで、一定時間が経っても完了しない場合は再実行できる状態に戻す、時間ベースの復帰を組み込みます。重複を止める仕組みが、正しい再実行まで止めてしまわないための安全弁です。
3. GASから書き戻しシグナルを返す
処理が終わったら、GASが本データのフラグ列に完了を書き戻します。AppSheet側はその列を見るだけで、処理の結果を把握できます。呼び出し側が「たぶん終わっただろう」と推測する必要がなくなります。
持ち帰る判断軸:状態は一時データではなく本データに
今回の学びを一言にまとめると、状態は本データに持たせるです。
表示のため、判定のためと目的ごとに一時データを増やすと、そのたびに「どちらが正しいか」という問題が生まれます。逆に、状態を本データの1列に集めれば、GASの判定も画面の表示も、同じ事実を見るだけで済みます。
外部から呼ばれる仕組みを作るときは、「この状態はどこに置くか」を最初に決めてください。それだけで、二重処理や表示のずれといったトラブルの多くを設計段階で防げます。
よくある質問
Q. GASの冪等性とは何ですか? 同じ処理を何度呼び出しても、結果が1回分と変わらない性質のことです。外部から呼ばれるGASは多重に呼ばれる前提で作り、入口で処理済みかを判定して、二度目以降は何もせず終える設計にします。
Q. AppSheetのBotからGASが二重に実行されるのを防ぐには? 受け側のGASで止めるのが確実です。本データに処理済みを表すステータス列を持たせ、GASの入口でその列を確認し、処理済みなら終了します。呼び出し側の挙動に頼らず、受け側で重複を吸収する考え方です。
Q. 処理中の表示には一時レコードを使ってもよいですか? おすすめしません。実案件では仮レコード方式で、表示消失・無言空振り・二重処理という3つの障害が起きました。状態は本データのフラグ列で管理し、表示もその列を参照する形にすると、データ同士のずれが起きにくくなります。
まとめ
- 外部から呼ばれるGASは、多重に呼ばれる前提で作る
- 本データにステータス列を持たせ、GASの入口で処理済みを判定する
- 仮レコードで状態を持つと、表示消失・無言空振り・二重処理が起きた
- 設計原則は、本データのフラグ列・時間ベースの復帰・GASからの書き戻しシグナル
- 判断軸は、状態は一時データではなく本データに持たせること
RENOYでは、AppSheetやGASを使った業務アプリ・自動化の設計から、運用で困らない状態管理の見直しまでお手伝いしています。「ときどきデータが二重になる」「処理が終わったか分からない」といった段階のご相談も歓迎です。まずはお気軽に無料個別相談(オンライン・60分)をご利用ください。サービス内容をまとめた資料は資料請求からご覧いただけます。
