見出し画像

GASの排他制御について


今回はGASの排他制御について説明して行きます。
そもそも排他制御とは、「同一資源の共同利用による整合性の破綻」を防ぐための仕組みです。
つまりこれが必要になるのは同じ資源を複数人で使う場面です。
GASではこのようなシーンが想定されます。

  • Aさんが「関数2」を介してシートを利用

  • BさんとCさんが「関数3」を介してシートを利用

  • Dさんは直接シートを利用

このとき、関数2,3に以下のような最後尾に1行追加する処理が設定されている場合、同時に実行されると、正常に反映されず最終行が上書きされる可能性があります。また、Dさんが直接最終行に1行追加する場合も同様です。

//関数2

function addDate(){
 const ss = SpreadsheetApp.getActive()
 const sh = ss.getSheetByName("シート1")
 const date = Utlities.formatDate(new Date(),"Asia/Tokyo","yyyy/MM/dd")
 sh.appendRow([date])
 
} 

//関数3

function addTime(){
 const ss = SpreadsheetApp.getActive()
 const sh = ss.getSheetByName("シート1")
 const time = Utlities.formatDate(new Date(),"Asia/Tokyo","HH:mm")
 sh.appendRow([time])
 
}

このように「スプレッドシート」あるいは「関数」といった資源を共同利用すると、場合によっては「最終行」などの整合性が破綻する恐れがあります。

これを防ぐためにGASのLockServiceクラスを利用します。

LockServiceクラス

これを呼び出すと対象に"扉+錠前"が出現し利用を制限できます。従来はどこからでも利用できた関数やシートの入り口がこの扉に限定され、尚且つ一単位ずつしか入れなくなります。

この"扉と錠前"を出現させる対象は3種類あるため、メソッドも3種類存在します。いずれも戻り値はLockオブジェクト(≒錠前)です。

.getDocumentLock()

このメソッドによってコンテナされているスプレッドシートやdocsなどのファイルに錠前を設定できます。従って、スタンドアロンやWEBアプリからは呼び出せません。

.getScriptLock()

このメソッドによって関数単位で錠前を設定できます。従って関数2をA3が実行してる間はBさんは実行できません。

.getUserLock()

このメソッドは関数の中でも特定ユーザーに錠前を設定できます。従って関数1をAさんが実行中、Aさんは二重で実行できませんが、Dさんは同時に関数1を実行できます。


ただ、これら3つのメソッドはあくまで錠前を設定するだけなので、鍵はかかっていません。
鍵をかけるにはこれら3つによって獲得したLockオブジェクトに対して用意されてるメソッドを使います。

Lockオブジェクト

↑3つの戻り値であるオブジェクトです。以下5つのメソッドがあります。

.tryLock()

引数に指定したミリ秒間解除を試み、成功すると実行権を獲得。他者との競合を防ぐためロックし、以降に続く処理を実行します。
指定の時間内に他社の利用が終わらないなど実行権を獲得できない場合はfalseを返します。先の関数2に当てはめるとこのようになります。

//関数2

function addDate(){
 const ss = SpreadsheetApp.getActive()
 const sh = ss.getSheetByName("シート1")
 const lock = LockServile.getDocumentLock()
 lock.tryLock(1000*10){
  const date = Utlities.formatDate(new Date(),"Asia/Tokyo","yyyy/MM/dd")
  sh.appendRow([date])
 }
}

10秒間で解錠を試み、他社のシートの利用が終われば今日の日付を最終行に追加できます。解錠できない(他社の利用が終わらない)場合、falseとなり最終行には何も追加されません。
従ってif文で条件分岐できます。

.tryWait()

こちらも同様引数に指定したミリ秒間解錠を試み成功した場合のみ処理が実行されますが、解錠できなかった場合にerrorオブジェクトを発生させます。
従ってtrycatchで条件分岐できます。

.releaseLock()

自ら掛けたロックを解除するメソッドです。

.hasLock()

ロック(≒実行権)の有無を判別するメソッドです。他者が処理中、あるいはtryLockやwaitLockなどロックを獲得するメソッドを一度も実行してない、またはreleaseLockでロックを手放している場合などはfalse、ロックを有している場合はtrueを返します。

まとめ

「ロックを取得する」という表現が頻発するため混乱しがちですが、「ロック=実行権」と置き換えると理解しやすいかも知れませんね。

いずれにしても、スクリプトを介した制御しかできないので、シート自体を直接触れないように権限を設定しておくことをお勧めします。

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