Thursday, June 29, 2023

Postgres 的一些好用的功能:Auth, jsonb, validation on json, ltree, Expression based index

1. Postgres 可以利用 pg_crypto 這個 extension ,快速地做出 Authentication。參考這篇

2. Postgres 的 jsonb column ,這可以用來快速地搬移 Firestore  到 Postgres 。參考這篇

3. Postgres 可以對 json column 加上 schema validation。參考這篇

4. Postgres 可以利用 ltree 這個 extension 來記錄 tree 類型的資料結構。參考這篇

5. Postgres 也支援 expression based unique index 。參考這篇

第五項多加一些說明:

在 Clojure 的世界,我想找一個簡單易用的 worker queue ,但是又懶得架 Redis,所以 Proletarian 是選項之一。(Proletarian 是一個輕薄的 job queue library on top of Postgres)

然而,使用 Proletarian 也是有一些缺點:

- 比方說,job 的 payload 會被 Proletarian library 先做成 Transit ,再轉換成 text 來儲存。這時,如果你要放進 worker queue 裡的 job ,你想要對其施加一些 unique constriaint 時,該怎麼做呢?


之前我想不出做法,因為整個 payload 都被先打包成一包做成 text ,總覺得 unique constraint 加不上去。無奈之下,只好在 application layer 去實作這個 unique constraint 的檢查,實在有夠難寫。


最近研究 Postgres 才發現,有一個很犯規的東西叫做 expression based UNIQUE INDEX。用了這個的話,就可以輕易地對 text 或是 json 之類的資料的部分集合,加上 unique constraint了。 

Thursday, February 16, 2023

Web application 生成的報表速度太慢

 最近在 Gaiwan 公司遇到客戶委任我們做一些效能改進:

  1. 客戶把 code 的 link 給我之後,他們留下的訊息是說,覺得有沒有可能,把一些 code 改成用 Transducer 來改寫,效能就會好了。
  2. 我第一時間就覺得,八成不是這個問題。一般來講,web application 這種東西,會慢,十之八九都是資料庫的查詢在慢,更何況,客戶用的資料庫還是 Datomic 。
  3. 但是,光是有直覺沒有用,還是要設法証明。我很久沒有做 profiling 了,上次做疑似是十年前,總之,基本的概念還有,技術完全沒有。花了一些時間研究工具之後,發現有兩個程式很有用。
    (3.a) jcmd => 這個可以快速找到 java program 對應的啟動 argument 與 pid
    (3.b) VisualVM => 我是用 statistical mode 去量測。它可以幫你一層又一層地向下展開,看到 x function calls y function ,且 x function 的 total time, CPU time, self time 。我用了這個之後,層層展開函數呼叫後,果然,定位出來,最耗時的地方是資料庫查詢 (Database Query)
  4. 該怎麼改進呢? 一旦找到關鍵點了,要改就有很多可能性。在眾多的改法之中,我個人覺得工程略為耗大,但是一勞永逸的改法,是把資料 sync 到資料倉儲 (Data warehouse) 裡,把本來很慢的資料庫查詢都送到資料倉儲去。由於資料倉儲在 schema 、index 上都有許多神奇的最佳化,效能的大躍進滿可以期待的。
  5. 然而,這件事的結尾並不是使用資料倉儲。
    我老闆一眨眼就找出效能低落的關鍵點:N+1 problem 。速度很慢的那一段程式碼,它在迴圈裡做 Query 。如果 Query 是那種極度單純的,完全不需要 scan 資料庫的,老實說還好,畢竟 Datomic 在 peer 端做了大量的 cache ,並不會有很大的 I/O 開銷。 而我遇到的這個案例,迴圈裡的 Query 是有做一些些 unification 的。所以解決之道,是要改變程式碼的寫法,把 Query 移到迴圈外。 修改完之後,就快了 20 倍。

Thursday, February 9, 2023

Clojure syntax error 解決方案

使用 Clojure CLI 來做為啟動程式的話,由於 Clojure CLI 會生成 .cpcache 檔,啟動速度會比較快一點。然而,有的時候,.cpcache 可能有一些 cache 造成程式的載入順序錯誤,就會造成 syntax error 

