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)を、もっと身近に、日々の暮らしに。」に沿った話しになっていきます。
この記事が気に入ったらサポートをしてみませんか?