為頻繁執行的GraphQL API加上Update Lock

我有一個 GraphQL Query,他做的事情很單純:根據ID去撈出對應紀錄的欄位

query {
 getProjectStatus(id: ID!)
}

因為這支 Query 會頻繁、大量的被拉取,所以需要做 Cache。

那麼 node 有 node-cache 這個套件可以做記憶體快取,而且它可以指定 ttl(時間一過自動清除cache),
每次 request 進來除非無法從 cache 找到資料才讀取 DB

不過既然說了頻繁、大量,那麼如果在 cache 過期瞬間,有大量 request 進來,DB不就爆了嗎?
看起來為此是務必要引入 lock 機制

然後單純 lock 是不夠的,
如果大量 request 都排隊等著 acquire lock,他們就需要等很久才回應…
那麼能不能達成像 nginx proxy cache lock + background update 高速且低負載的效果呢?

可以的,
關於詳細的程式碼,我覺得可以參考 David Barral 寫的 Synchronize cache updates in Node.js with a Mutex

也可以參考下面我小調整後的程式碼,調整部份是 L18-L22


題外話,
雖然說心裡有想把這類 API 用 Restful API 改寫,
搭配 Nginx proxy cache lock + background update

只是還要多開一台小 node server,有點懶阿~

JS Callback轉成Promise

Promise + async/await 很好用,但現在還有很多 JS library 是以 event + callback 為基礎的
比如說 PM2 的 API 都像是這樣:

想把他轉成 Promise 也不是太難,因為 new Promise() 的時候有 resolve、reject 分別用來表示這兩種狀況:

  • resolve(variable):正常執行,variable代表要 return 的變數
  • reject(error):發生錯誤,error代表要 throw 的錯誤


直接來看程式碼吧
(因為這段程式碼是示範用,沒測試過)

用Nginx做Rate Limiting

一般人都會用 LNMP stack 做網站吧?

如果沒做 rate limiting,當有人狂連線到網站的時候,PHP 這類語言跟資料庫可能會把 CPU 吃乾抹淨CC

雖然台灣常用的 Laravel 就有內建 rate limiting 的 middleware,但畢竟還是用 PHP 寫的,
可以的話當然用效率更高的 nginx 來做囉

好在 nginx 設定這個超級簡單
這篇文章推薦大部份網站都可以這樣設定,參考看看:

Savings Plans能節省多少EC2的花費?

我用的是 t3a.micro,隨需的原價是一小時 0.0122

Compute Savings PlansEC2 Instance Savings Plans
1年3年1年3年
不預繳0.00960.0070.00770.0053
部份預繳0.00920.00640.00730.0049
全部預繳0.0090.00630.00720.0046

對於我這種開一台機器架 blog,好幾年不會動的人,買 EC2 Instance Savings Plans 的 3 年好像很適合R

0.0122 * 24小時 * 365天 * 3年 = 320鎂
0.0046 * 24小時 * 365天 * 3年 = 120鎂

差真多R

兩個Laravel專案透過Redis queue傳遞Job

最近在做兩個專案,他們有父子關係,其中子專案在註冊會員後,需要丟 job 給 父專案執行

雖然兩個專案使用同一台 redis 作為 queue,但 laravel 預設會把 job 存在 redis 中下面這個 key 的位置:

env(‘REDIS_PREFIX’, Str::slug(env(‘APP_NAME’, ‘laravel’), ‘_’).’_database_’)

// APP_NAME=laravel
// output => laravel_database_queue

可以看到想讓兩個專案真正讀取同一個 queue,就必須在 .env 裡面自訂 REDIS_PREFIX

接著,父子專案還要建立相同名稱的 job(ex: App\Jobs\TestJob),然後由子專案 dispatch job,由父專案執行 php artisan queue:work


當然父跟子沒有絕對,要不要寫 dispatch()->onQueue() 也沒有絕對