[閱讀筆記] 誰搬走我的乳酪? 堅持與PIVOT

故事簡述

一個大的迷宮裡面有許多乳酪,
四個角色「哈哈」、「猶豫」、「好鼻鼠」、「飛腿鼠」
「哈哈」是主角,它們在迷宮裡找乳酪,
每個人喜歡的乳酪都不相同,一開始他們找到了一個大乳酪站,
但是隨著時間過去,乳酪消失了,
「好鼻鼠」、「飛腿鼠」很快的離開尋找新的乳酪,
「哈哈」與「猶豫」在原處停留了一陣子,
最後「哈哈」終於啟程去尋找乳酪的心路歷程…

心得

「哈哈」是主角,
「猶豫」代表過去的自已,
「好鼻鼠」、「飛腿鼠」分別代表兩種成功的人,
一種是有敏銳的嗅覺,能快速找到「乳酪」所在的方向
另一種是行動派,能快速嘗試迷宮的各個走道。

迷宮意謂著這個世界,乳酪讓人生成功與幸福的事物。
是的,乳酪是會改變的,就像是人生的目標也會改變
不論是人生的階段的不同,或是市場的變化或是單純心境的改變。
你的乳酪就是會改變。

我想起「牧羊少年奇幻之旅」的商人,他永遠不會去麥加(假的乳酪),反而期待著去麥加
才是他真正的乳酪。真實的生活上也是這樣,我們會訂定很多目標,
但是許多目標不是你真正熱情所在,反而訂目標這個行為會成為安慰自已的碎屑乳酪,
訂目標多容易不是嗎?比起實踐它。

最近在看另一本書「OKR 工作法」,裡面也有提到訂定目標(乳酪)的原則。

  1. 目標要明確方向且鼓舞人心
  2. 要有時間限制
  3. 由獨立團隊來執行目標

我只談第一點,但在工作上很少有目標讓自已能振奮起來的,
缺乏獎勵是其一,不合個人目標是其二,說一套作一套是其三。
當然我相信有的幸運兒會工作與個人目標契合的,
缺乏獎勵在台灣是常態,但我認為獎勵只是特效藥,
最糟糕的是說一套作一套,目前有點陷入這樣的困境了。

我的乳酪究竟是什麼呢 ?
不要停在沒有乳酪的地方,但是離開的時機很關鍵啊…
這個小寓言仍然是很樂觀的,完全沒有描述在尋找乳酪的路上餓死情境。
我的乳酪究竟是什麼呢 ?

哈哈的牆上筆記

「有了乳酪,你就會快樂」
「乳酪對你愈重要,你就愈想擁有它」
「如果你不改變自已,你就會被淘汰」
「如果你對未來無所謂畏懼的話,你會怎麼作?」
「雖然比較晚才開始,但也比從來都沒有開始要好得多了!」
「要常常嗅一嗅乳酪的氣味,如此你才會知道它何時開始變質!」
「往新的方向移動能幫助你找到新乳酪!」
「當你擺脫了自已的恐懼,你就會感到無比的暢快和舒適!」
「在還沒有享用乳酪前,先想像我正在享用那些乳酪,這會幫助我快點找到乳酪!」
「你愈找放棄舊乳酪,你就會愈快找到新乳酪!」
「進入迷宮裡尋找新的乳酪,比繼續停留在已經沒有乳酪的地方要安全得多了!」
「食古不化的想法,不會幫助你找到新的乳酪。」
「當你覺得你會發現並享受新的乳酪時,你就會改變你的路徑。」
「及早注意事情的小變化,就能幫助提早適應即將到來的大變化。」

變化是會發生的
預期改變的到來
觀察變化
迅速地適應變化
改變自已
享受自已的改變
隨時準備好迅速地適應改變並再度享用美味的乳酪

(fin)

[實作筆記] Hexo Update 與 npm 修復漏洞

好久沒有作 hexo 更新了,
本身這個 Blog 就是透過 hexo 與 Github 建置的 。
眨眼也過了兩年,文筆仍然很差;
說真的有很大的資訊焦慮,文章的內容也不夠充實,
很多觀點無法忠實的記錄下來,更無法連貫成線;
自已還要多方努力與學習。

