{%hackmd @wh91008/theme %} # Lecture 7 - Cloud Function的延伸應用 ###### tags: `GCP` `Firestore` `Cloud Function` `Python` [TOC] --- ## ==一、前言== 在 [Cloud Function 的入門與部署](https://hackmd.io/@wh91008/Sy9U8aZbB) 和 [ Firestore的監聽](https://hackmd.io/@wh91008/H1JFhmLFr/https%3A%2F%2Fhackmd.io%2FdspKO4cPSQaOpUCt-eZgTg) 中,已經學會使用 Cloud Function 來做到基礎的布置和監聽。這個章節主要是用來介紹筆者在專案製作的過程中有遇到那些問題,還有我是如何解決這些問題的過程。 如果遇到一些好用的用法,也會補充在這裡。 --- ## ==二、問題集== ### 問題一:平行運算 #### (一)平行運算 :::danger :question: 多場遊戲進行時,產生必須讓 A 遊戲結束時 B 遊戲才會開始進行的問題 ::: 我一開始是寫了一個 function ,它主要運作的邏輯就是找出 `db.collection('col_name')` 路徑中最新的一個 `match_key`,程式如下 。 ```python= # get last generate doc id def get_last_doc_id(col_ref, order_key): query_get = col_ref.order_by(order_key, direction=firestore.Query.DESCENDING).limit(1).stream() for doc in query_get: return doc.id ``` 一開始,當 A 遊戲 和 B 遊戲 創建的時間相隔超過 5 秒時,兩場遊戲幾乎都能順利的進行平行運算,此時我就想,會不會是 A 遊戲 的程式還沒成功執行 B 遊戲的程式就已經開始了,此時我的思維是「如何減少遊戲觸發後執行的時間?」 我有的解決方式可能有: 1. 增進程式碼效率 2. 添加異步協程 除此之外,參考了官方文件,我發現 Cloud Function 存在一個名為 **「Cold Start」** 的問題。 #### (二)Cold Start Cold Start 並不是 Cloud Function 裡面存在的專有名詞,而是存在於各式各樣的電腦都有這個問題。 > Cold Start > 計算機的電源關掉後,經過一段時間再打開電源,使計算機重新起動。冷起動會使計算機的工作被重新設定,而記憶體內之資料也會被清除。 換句話說,由於 Cold Start 會讓工作被重新設定、清除記憶體內的資料,所以在重新啟動時,執行的時間會 大大的超過 已經暖機狀態的執行時間。 可以參考 [這份資料](https://medium.com/@duhroach/improving-cloud-function-cold-start-time-2eb6f5700f6) ,在文章中它將 Cold Start 優化縮短了大量的時間。 最後,我參考了 [官方文件](https://cloud.google.com/functions/docs/bestpractices/tips) ,再搭配了異步協程撰寫了下方的程式碼,讓兩場遊戲創建時間相隔縮短到 1~2 秒以內也可成功執行。 ```python= # TODO async class ParallelExecution: def __init__(self, data, context): self.data = data self.context = context self.instance_var = self.heavy_computation() # Placeholder def heavy_computation(self): return time.time() def light_computation(self): return time.time() # Global (instance-wide) scope # This computation runs at instance cold-start async def scope_demo(self, data, context): # Per-function scope # This computation runs every time this function is called function_var = self.light_computation() return 'Instance: {}; function: {}'.format(self.instance_var, function_var) async def main(self): # Schedule scope_demo() to run soon concurrently with "main()". task = asyncio.create_task(self.scope_demo(self.data, self.context)) # "task" can now be used to cancel "scope_demo()", # or can simply be awaited to wait until it is complete: await task # 等待一會(等待返回再去執行) ``` 不過假設兩場遊戲之間只要隔 1 秒,那 100 場遊戲的話也意味第 1 場遊戲和第100場遊戲中間至少要間隔 99 秒,這完全是不合理的!所以還是得寫一個 log 檔,看真正的問題到底出在哪。 不過很棒的是,在 GCP 的後台可以直接監控 log 檔,如下圖點擊後就可觀看,程式中所有 `print` 的資訊也會表現在裡面。  在花了幾天的時間 debug 後,我發現真正的問題是 Firestore 本身存在的一個限制— **每秒新增 document 次數**。 #### (三)每秒新增 document 次數限制 在 [官方文件](https://firebase.google.com/docs/firestore/quotas) 中明確提到 > Firestore 每秒寫入次數為 1 次 也就代表說,在我兩場遊戲同時創建時,由於每秒寫入次數最多1次的關係,兩者本身創建的時間就會差距 1 秒,此時如果我 Cloud Function 早就觸發兩次了,那我在 **(一)平行運算** 中提到說,我抓 document 邏輯是以「Clodu Function 觸發時的最後一個`match_key`」,就不可行了。 (因為 B 遊戲的 Cloud Function 觸發後能抓到的最後一個 key 是 A 遊戲的 key) #### (四)最終解決方案 最後,參考了 [這份](https://cloud.google.com/functions/docs/calling/cloud-firestore?hl=zh-TW) 文件,找到 Cloud Function 可以直接抓取觸發條件的 document id,程式碼如下,這樣就能解決各種時差問題了,因為本身執行的 match 一定就是被觸發的那場 match 。 ```python= def hello_firestore(data, context): # 要優化 db = firestore.client() trigger_resource = context.resource last_match_key = trigger_resource.split('/')[-1] # match0001 print('%s start!!!' % last_match_key) # match0001 start!!! ``` ###### :book: 參考資料 ###### 1. https://medium.com/@duhroach/improving-cloud-function-cold-start-time-2eb6f5700f6 ###### 2. https://cloud.google.com/functions/docs/bestpractices/tips ###### 3. https://firebase.google.com/docs/firestore/quotas ###### 4. https://cloud.google.com/functions/docs/calling/cloud-firestore?hl=zh-TW
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up