見出し画像

GAS失敗談「ほぼ同時にフォーム送信されたらダメだった話」

Google Apps Script (GAS)を、もっと身近に、日々の暮らしに。

ちょうど前回の【GAS活用術⑦-2】を公開した日、私が作成した読み聞かせシステムで、予期せぬ動作・誤作動が発生しました。

実は、前からこの作りではまずいかも、、と危惧していたのですが。。

せっかくなので、この話(ネタ?)を、GAS失敗談として書いていこうと思います。


ダメだったシステムの作りと心配だったこと

私がググりながら見よう見まねで作った、読み聞かせの記録システムでは、Googleフォームから送信のタイミング(「フォーム送信時」のトリガー)で、以下のすべての処理をスプレッドシート側のGASで行っていました。

①LINE送信
スプレッドシートの最終行のデータを読み、管理者である私にその内容をLINE送信する

②文字列型(String)から日時型(Date)に変換後、日付データを設定
スプレッドシートの最終行の、文字列型(String)になっている日付データを、日時型(Date)に変換して書き込む(参照:【GAS活用術⑦-2】

③並べ替え(ソート)
【GAS活用術③】で紹介したように、1件登録されたらシート全体の並べ替え(ソート)をする

図にするとこんな感じ

なぜ①と②で「スプレッドシートの最終行」かというと、フォームから送信されたデータはスプレッドシートの最終行に書き込まれるからです。

ググってみると、スプレッドシートの最終行を、getLastRow()で取得するサンプルコードが見つかったので、それを真似たのですが、、、

複数行が同時にフォームから送信されたり、その後、データを並べ替えしちゃったりするけど、これで大丈夫???

というのが私の心配でした。

結局、システム稼働から数か月間は、フォームから同時に送信されることはなく、問題なく動作していましたが、ついに先日、その心配が現実化したわけです。

事象と推測される処理の流れ

「何が起きたか」を言葉で説明すると冗長になるので、図を使って説明してみます。

まず、以下のように、データAとデータBが、ほぼ同時にフォームから送信され、スプレッドシートに書き込まれた、とします。

で、事象から推測するに、どうやら下記のような流れで処理が行われたようです。

まだちょっとわかりづらいので、さらに表にしてまとめてみます。

おわかりでしょうか?文章にしてまとめると、以下のようなことが起きたということです。

  • 登録を知らせるLINE通知が、データBの内容で2通送られてきた(はじめに登録されたデータAの内容は送られて来なかった)

  • データAの行の日付データが日時型(Date)に変換されず、ソート後の最終行の日付(前月の日付)がその日の日付で書き換わっていた

事象だけみると、なんで???何が起きた???と混乱しましたが、要するに、1つデータに対する①~③の処理がすべて終わらないうちに、次のデータの処理が開始されてしまった、ということです。

まずいかも、と気にはなっていましたが、やはり、「スプレッドシートの最終行への書き換え処理とシート全体のソート処理をフォーム送信時に行う」のは、ダメだったことが実証されたのでした。

対応策として

そもそも私の読み聞かせの記録システムの作成方法は、「これができたらいいなー」と思いついては、ネットで調べ、見つけたGASのコードをよくわからないまま真似て、とりあえずやってみる、を繰り返してました。

思いつきで、少しずつシステムの機能追加を繰り返してきたので、全体の最適化ができていない状態でした。

Noteの記事を書くにあたり、もっといい方法がないか、もっとコンパクトなコードにならないか、と自分なりに調べることで、徐々にGASの知識は増えていましたが、システム稼働済みのコードは書き直さずに放置していました。

そんな矢先に発生した事象でした。やっぱりサボっていてはイケないのです。。反省反省。

鉄は熱いうちに打て、とばかりに、さっそくコードの書き換えに着手しました。

ちなみに、「初期段階では知らなかったけど、その後、わかったこと」は以下の通りです。

  • Googleフォーム側にもGASを定義できること

  • フォームの回答を引数eで取得できること

  • 引数eが、Googleフォーム側のGASにも、スプレッドシート側のGASでも使用可能なこと

  • スプレッドシート側のGASに、「e.range.getRow()」と記述することで編集されたセルの行番号が取得できること

こうして挙げてみると、GASに対する理解が深まっている、というか、私、賢くなっている、かも。(単に初期段階で何もわかってなかっただけですが)

で、これらのアップグレードされた知識を元に、以下のように作り直しました。

変更点1. LINE通知のGASをフォーム側に変更

まず、登録される都度、回答内容を送るLINE通知は、フォーム側のGASに変更しました。

スプレッドシート側でも引数eで回答を取得できますが、役割分担というか、負荷分散というか、「フォームの回答を送るなら、フォーム側のGASの方がいい(はず)」と思ったからです。

具体的には、【GAS活用術④-2】で紹介したコードに書き換えました。

変更点2. e.range.getRow()で変更データの行番号を特定

続けて、長いこと謎だった、「フォームからスプレッドシートに書き込まれたデータの行番号を特定する方法」が、「e.range.getRow()」とわかったので、これに書き換えました。

書き換えた後のコードが、【GAS活用術⑦-2】スクリプトにonFormSubmit(e)関数を追加に載っています。

上記1と2を変更した結果、「フォームから送信されたデータとして、スプレッドシートの最終行を読む」という処理がなくなりました。

こうしてみると、改めて、シートに書き込まれたデータをわざわざもう一度読み込む、という変更前の処理は、ずいぶん無駄が多いように思いました。

変更点3. 並べ替えのトリガーを「時間(日付)ベース」に変更

そして、最後に、並べ替えのトリガーを「フォーム送信時」から「時間(日付)ベース」に変更しました。

まず、このように全体を見直してみると、「そもそもフォームから新しいデータが送信される度にデータをソートする必要ないかも。。」と思い始めました。

その日に新しく登録されたデータは一番下の方にまとまっていて、一定時刻にまとめてソートする、で十分だな、と。

あれ、待てよ?シートの最終行でも、e.range.getRow()でも、ソートによって行番号が変わってしまえば、、、結局、誤作動の可能性、、はありますね!?

そう考えると、フォーム送信時にシートの特定の行に対して何らかの処理をするのであれば、ソートはフォーム送信時に行わない方がなさそうです。

実は、私にとって初めてのGASは、この「フォームが送信される度にデータを並べ替える」でした。

固定概念で、当初の設計のまま、ずっとトリガーは「フォーム送信時」でしたが、これらの点を考慮して、ソートは「時間(日付)ベース」に変更することにしました。

システム全体の最適化は大事

おそらくこれで今回のような事象は発生しないはずです。

しかし、こうして、増改築を繰り返して作ったシステムを改めて見直してみると、結構手を入れることになりました。

GASはお手軽に使えますが、やはりシステムなので、全体の最適化は必要だなぁ、と思いました。一度稼働しちゃうと手をいれるのは面倒で、ついつい後回しになっちゃいますけど。

さて、これまでGAS活用術として、主に単発の技を紹介してきましたが、次回からは少し趣向を変えて、GAS活用システムを紹介していきたいと思います。

これまで紹介してきた技を組み合わせて、こんなシステムが作れます、という話ですね。

ようやくテーマの「Google Apps Script (GAS)を、もっと身近に、日々の暮らしに。」に沿った話しになっていきます。

この記事が気に入ったらサポートをしてみませんか?