Hero Image
善用 Go Fuzzing,幫助你寫出更完整的單元測試

善用 Go Fuzzing,幫助你寫出更完整的單元測試 func Pow(base uint, exponent uint) uint { if exponent == 0 { return 1 } return base * Pow(base, exponent-1) } func FuzzPow(f *testing.F) { f.Fuzz(func(t *testing.T, x, y uint) { assert := assert.New(t) assert.Equal(Pow(x, 0), uint(1)) assert.Equal(Pow(x, 1), x) assert.Equal(Pow(x, 2), x*x) if x > 0 && y > 0 { assert.Equal(Pow(x, y)/x, Pow(x, y-1)) } }) } go test -fuzz=Fuzz -fuzztime 20s 在做 Fuzzing Test 的時候如果跑一跑 FAIL 了,Go 會幫忙把那組 input 記在 testcase/ 裡面 看了之後會發現在 x=6、y=30 時 assert 會失敗,也就是說 pow(6, 30)/6 不會等於 pow(6, 29)。但這也太奇怪了吧?仔細實驗之後才發現是因為在計算 pow(6, 30) 的時候會發生 overflow。 因為 Go 定義的 max.MaxUint 大約是 18 * 10¹⁸,但 6²⁹ 大概是 7 * 10¹⁸。如果把 6²⁹ 再乘上 6,就會發生 overflow 得到 8 * 10¹⁸,很像繞了操場兩圈結果在跟原本差不多的位置。 var ErrOverflow = fmt.Errorf("overflow") func Pow(base uint, exponent uint) (uint, error) { if exponent == 0 { return 1 } prevResult, err := Pow(base, exponent-1) if math.MaxUint/base < prevResult { return 0, ErrOverflow } return base * prevResult, nil } func FuzzPow(f *testing.F) { f.Fuzz(func(t *testing.T, x, y uint) { assert := assert.New(t) if result, err := Pow(x, 1); err != ErrOverflow { assert.Equal(result, x) } if result, err := Pow(x, y); x > 0 && y > 0 && err != ErrOverflow { resultDivX, _ := Pow(x, y-1) assert.Equal(result/x, resultDivX) } }) }

Hero Image
一張圖學會【拼音輸入法】

一張圖學會【拼音輸入法】 聲母(Initials) 聲母 拼音 例字 ㄅ b 爸 ba、別 bie、奔 ben ㄆ p 撇 pie、平 ping、鵬 peng ㄇ m 媽 ma、莫 mo、某 mou ㄈ f 發 fa、飯 fan、風 feng ㄉ d 的 d、但 dan、段 duan ㄊ t 他 ta、貼 tie、團 tuan ㄋ n 你 ni、農 nong、暖 nuan ㄌ l 李 li、亂 luan、亮 liang ㄍ g 跟 gen、怪 guai、灌 guan ㄎ k 快 kuai、考 kao、狂 kuang ㄏ h 哈 ha、漢 han、橫 heng ㄐ j 叫 jiao、雞 ji、京 jing ㄑ q 七 qi、琴 qin、巧 qiao ㄒ x 西 xi、寫 xie、小 xiao ㄓ zh 知 zhi、戰 zhan、正 zheng ㄔ ch 吃 chi、船 chuan、重 chong ㄕ sh 是 shi、山 shan、刷 shuan ㄖ r 日 ri、讓 rang、人 ren ㄗ z 字 zi、讚 zan、昨 zuo ㄘ c 次 ci、菜 cai、餐 can ㄙ s 三 san、四 si、算 suan 介音 / 特殊聲母 類型 拼音 例字 y yi / i 以 yi、依 yi、修 xiu w wu / u 無 wu、溫 wen、船 chuan v yu / ü 律 lü、雨 yu、院 yuan 單韻母(Finals) 韻母 拼音 例字 ㄚ a 沙 sha、撒 sa、奧 ao ㄛ o 哦 o、偶 ou、歐 ou ㄜ e 餓 e、樂 le、設 she ㄝ ê / ye 也 ye、學 xue、切 qie ㄞ ai 愛 ai、篩 shai、崖 yai ㄟ ei 雷 lei、最 zui、脆 cui ㄠ ao 凹 ao、老 lao、笑 xiao ㄡ ou 歐 ou、樓 lou、手 shou ㄢ an 安 an、蘭 lan、算 suan ㄣ en 恩 en、林 lin、冷 leng ㄤ ang 昂 ang、浪 lang、讓 rang ㄥ eng 成 cheng、冷 leng、令 ling ㄦ er 二 er、兒 er、而 er 複韻母 / 組合韻 拼音 例字 ye 也 ye、結 jie、血 xie iu 六 liu、有 you、謬 miu in 音 yin、新 xin、林 lin ing 英 ying、靈 ling、醒 xing uan 元 yuan、卷 juan、團 tuan ue 略 lue、學 xue yun 雲 yun、巡 xun、群 qun ong 用 yong、兇 xiong、同 tong ui 鬼 gui、為 wei、虧 kui un 文 wen、混 hun、準 zhun ua 華 hua、抓 zhua、蛙 wa uai 壞 huai、快 kuai、踹 chuai