如前面所說,這個 Blog 是透過 Github 作存放的空間,
雖然 Github 最近被 Microsoft 收購了;
不過它仍是開發人員最常使用的代碼托管服務與社群,
而且微軟近年轉型相同成功 –— 未來會怎麼樣我們可以觀察後再下定論。

Github 一直以來都有一個很棒的功能,
GitHub’s security alerts for vulnerable dependencies

它能夠對你的 Repo 提供安全警告,
如下圖,這是對我的個人 Blog 所使用的框架 Hexo 的相依性安全警告,
這封信說明了 lodash 這個套件的版本存在著高風險的資安問題,
建議升級套件以解決這個問題。
升級套件

解決方式

使用 npm update , 這個命令會更新專案的 package 到最新版本

1
npm update

可能會看到類似以下的結果,同時提示你可以使用 npm audit fix 更新有風險的 package 以修複這些漏洞

1
2
3
4
5
6
7
8
9
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: [email protected] (node_modules\nunjucks\node_modules\fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for [email protected]: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"})
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: [email protected] (node_modules\fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for [email protected]: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"})

+ [email protected]
added 126 packages from 59 contributors, removed 80 packages, updated 21 packages, moved 14 packages and audited 3143 packages in 25.064s
found 68 vulnerabilities (21 low, 38 moderate, 9 high)
run `npm audit fix` to fix them, or `npm audit` for details

(fin)

[實作筆記]SQL Server 與 Linked Server 注意事項

原始 SQL

1
2
3
4
5
6
7
8
9
10
11
USE NationalDB
GO
BEGIN TRY
---Select Cross Linked Server
Select Name From LinkedServer.Shop.dbo.Member
---DO SOMETHING
END TRY
BEGIN CATCH
DECLARE @errorMessage NVARCHAR(MAX) = ERROR_MESSAGE();
RAISERROR (@errorMessage, 16, 1);
END CATCH
  1. 變數先行宣告
  2. 非必要不要宣告MAX
  3. 跨 DB 存取不使用四節式的查詢語法,改用 sp_executesql

修改後的語法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
USE NationalDB
GO
DECLARE @errorMessage NVARCHAR(4000) = ERROR_MESSAGE();
BEGIN TRY
---Select Cross Linked Server
EXEC LinkedServer.Shop.dbo.sp_executesql N'
SELECT @Name = Name
FROM dbo.Member
WHERE MemberId = @MemberId
AND Valid = 1',N'@MemberId bigint,@Name varchar(20) OUTPUT',@MemberId,@Name OUTPUT
---DO SOMETHING
END TRY
BEGIN CATCH
RAISERROR (@errorMessage, 16, 1);
END CATCH

(fin)

[踩雷筆記] Docker 啟動 MongoDB Server Error

錯誤訊息

1
2
3
4
5
6
> docker-compose up db
Starting marsen-mongodb ... error
ERROR: for marsen-mongodb Cannot start service db:
driver failed programming external connectivity on
endpoint marsen-mongodb (6f2be666a700187b7f30884478d7231cc210182e2650602e513c6627d0bc3e5a):
Error starting userland proxy: mkdir /port/tcp:0.0.0.0:27017:tcp:172.18.0.2:27017: input/output error

除錯

重啟 docker

(fin)

[閱讀筆記] 拿破崙不是敗在滑鐵盧戰役而是金融戰爭

  • 猶太教典

    • 財富是要塞,貧苦是廢墟
    • 金錢並非罪惡、並非詛咒,而是給予人的祝福
    • 有三件事情會傷人:煩惱、爭吵、空錢包。其中尤以空錢包最為傷人
  • 伊斯蘭人

    • 發明複式簿記
    • 損益表(銷售額與支出費用)
    • 資產負債表(資產與負債)
  • 內克爾

    • 公開透明引發法國大革命
  • 第一艘蒸汽船停駛

    • 既得利益者(運河公司與船東)的反對
  • 緬甸因英國殖民政策引發種族問題

    • 社會地位,英國人 > 中國/印度人 > 克倫族(少數民族) > 緬甸人
    • 獨立後,克倫族反而持續遭受迫害至今
  • 猶太人待過的金融重鎮

    • 西班牙
    • 荷蘭 阿姆斯特丹
    • 英國 倫敦
    • 美國 紐約
  • 一次大戰

    • 四打一的圍毆(英法俄美 vs 德)
    • 德國的優勢,與土耳其建鐵路,與奧匈帝國相同語言,若統一會成為歐陸強國
    • 美國產石油,英國產煤,能源革命導致需求轉向
  • 希特勒

    • 凡爾賽條約是個源頭
    • 希特勒被提名過諾貝爾獎
    • 一開始的「侵略」是「收復國土」
  • 二戰的好處

    • 亞非各國的獨立
  • 共產主義

    • 建立平等社會
    • 平均分配財富
    • 蘇聯未受大蕭條影響
    • 短期的極權,有效;長期的極權,腐敗
    • 蘇聯比其它國家更不平等
    • 「當下有事情該作,只要沒納入事前計劃中就不能作;而當下發現沒必要作的事情,只要已經在計劃裡就非作不可,所以完全無法發揮第一線人員的創意」
  • 凱因斯

    • 二戰對德不合理條款的警告
    • 對「廢除德匯兌保護條款」的警告
    • 美元本位的警告
  • 亡國模式

    • 富人有逃(節)稅特權
    • 國家對窮人課重稅
    • 貧富差距變大

(fin)

[KATA] 最大公因數與最小公倍數

簡介

數學系畢業後,在學寫程式雖然偶爾會用到一些數學特性來解題,
不過到了工作確不是那麼一回事,大多在處理商業流程,資料流 ;
然後在商業流程與資料流中穿梭,找蟲然後補丁 ;

這是最近在練習 Kata 的題目所用到的兩個國中數學,
「最大公因數」與「最小公倍數」,還用程式實踐了「輾轉相除法」,
真的是有點懷念啊 。

這是題外話,Rx(Reactive Functional Programing)感覺很有趣
最近參與 functional programing 的讀書會,
裡面似乎有比現在開發模式更優雅的解決問題方式。
不過門檻有點高,需要具備的抽象思考能力,是種非常燒腦的開發方式。

實作

1
2
3
4
5
6
7
8
//// GCD
static Func<int, int, int> GCD = (numA, numB) => {
return numB == 0 ? numA : GCD(numB, numA % numB);
};
//// LCM
static Func<int, int,int> LCM = (numA,numB) => {
return numA * numB / GCD(numA, numB);
} ;

參考

(fin)

i18n 與 l10n

I18n 國際化

1
2
3
| I | n | t | e | r | n | a | t | i | o | n | a | l | i | z | a | t | i | o | n |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |10 |11 |12 |13 |14 |15 |16 |17 |18 | |

Internationalization 因為字首I到字尾N18個英文字

L10n 在地化

1
2
3
| L | o | c | a | l | i | z | a | t | i | o | n |
|---|---|---|---|---|---|---|---|---|---|---|---|
| | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |10 | |

Localization 因為字首L到字尾N共10個英文字

國際化/在地化常見問題

  • 語言
    • 語系-區碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
  [
{"LangCultureName": "af-ZA", "DisplayName": "Afrikaans - South Africa"},
{"LangCultureName": "sq-AL", "DisplayName": "Albanian - Albania"},
{"LangCultureName": "ar-DZ", "DisplayName": "Arabic - Algeria"},
{"LangCultureName": "ar-BH", "DisplayName": "Arabic - Bahrain"},
{"LangCultureName": "ar-EG", "DisplayName": "Arabic - Egypt"},
{"LangCultureName": "ar-IQ", "DisplayName": "Arabic - Iraq"},
{"LangCultureName": "ar-JO", "DisplayName": "Arabic - Jordan"},
{"LangCultureName": "ar-KW", "DisplayName": "Arabic - Kuwait"},
{"LangCultureName": "ar-LB", "DisplayName": "Arabic - Lebanon"},
{"LangCultureName": "ar-LY", "DisplayName": "Arabic - Libya"},
{"LangCultureName": "ar-MA", "DisplayName": "Arabic - Morocco"},
{"LangCultureName": "ar-OM", "DisplayName": "Arabic - Oman"},
{"LangCultureName": "ar-QA", "DisplayName": "Arabic - Qatar"},
{"LangCultureName": "ar-SA", "DisplayName": "Arabic - Saudi Arabia"},
{"LangCultureName": "ar-SY", "DisplayName": "Arabic - Syria"},
{"LangCultureName": "ar-TN", "DisplayName": "Arabic - Tunisia"},
{"LangCultureName": "ar-AE", "DisplayName": "Arabic - United Arab Emirates"},
{"LangCultureName": "ar-YE", "DisplayName": "Arabic - Yemen"},
{"LangCultureName": "hy-AM", "DisplayName": "Armenian - Armenia"},
{"LangCultureName": "Cy-az-AZ", "DisplayName": "Azeri (Cyrillic) - Azerbaijan"},
{"LangCultureName": "Lt-az-AZ", "DisplayName": "Azeri (Latin) - Azerbaijan"},
{"LangCultureName": "eu-ES", "DisplayName": "Basque - Basque"},
{"LangCultureName": "be-BY", "DisplayName": "Belarusian - Belarus"},
{"LangCultureName": "bg-BG", "DisplayName": "Bulgarian - Bulgaria"},
{"LangCultureName": "ca-ES", "DisplayName": "Catalan - Catalan"},
{"LangCultureName": "zh-CN", "DisplayName": "Chinese - China"},
{"LangCultureName": "zh-HK", "DisplayName": "Chinese - Hong Kong SAR"},
{"LangCultureName": "zh-MO", "DisplayName": "Chinese - Macau SAR"},
{"LangCultureName": "zh-SG", "DisplayName": "Chinese - Singapore"},
{"LangCultureName": "zh-TW", "DisplayName": "Chinese - Taiwan"},
{"LangCultureName": "zh-CHS", "DisplayName": "Chinese (Simplified)"},
{"LangCultureName": "zh-CHT", "DisplayName": "Chinese (Traditional)"},
{"LangCultureName": "hr-HR", "DisplayName": "Croatian - Croatia"},
{"LangCultureName": "cs-CZ", "DisplayName": "Czech - Czech Republic"},
{"LangCultureName": "da-DK", "DisplayName": "Danish - Denmark"},
{"LangCultureName": "div-MV", "DisplayName": "Dhivehi - Maldives"},
{"LangCultureName": "nl-BE", "DisplayName": "Dutch - Belgium"},
{"LangCultureName": "nl-NL", "DisplayName": "Dutch - The Netherlands"},
{"LangCultureName": "en-AU", "DisplayName": "English - Australia"},
{"LangCultureName": "en-BZ", "DisplayName": "English - Belize"},
{"LangCultureName": "en-CA", "DisplayName": "English - Canada"},
{"LangCultureName": "en-CB", "DisplayName": "English - Caribbean"},
{"LangCultureName": "en-IE", "DisplayName": "English - Ireland"},
{"LangCultureName": "en-JM", "DisplayName": "English - Jamaica"},
{"LangCultureName": "en-NZ", "DisplayName": "English - New Zealand"},
{"LangCultureName": "en-PH", "DisplayName": "English - Philippines"},
{"LangCultureName": "en-ZA", "DisplayName": "English - South Africa"},
{"LangCultureName": "en-TT", "DisplayName": "English - Trinidad and Tobago"},
{"LangCultureName": "en-GB", "DisplayName": "English - United Kingdom"},
{"LangCultureName": "en-US", "DisplayName": "English - United States"},
{"LangCultureName": "en-ZW", "DisplayName": "English - Zimbabwe"},
{"LangCultureName": "et-EE", "DisplayName": "Estonian - Estonia"},
{"LangCultureName": "fo-FO", "DisplayName": "Faroese - Faroe Islands"},
{"LangCultureName": "fa-IR", "DisplayName": "Farsi - Iran"},
{"LangCultureName": "fi-FI", "DisplayName": "Finnish - Finland"},
{"LangCultureName": "fr-BE", "DisplayName": "French - Belgium"},
{"LangCultureName": "fr-CA", "DisplayName": "French - Canada"},
{"LangCultureName": "fr-FR", "DisplayName": "French - France"},
{"LangCultureName": "fr-LU", "DisplayName": "French - Luxembourg"},
{"LangCultureName": "fr-MC", "DisplayName": "French - Monaco"},
{"LangCultureName": "fr-CH", "DisplayName": "French - Switzerland"},
{"LangCultureName": "gl-ES", "DisplayName": "Galician - Galician"},
{"LangCultureName": "ka-GE", "DisplayName": "Georgian - Georgia"},
{"LangCultureName": "de-AT", "DisplayName": "German - Austria"},
{"LangCultureName": "de-DE", "DisplayName": "German - Germany"},
{"LangCultureName": "de-LI", "DisplayName": "German - Liechtenstein"},
{"LangCultureName": "de-LU", "DisplayName": "German - Luxembourg"},
{"LangCultureName": "de-CH", "DisplayName": "German - Switzerland"},
{"LangCultureName": "el-GR", "DisplayName": "Greek - Greece"},
{"LangCultureName": "gu-IN", "DisplayName": "Gujarati - India"},
{"LangCultureName": "he-IL", "DisplayName": "Hebrew - Israel"},
{"LangCultureName": "hi-IN", "DisplayName": "Hindi - India"},
{"LangCultureName": "hu-HU", "DisplayName": "Hungarian - Hungary"},
{"LangCultureName": "is-IS", "DisplayName": "Icelandic - Iceland"},
{"LangCultureName": "id-ID", "DisplayName": "Indonesian - Indonesia"},
{"LangCultureName": "it-IT", "DisplayName": "Italian - Italy"},
{"LangCultureName": "it-CH", "DisplayName": "Italian - Switzerland"},
{"LangCultureName": "ja-JP", "DisplayName": "Japanese - Japan"},
{"LangCultureName": "kn-IN", "DisplayName": "Kannada - India"},
{"LangCultureName": "kk-KZ", "DisplayName": "Kazakh - Kazakhstan"},
{"LangCultureName": "kok-IN", "DisplayName": "Konkani - India"},
{"LangCultureName": "ko-KR", "DisplayName": "Korean - Korea"},
{"LangCultureName": "ky-KZ", "DisplayName": "Kyrgyz - Kazakhstan"},
{"LangCultureName": "lv-LV", "DisplayName": "Latvian - Latvia"},
{"LangCultureName": "lt-LT", "DisplayName": "Lithuanian - Lithuania"},
{"LangCultureName": "mk-MK", "DisplayName": "Macedonian (FYROM)"},
{"LangCultureName": "ms-BN", "DisplayName": "Malay - Brunei"},
{"LangCultureName": "ms-MY", "DisplayName": "Malay - Malaysia"},
{"LangCultureName": "mr-IN", "DisplayName": "Marathi - India"},
{"LangCultureName": "mn-MN", "DisplayName": "Mongolian - Mongolia"},
{"LangCultureName": "nb-NO", "DisplayName": "Norwegian (Bokmål) - Norway"},
{"LangCultureName": "nn-NO", "DisplayName": "Norwegian (Nynorsk) - Norway"},
{"LangCultureName": "pl-PL", "DisplayName": "Polish - Poland"},
{"LangCultureName": "pt-BR", "DisplayName": "Portuguese - Brazil"},
{"LangCultureName": "pt-PT", "DisplayName": "Portuguese - Portugal"},
{"LangCultureName": "pa-IN", "DisplayName": "Punjabi - India"},
{"LangCultureName": "ro-RO", "DisplayName": "Romanian - Romania"},
{"LangCultureName": "ru-RU", "DisplayName": "Russian - Russia"},
{"LangCultureName": "sa-IN", "DisplayName": "Sanskrit - India"},
{"LangCultureName": "Cy-sr-SP", "DisplayName": "Serbian (Cyrillic) - Serbia"},
{"LangCultureName": "Lt-sr-SP", "DisplayName": "Serbian (Latin) - Serbia"},
{"LangCultureName": "sk-SK", "DisplayName": "Slovak - Slovakia"},
{"LangCultureName": "sl-SI", "DisplayName": "Slovenian - Slovenia"},
{"LangCultureName": "es-AR", "DisplayName": "Spanish - Argentina"},
{"LangCultureName": "es-BO", "DisplayName": "Spanish - Bolivia"},
{"LangCultureName": "es-CL", "DisplayName": "Spanish - Chile"},
{"LangCultureName": "es-CO", "DisplayName": "Spanish - Colombia"},
{"LangCultureName": "es-CR", "DisplayName": "Spanish - Costa Rica"},
{"LangCultureName": "es-DO", "DisplayName": "Spanish - Dominican Republic"},
{"LangCultureName": "es-EC", "DisplayName": "Spanish - Ecuador"},
{"LangCultureName": "es-SV", "DisplayName": "Spanish - El Salvador"},
{"LangCultureName": "es-GT", "DisplayName": "Spanish - Guatemala"},
{"LangCultureName": "es-HN", "DisplayName": "Spanish - Honduras"},
{"LangCultureName": "es-MX", "DisplayName": "Spanish - Mexico"},
{"LangCultureName": "es-NI", "DisplayName": "Spanish - Nicaragua"},
{"LangCultureName": "es-PA", "DisplayName": "Spanish - Panama"},
{"LangCultureName": "es-PY", "DisplayName": "Spanish - Paraguay"},
{"LangCultureName": "es-PE", "DisplayName": "Spanish - Peru"},
{"LangCultureName": "es-PR", "DisplayName": "Spanish - Puerto Rico"},
{"LangCultureName": "es-ES", "DisplayName": "Spanish - Spain"},
{"LangCultureName": "es-UY", "DisplayName": "Spanish - Uruguay"},
{"LangCultureName": "es-VE", "DisplayName": "Spanish - Venezuela"},
{"LangCultureName": "sw-KE", "DisplayName": "Swahili - Kenya"},
{"LangCultureName": "sv-FI", "DisplayName": "Swedish - Finland"},
{"LangCultureName": "sv-SE", "DisplayName": "Swedish - Sweden"},
{"LangCultureName": "syr-SY", "DisplayName": "Syriac - Syria"},
{"LangCultureName": "ta-IN", "DisplayName": "Tamil - India"},
{"LangCultureName": "tt-RU", "DisplayName": "Tatar - Russia"},
{"LangCultureName": "te-IN", "DisplayName": "Telugu - India"},
{"LangCultureName": "th-TH", "DisplayName": "Thai - Thailand"},
{"LangCultureName": "tr-TR", "DisplayName": "Turkish - Turkey"},
{"LangCultureName": "uk-UA", "DisplayName": "Ukrainian - Ukraine"},
{"LangCultureName": "ur-PK", "DisplayName": "Urdu - Pakistan"},
{"LangCultureName": "Cy-uz-UZ", "DisplayName": "Uzbek (Cyrillic) - Uzbekistan"},
{"LangCultureName": "Lt-uz-UZ", "DisplayName": "Uzbek (Latin) - Uzbekistan"},
{"LangCultureName": "vi-VN", "DisplayName": "Vietnamese - Vietnam"}
]
  • 單位
  • 時區
    • 跨時區
    • 日光節約時間
  • 其它
    • 地址
    • 姓名
    • 電話號碼

(fin)

[內部分享]Cache 使用 Files 與 Redis

Why Cache ?

  1. 保護稀缺資源(database/disk io/server)
  2. 提高反應速度,減少延遲(Latency)

Cache Point

  1. 資料即時性/如何除錯/如何更新
  2. Hit Rate
  3. High Availability(HA)
  4. 單位時間承載量

Our Cache

  1. CDN(Cloudfront)
  2. OutputCache
  3. Memory Cache
  4. Redis Cache
  5. File Cache

More Cache not in above

  1. Browser Cache
  2. Database Execution Plan Cache (執行計劃快取)
  3. Elastic Search

File Cache

  • pro
    • 簡單易用
  • con
    • 咬檔
    • 不會自動過期
    • 自已要處理 lock 機制

Redis Cache

  • pro
    • 會自動過期
    • 有提供 Queue 的功能
    • 高速(in memory)
  • con
    • 單緖(Single Thread)
    • HA 的機制費用高
    • Redis 承載量受限於記憶體大小
    • 存儲的東西過大,會導致存取速度下降

實踐問題:在大流量的站台,Cache 過期時如何避免大量 Request 同時更新同一份 Cache ?

Ans: 使用 Lock ,
不要使用.NET 的lock ;.NET Lock 的機制問題在於,Lock 會導致其它 Thread 排隊
當系統處於高流量的情境底下,會發生間歇性 Latency 提高,
而且難以追查問題,也難以重現(一般的壓測無法達到如此高量)。

使用共用的 Lock Key 作判斷標準:

  1. Lock Key 存在,回傳 Cache Data
  2. Lock Key 不存在,建立 Lock Key(包含 Server Id)
  3. 重新取得 Lock Key 如果
    • Server Id 與 Request 相同,更新 Cache Data 並回傳
    • Server Id 與 Request 不相同,直接回傳 Cache Data

[Cache Double check Lock Patter(https://docs.microsoft.com/en-us/previous-versions/office/developer/sharepoint-2010/ee558270(v%3Doffice.14)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private static object _lock =  new object();

public void CacheData()
{
SPListItemCollection oListItems;
oListItems = (SPListItemCollection)Cache["ListItemCacheName"];
if(oListItems == null)
{
lock (_lock)
{
// Ensure that the data was not loaded by a concurrent thread
// while waiting for lock.
oListItems = (SPListItemCollection)Cache["ListItemCacheName"];
if (oListItems == null)
{
oListItems = DoQueryToReturnItems();
Cache.Add("ListItemCacheName", oListItems, ..);
}
}
}
}

Other

  1. 使用多執行緖時要小心處理 Exception,不然會導致主程序被中斷
    (File Cache 寫入真實案例,多緖的錯誤不佳會使得 IIS 無法處理錯誤,進而中斷程序(w3wp.exe))
  2. AWS S3 是走 Https Protocal
  3. Redis Delete/Backup 會 Lock Thread
  4. Redis Command Timeout 極短,所以要注意單一 Cache 資料量大小 (建議資料要小於 1k?)

參考

(fin)

[異常記錄] 多語系架構上線

前情提要

很久很久很久以前,
【跨國語系基礎架構】上線了.
又過了很久很久 我們評估這多語系 API 需要上 CDN
我們評估這多語系 API 需要上 CDN

Release 項目與工作內容

  • 8/16 要 release 什麼?
    我們要將多語系開始開發的項目一次回台灣正式環境。
  • 我們作了什麼
    • sp23/sp24/sp25/sp26/sp27
    • 前端多語系底層架構的調整(含 CDN)
    • 國別設定
    • 還有一大堆 Merge 解衝突的項目…
  • 為什麼要等這麼久才上線 ?
    風險考量,我們先上了基礎架構,
    在 G3 環境上觀察了一段時間確定沒有問題才上線。
  • 講到風險,我們有多少保險 ?
    • 只上基礎架構 (讓異動最小)
    • 只上 G3 環境 (讓 impact 最小)
    • 藍綠部署 (標配,讓壞掉的時間儘可能縮短)
    • Merge Before Build (讓原始碼不受影響,rollback 時不會拔掉其它團隊的功能)

講那麼多,還不是壞掉了。

錯誤原因

錯誤畫面

錯誤畫面

錯誤原因

錯誤原因

多語系 CDN 的 Response Header Access-Control-Allow-Origin Protocol 與主頁面不符;

流程面

測試環境 測試結果
QA8 OK
QA OK
PP OK
鎖 Host 測試 OK
半數機器上線 OK
全數機器上線 OK,好 (壞了)

為什麼全數機器上線才發生異常。

  • QA8 沒有 CDN
  • 鎖 Host/半數機器上線未開 CDN
  • QA / PP CND 設定與正式環境不一致
    • RD 並未 Check 設定的一致性
    • RD 沒有權限

技術細節

  1. 使用 Partial View 與 OutputCache
  2. 小心 Browser Cache
  3. 站台上會有跨 Domain 的存取行為
    • Service to Official
    • Service to CDN
    • Official to Service
  4. CDN & Access-Control-Allow-Origin
  5. 稍微提一下 CORSModule

錯誤分析

錯誤分析

TranslationCDN : 用來防止瞬間量對 Server 的 Impact
FingerprintTag : 一份Cache的時戳, 用以取得新版多語系 Json 檔
site : BrowserCache 遇到跨 Domain 存取時, 會發生 CORS Error
  1. Partial View 會有 OutputCache
    → 保得了一時,保不了一世
  2. 為了 Browser Cache ,F2E 在 QueryString 作了加工。
  3. 網站跨 Domain 的存取行為,歷史共業。
  4. CDN & Access-Control-Allow-Origin
  5. CorsModule,有必要區分 protocol 嗎 ?

架構圖

1
2
3
4
if (httpContext.Request.Url.Scheme == Uri.UriSchemeHttps && (sourceUri == null || sourceUri.Scheme == Uri.UriSchemeHttps))
{
allowOrigin = allowOrigin.Replace("http:", "https:");
}

問題處理經過

  1. 第一時間 QA 有看到異常, 但是在 PO 的環境無法重現, RD 初步判斷為快取的問題, 並嘗試重現錯誤 。

  2. SRG 收到通報有客戶反應顯示異常,立即啟動藍綠部署,耗時 93 分鐘

  3. 縮小範圍至 CDN 與 Access-Control-Allow-Origin 異常,但是仍然無法完整重現錯誤(時好時壞)

  4. 申請 CDN 權限,確認各環境的設定值。

  5. 申請 QAn 測試用 CDN,在 QA8 環境反覆測試,
    發現頁面 OutputCache 會影響測試
    關閉 QA8 OutputCache
    測試步驟如下:

  6. 確認 Prod CDN 的 WhiteList Header 的設定有問題,
    那為什麼 PP/G3 不會有問題呢 ?
    => 在 PP/G3 設定為 Referrer

  7. 測試過程發現 x-cache Header 常常
    Miss from cloudfront,
    表示資料並非來自 CDN .
    實際測試 Origin 比 Referrer 更適合

回饋與後續

1. 團隊評估欠缺數據 →
2. 客戶反應異常。
3. 藍綠部署異常回覆速度,耗時 93 分鐘 → `已有RD著手改善`
4. 無法完整重現錯誤
5. RD 沒有 CDN 權限 → `已申請`
6. QAn 沒有設定 CDN → `沒有CDN的Issue沒必要特別設定(因為需要對外)`
7. 頁面 OutputCache 會影響測試。
8. PP / G3 與 Prod  CDN設定不一致 → `webapi/translate/* 已調整一致`
9. Release 三步驟 , 最後一步才會開啟 CDN

Q. 下次我們怎麼作 ?

補充資料

  • 什麼是 CDN ?
    什麼是 CDN

    Server 跟 CDN 就像工廠與便利店的關係, 你要買東西不用跑到工廠
    只要在離自已最近的便利店買就好了, 過期的商品(資料)會被丟掉
    重新跟工廠進貨(拉新資料)。
    
  • 什麼是 CORS ?

  • 什麼是 Origin

  • 什麼是 Referrer

  • Slide

(fin)

[踩雷筆記] .Net專案升級 SpecRun.Runner 與調整 CI

前情提要

  • 專案使用 Specflow 寫 BDD
  • Visual Stuio 找不到測試所以才升級 SpecRun 與 Specflow
  • Visual Studio 2017 升級到 15.8.1 後突然無法正常搜尋到測試
  • SpecRun.Runner 升級(1.2 → 1.8.2)

去年 12 月跟 Visual Studio 折騰了許久,才讓專案的測試項目重見光明
在今年與其它專案合併後,測試又從我們團隊的眼前消失了。

調整項目

升級 Specflow 與 SpecRun 相關套件
升級 Specflow 與 SpecRun 相關套件

取得 SpecRun 位於專案的 packages 資料夾中。
取得 SpecRun 位於專案的 `packages` 資料夾中。

設定 CI

由於 SpecRun 版本不符會導致 CI Job Error , 需要設定以新的 SpecRun 執行 CI

D:\SpecRun.Runner.1.8.2\tools\SpecRun.exe run D:\Project\Core.Test\bin\Debug\Core.Test.dll /baseFolder:Core.Test\bin\Prod /toolIntegration:vs201 0 /reportFile:Core.Test.TestResult.html

參考

(fin)

Please enable JavaScript to view the LikeCoin. :P