專題「疫苗追蹤器」的學習

簡信昌
READr
Published in
Jul 6, 2021

上禮拜,我們因應台灣疫苗的進口,跟施打的安排,搶出了一個「疫苗追蹤器」的專題。不過因為這個專題擴散幾乎都不是在 FB 上,以及會想看這篇(技術文)的人跟專題的 TA 還是不太一樣。所以我還是快速講一下相關機制吧。

基本上,就是讓使用者根據自己的地區、年齡,跟職業等等,去查看是不是可以登記疫苗注射。如果還沒有輪到,或是已經注射完第一劑,也可以留下 email,等政府公布,我們就可以寄發通知信。

資料串接

這裡有分兩個大的部分,一個是每天更新政府的施打政策。另外,就是讓使用者登記自己的電子郵件信箱,以便等到符合資格才能收到通知。至於頁面的製作時間,從發想,設計,到實作。原來預估是只有一個禮拜多一點(後來因為政策轉變頻繁,拖延了上線時間),所以我們希望在最快的時間來完成這個專題。當然,除了頁面本身的製作之外,複雜的資格比對是非常花時間的。而除此之外,怎麼可以把使用者的輸入跟後面的資料儲存可以快速的實作也是另一個讓專題可以儘快上線的重要關鍵。

所以怎麼可以用簡單的方式實作一個存取資料庫的 API,讓前端可以不用考慮太多複雜的介面呢?

照過去的經驗,就是拿 google spreadsheets 當資料庫,就可以不用考慮欄位設計,直接把所有資料塞進去就好。但是對於寫入特定的 spreadsheets 要有足夠的控管,還是需要有 server side 的程式來確保。

因為前端已經要花大部分的時間去做頁面的部分,而且我們的專題都是轉成靜態 HTML,也就是沒有特定的前端 server,所以我就決定自己來完成 server side 的 API 部分。那麼接下來就要考慮我自己要怎麼寫這個 server 了。

因為這是新聞專題的需求,大部分的新聞專題,除非是準備長期更新的專題(例如我們之前做過的總統大選的事實查核專題那一類),否則專題的生命週期一般不會太長,或說就算是長尾的專題,他的高峰期可能也不會超過一個禮拜。所以要可以快速開發,佈署的方式對於新聞專題是比較理想的。否則花太多時間還在弄 CI/CD,結果不到一個禮拜就可能不再需要的 server 也是有點麻煩。而第一時間想到簡單的方式,應該可以用 serverless 的方式,我們自己用的 GCP 是用 cloud functions。Cloud functions 的 python 其實就是起了一個 Flask,但使用者不用去擔心這個部分,只要把函式寫好就可以了。但是可能會需要考慮的就是 function 的冷啟動(cold start,就是一段時間沒用,也沒有 instance 在跑你的 function,因此會需要先把 function 跑起來,然後再去接使用者的 requests)。

所以我就用了 Cloud functions,再使用 python 存取 google spreadsheets 的 library —pygsheets。這樣一來,大概幾十行的程式就可以把訂閱跟取消訂閱的功能都實作出來。因此從決定要自己重寫一個 server 到第一版可以用的函式部屬成功,大概花了 4 個小時吧。

Trouble Shooting

一般來說,一個專題的實作大概到這裡就可以結束了。但這個專題大概是擊中了大多數人的需求,而且在上線後第三天似乎在 LINE 群組裡開始傳開,然後開始湧入大量的使用者。根據 GA 的資料,高峰期間的同時上線人數已經超過 250 人,這時候發現了開始有比較多數量的資料在 spreadsheets 上是呈現空白。

看來這是一個滿大的問題,所以就開始研究這個問題會發生的原因。我先看了 Cloud functions 的 log,發現使用者送出的資料在 log 裡都有出現,也就是說 Cloud functions 的程式都可以順利取得使用者送來的資料,因此可能的問題應該是這個函數在寫入 spreadsheets 的時候發生了問題。回去重新看了 log,有問題的資料好像有個 pattern,也就是和上一筆(或下一筆)資料的送出時間幾乎都在同一秒(log 會顯示到毫秒)。這樣猜測有可能在 pygsheets 寫入 spreadsheets 的時候,因為同時發生寫入,造成某些衝突。

於是決定再回去看 pygsheets。就在這裡

https://github.com/nithinmurali/pygsheets/blob/c469e13d0d342791fa689113e379bfc0507baf02/pygsheets/worksheet.py

會發現當我們要用 pygsheets 去 insert_rows,其實他是用了 batch_updates 的方式,也就是先找到使用者指定的位置,增加空白的列數,再把資料填入。因此當兩個(以上)的 requests 同時進來的時候,batch_updates 會拿到一樣的 index,可是當上一個 request 被完成時,由於 sheets 的 rows 沒有被 lock,接下來的 requests 就有可能覆蓋掉原來的資料。

簡單來說,就算是想要快速完成生命週期短的新聞專題資料庫存取,如果需要有使用者寫入的功能,看來似乎還是不適合透過 google spreadsheets 來完成。至於可以怎麼樣完成快速又有彈性的功能呢?!可能下一篇再來討論……

--

--

簡信昌
READr
Editor for

工程師 / 攝影師 / 資料新聞 各種興趣,目前擔任資料新聞媒體《READr》總編輯。對於新聞媒體未來想像充滿興趣,也希望透過技術改變新聞媒體的為來樣貌。同時也思考著如何以鏡頭與藝術創作面對自己的人生。