Hero Image
How to Make Output Overwrite the Same Line in a Terminal

How to Make Output Overwrite the Same Line in a Terminal Introduction to the Problem $ cat print_status.sh !/bin/bash echo "[INFO] Processing file: readme.txt" sleep 2 To simulate the file processing echo "[INFO] Processing file: veryPowerfulService.service" sleep 2 echo "[INFO] Processing file: log.txt" echo "DONE" $ ./print_status.sh [INFO] Processing file: readme.txt [INFO] Processing file: veryPowerfulService.service [INFO] Processing file: log.txt DONE The “Magic Code”: \033[0K\r -n option asks the echo command to stop outputting the trailing newline character -e option allows the echo command to interpret backslash escapes such as \n (newline) and \r (carriage return) \033 - It’s the escape sequence. In other words, it’s ESC. \033[ - Then this becomes “ESC [”, which is the control sequence introducer (CSI). \033[0k - So it’s “CSI 0 K”. Further, “CSI 0 K” erases the text from the cursor to the end of the line. \r - This is the carriage return. It brings the cursor to the beginning of the line. $ cat print_status.sh #!/bin/bash echo -ne "[INFO] Processing file: readme.txt\033[0K\r" sleep 2 echo -ne "[INFO] Processing file: veryPowerfulService.service\033[0K\r" sleep 2 echo -e "[INFO] Processing file: log.txt\033[0K\r" echo "DONE" !/bin/bash printf "[INFO] Processing file: readme.txt\033[0K\r" sleep 2 printf "[INFO] Processing file: veryPowerfulService.service\033[0K\r" sleep 2 printf "[INFO] Processing file: log.txt\033[0K\r\n" echo "DONE"

Hero Image
Avoiding the Top 10 NGINX Configuration Mistakes - NGINX

Avoiding the Top 10 NGINX Configuration Mistakes - NGINX 1) 每個 worker 的檔案描述元(FD)不夠 問題點 worker_connections 只限制 單一 worker 可同時開啟的連線數(預設 512)。 但每個連線/檔案/暫存檔/日誌都會消耗 檔案描述元(FD),而 OS 預設每個 process 常見是 1024。 常見錯誤:只調大 worker_connections,卻沒有同步提高 FD 限制,導致 worker 提早耗盡 FD。 修正方式 在 main context 設定 worker_rlimit_nofile,至少為 worker_connections 的 2 倍(經驗值)。 同時確認系統總 FD 上限 fs.file-max 足夠: worker_rlimit_nofile * worker_processes 要明顯小於 fs.file-max。 # main context worker_connections 1024; # 在 events {} 內 worker_rlimit_nofile 2048; # 在 main context 補充 NGINX 當 proxy 時:client 連線 1 FD + upstream 連線 1 FD,可能還需要暫存檔 1 FD。 若被 DoS 打滿 FD,甚至可能無法登入機器處置,因此要預留系統餘裕。 2) error_log off 其實沒有關閉 error log 問題點 error_log 不支援 off 參數。 寫成 error_log off; 會讓 NGINX 產生一個名為 off 的檔案(通常在 /etc/nginx/)。 修正方式(不建議真的關閉) 若真的必須停寫 error log(例如磁碟極度有限),改導到 /dev/null 並限制等級: # main context error_log /dev/null emerg; 補充 這條生效前,NGINX 啟動/ reload 驗證設定的過程仍可能先寫到預設路徑(常見 /var/log/nginx/error.log)。 可用啟動參數 nginx -e <error_log_location> 指定啟動階段的 error log 位置。 3) 沒有對 upstream 啟用 keepalive(導致連線/來源埠耗盡) 問題點 預設:NGINX 對 upstream 每個 request 都新建連線,連線建立/關閉都有成本。 高流量時會放大 OS 資源消耗;且連線關閉後會進入 TIME-WAIT,可能導致 來源埠(ephemeral ports)耗盡,進而無法建立新連線。 修正方式 (A) 在每個 upstream {} 內加 keepalive

Hero Image
Go string format

Go string format The verb at the end defines the type and the interpretation of its corresponding argument. d - decimal integer o - octal integer O - octal integer with 0o prefix b - binary integer x - hexadecimal integer lowercase X - hexadecimal integer uppercase f - decimal floating point, lowercase F - decimal floating point, uppercase e - scientific notation (mantissa/exponent), lowercase E - scientific notation (mantissa/exponent), uppercase g - the shortest representation of %e or %f G - the shortest representation of %E or %F c - a character represented by the corresponding Unicode code point q - a quoted character U - Unicode escape sequence t - the word true or false s - a string v - default format #v - Go-syntax representation of the value T - a Go-syntax representation of the type of the value p - pointer address % - a double %% prints a single % Go string format indexing package main import ( "fmt" ) func main() { n1 := 2 n2 := 3 n3 := 4 res := fmt.Sprintf("There are %d oranges %d apples %d plums", n1, n2, n3) fmt.Println(res) // There are 2 oranges 3 apples 4 plums res2 := fmt.Sprintf("There are %[2]d oranges %d apples %[1]d plums", n1, n2, n3) fmt.Println(res2) // There are 3 oranges 4 apples 2 plums } Go string format precision package main import ( "fmt" ) func main() { fmt.Printf("%0.f\n", 16.540) // 17 fmt.Printf("%0.2f\n", 16.540) // 16.54 fmt.Printf("%0.3f\n", 16.540) // 16.540 fmt.Printf("%0.5f\n", 16.540) // 16.54000 } Go string format flags package main import ( "fmt" ) func main() { fmt.Printf("%+d\n", 1691) // +1691 fmt.Printf("%#x\n", 1691) // 0x69b fmt.Printf("%#X\n", 1691) // 0X69B fmt.Printf("%#b\n", 1691) // 0b11010011011 fmt.Printf("%10d\n", 1691) // 1691 fmt.Printf("%-10d\n", 1691) // 1691 fmt.Printf("%010d\n", 1691) // 0000001691 } Go string format width package main import ( "fmt" ) func main() { w := "falcon" n := 122 h := 455.67 fmt.Printf("%s\n", w) // falcon fmt.Printf("%10s\n", w) // falcon fmt.Printf("%d\n", n). // 122 fmt.Printf("%7d\n", n) // 122 fmt.Printf("%07d\n", n) // 0000122 fmt.Printf("%10f\n", h) // 455.670000 fmt.Printf("%11f\n", h) // 455.670000 fmt.Printf("%12f\n", h) // 455.670000 }

Hero Image
YAML 裡的字串很長該怎麼做?

YAML 裡的字串很長該怎麼做? 在 YAML 裡已經有規範此部份,在這種情況有四種方法可以幫助我們: |: 其下內容的換行,就是換行,最後一行會有換行。 >: 其下內容的換行,不會是換行,會變為一個很長的字串,最後會有換行。 |-: 其下內容的換行,就是換行,但最後一行不會有換行。 >-: 其下內容的換常,不會是換行,最後一行也不會有換行。 簡單的說,> 跟 >- 可以增加 YAML 的可讀性,又不會有多餘的換行符號。而 | 跟 |- 則可以讓字串跟定義的一致,在 YAML 裡看到換行,那字串裡就會有換行符號。 --- - name: Test long string hosts: all vars: s1: "hello" s2: | s2 this is my very very very long string line1 line2 line3 s3: > s3 this is my very very very long string line1 line2 line3 s4: |- s4 this is my very very very long string line1 line2 line3 s5: >- s5 this is my very very very long string line1 line2 line3 tasks: - name: s1 copy: content: "{{ s1 }}" dest: "/tmp/s1.txt" # hello% - name: s2 copy: content: "{{ s2 }}" dest: "/tmp/s2.txt" # s2 # this is my very very very # long string # line1 # line2 # line3 - name: s3 copy: content: "{{ s3 }}" dest: "/tmp/s3.txt" # s3 this is my very very very long string line1 line2 line3 - name: s4 copy: content: "{{ s4 }}" dest: "/tmp/s4.txt" # s4 # this is my very very very # long string # line1 # line2 # line3% - name: s5 copy: content: "{{ s5 }}" dest: "/tmp/s5.txt" # s5 this is my very very very long string line1 line2 line3%