如果遇到明明沒有寫錯 syntax ,卻一直得到 syntax error 的話,可以考慮將 .cpcache 刪除,就可以解決了。

Thursday, June 30, 2022

tmux, Clojure REPL, direnv, alias

開發 Clojure/ClojureScript 的程式時,我的習慣是會用 tmux 來開啟一個在背景的 repl 來做 development 

我常用的一些 shell alias 長成這樣子:

alias bbrepl="tmux new-session -d -s bbrepl 'bb nrepl-server 1667'"

alias drepl="tmux new-session -d -s drepl 'clj -M:dev:cider-clj'"

alias dfull="tmux new-session -d -s dfull 'clj -M:dev:shadow-cider-clj'"

alias dshadow="tmux new-session -d -s dshadow 'clj -M:shadow-cljs watch main'"


這個方式用了很久都沒有什麼問題,直到有一天,我發覺我需要所謂的 local repl 也就是說,我啟動的這個 repl ,它指定的 deps.edn 的 alias 是可以根據 project directory 來變動的。


本來,我設法用了 direnv ,很快就做到了,「可以讓 environment variable 隨著切換進入資料夾而掛載」。然而,其實我需要的是「可以讓 alias 隨著切換進入資料夾而掛載」。


最後我的作法是這樣子:
(1) 在專案的資料夾下,準備一個 .alias 的資料夾,裡頭放一些我要拿來當指令的 shell script ,比方說 lrepl

(2) 在專案的資料夾下,準備一個 .envrc 檔,裡頭寫了 
PATH_add .alias

Monday, May 2, 2022

在 mac 電腦安裝/使用 pyenv 

安裝 pyenv

step 1: 利用 homebrew 來安裝

brew update
brew install pyenv

step 2: 設置 zshell

echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.zshrc
echo 'command -v pyenv >/dev/null || export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.zshrc
echo 'eval "$(pyenv init --path)"' >> ~/.zshrc

pyenv 的基本操作指令

(1) pyenv versions
顯示現在 local 的電腦有哪些 python 版本可以使用
(2) pyenv install --list
顯示 pyenv 提供哪些版本的 python 可供安裝
(3) pyenv install 3.7.0 
安裝某特定 python 版本
(4) pyenv global 3.70 
將系統預設的 python 設定成 3.7.0
(5) pyenv local 3.7.0 
將當前 session 的 python 設定成 3.7.0

參考資料

Wednesday, April 20, 2022

variants

我之前去研究過 monad ,得到的心得是:「這個東西對我沒有什麼啟發性,因為我在 Clojure 裡,用 vector 或是用 map 就可以夾帶一些資訊了。」

然而,前兩天看了一個很久以前的 talk ,它在介紹 variants 這個概念。看完之後,我有了全新的觀點:不是這些 monad, variants 東西對我沒有用,是早就一直在使用了,或是被 library/DSL 半強迫地要我用了…。

https://www.youtube.com/watch?v=ZQkIWWTygio

這邊先做超快速地介紹:

(1) 什麼是 variants ?

[:tag value]

(2) 什麼是 recursive variants?

[:app [:op [:val 15]

               [:val 16]]]

基本上,hiccup 就算是一種 recursive variants。

(3) 誰用了 variants ?

Erlang 使用了 result variant 

Hiccup 也可以視為是使用 variant 

re-frame 也用了

還有很多時候,是該用,而沒有去用,所以就寫出爛 code 了 

Monday, April 11, 2022

Clojure macro 的妙用

何時該使用 Clojure macro 最適當的時機,一直是一個我覺得費解的問題。對我來講,macro 是一種 compiler plugin ,只有在 programming language 這個抽象層明顯不足的時候,才會用到。

終於,我找到了一個我覺得算是頗為合理的應用。在 antizer 這個 library 的 macro 應用,我覺得極為合理。

它應用的邏輯是這樣子:
要在 ClojureScript 使用 react ,比較好用的方式,是要使用 reagent component ,而不是直接使用 react component 。換言之,要先寫一點程式碼,先包一層。然而, Ant react library 裡卻有著大量的 react component 。所以要寫大量重複的程式碼。應用 macro 的話,就可以讓這些大量的程式碼只要寫一遍即可。