---
# System prepended metadata

title: 統整筆記-T

---

# 統整筆記

[toc]

## JWT
- jwt
    - 通常用來將 json 的驗證資訊編碼後存在 client 端 (ex. cookie)，client reqeust 會將 jwt token 給 server 做驗證
    - 組成
        - header
            - 包含 signature 採用的非對稱式加密的演算法種類（ex. SHA）
            - 以 base64 編碼
        - payload
            - 實際要傳輸的 json object
            - 以 base64 編碼
                - base64 編碼是可逆的，不能存敏感資訊
        - signature
            - 將 base64 編碼後的 header, payload, secret 以 header 的演算法做 hash
    - 優
        - 可以確保 payload 沒有被更改
        - 可以設 exp，過期期限
        - 不用存 session 在 server
            - 省空間, 查詢時間
            - 如果有多台 server 不須同步 session
    - 缺
        - 無法像 session 一樣主動讓 token 無效
            - sol : 
                - exp 設短一點
                - 限制使用次數 
- docker swarm vs k8s
    - docker sawrm
        - 可以讓多台 host 組成 cluster 共同管理 container
        - 有基本的 ha, load balance 的功能
            - service 可以用多個 task 部屬在不同台 host
                - 且其中的 host 有問題，manager node 將有問題的 host 的 container 移到別台部屬
            - 預設用 round-robin loaf balance service 的流量到不同 task
        - 指令簡單
            - 基本上和 docker 指令差不多，部屬也是用 `docker-compose.yml`
            - 會 docker 上手就很快
        - docker swarm 原生少了一些進階的功能
            - service 沒有 auto scaling 的功能
                - 所以如果要有這個功能就要自己寫腳本去監控（ex. 如果某個服務目前的 cpu, memory 使用量超過一定百分比就擴展多少個…等等的方式）
            - k8s rbac 的機制較好管理存取 k8s api 的權限
            - k8s 的 ingress 可以統一 host 對外的 port 號，並且再用 hostname or path name 去區分 service（service 不用一定要開在 host ip 的 port）
                - 雖然 docker swarm 也可以用 traefik 做到，但我覺得 ingress 設定比較方便
                - https://blog.aotoki.me/posts/2022/07/08/rails-deployment-in-practice-deploy-to-docker-swarm/
        - k8s 將功能模組化的較完整，降低模組的耦合，較好針對特定功能調整
            - ex. 可以用 service 決定如何存取 deployment 下的 pod，service 又有很多不同的 type (clusterip, nodeport, load balancer)
            - ex. k8s rbac 由 ServiceAccount, ClusterRole, ClusterRoleBinding 組成
                - 因為一個 ClusterRole 可以綁定（ClusterRoleBinding）給多個 Service account，也可以一個 Service account 綁定多個 ClusterRole，或是多對多。
        - k8s 背後是 google 且有強大的社群維護
        - Google, Azure, and AWS 都支援 k8s instance，不用另外再下載和安裝


## `==` vs `===` vs `Object.is`
- `==` vs `===` vs `Object.is`
    - `===` : 先比較 type，再比較 value
    - `==` : 先做型別轉換，再比較 value
    - `Object.is` : 和 `===` 差在下面兩個而已
        - `Object.is(NaN,NaN)` 為 true
        - `Object.is(+0,-0)` 為 false
    - object type 的比較 : object value 是記憶體位址 
        ```js=
        a = new User("a");
        b = new User("b");
        a == b; // false
        a === b; // false
        ```
        - 所以除了用 for loop 一個一個跑 check，還可以用 `JSON.stringfy` check 相等
            ```js=
            a = new User("a");
            b = new User("b");
            JSON.stringfy(a) === JSON.stringfy(b); // true
            ```
    - ex.
        ```js=
        var yiifaa = 'yiifaa'
        str1 = new String('yiifaa')
        str2 = String('yiifaa')
        
        yiifaa === str2; // true, Object.is 也是
        str1 === str2; // false, Object.is 也是
        str1 == st2; // true, 會先 str1.toString()
        ```
- setTimeout vs setInterval
    - setTimeout = 過幾秒執行一次（只會執行一次
    - setInterval = 每過幾秒就執行一次（會一直執行
## es6
- es6
    - `array.map`
        - 將 array 中 element 都跑過一遍，不修改原本的 array，會回傳新的 array（pure function）
        - ![](https://hackmd.io/_uploads/r1m0ZlWe6.png)
        - ![](https://hackmd.io/_uploads/HyqlfgZg6.png)
        - note : array 的 deepcopy
            - `Array.from(a)`
            - ![](https://hackmd.io/_uploads/ry_QLHSGp.png)
    - `array.filter`
        - ![](https://hackmd.io/_uploads/HJtYQOiza.png)
    - `array.foreach`
        - ![](https://hackmd.io/_uploads/Sy3jQusGp.png)
    - arrow function
- es7
    - ![](https://hackmd.io/_uploads/BJljC-gzp.png)
        - https://ithelp.ithome.com.tw/articles/10249963
- es8
    - async & await
    - `Object.keys`
    - `Object.values`
    - 把 Object 變成二維陣列：`Object.entries`
        ```js=
        for (let [key, value] of Object.entries(menu)) {
            console.log(`${key} : ${value}`)
        }
        ```
- es9
    - `finally`
        - ![](https://hackmd.io/_uploads/HyXmJMgzT.png)
- Event Bubbling
    - 由內到外觸發 event handler
- Event capturing
    - 由外到內觸發 event handler

- 127.0.0.1 vs localhost vs 0.0.0.0
    - 127.0.0.1 : loopback address，指向本機
    - localhost : domain name，通常在 `/etc/hosts` 下都會指向 127.0.0.1
    - 0.0.0.0 : 在 server 的部屬上，代表指向 host 中所有的 interface。所以如果把服務開在 0.0.0.0:5000，代表可以用這台 host 上的所有 ip 存取。
        - 在 route 中，是代表 default gateway

- selinux
    - 基本上如果只有一個 user 有 root 權限關掉沒什麼關係

- Git rebase 可以達成什麼問題？
- es6 const, let, var 之間差別是什麼？
- 用過哪些後端框架？
- 用過哪些前端框架？
- CORS 是什麼？
- Content secure policy 是什麼？
    - Content Security Policy (CSP) 
        - 用來限制網頁對外部的 request 的 origin，只能送白名單的 origin
        - 防止被 XSS
            - 去執行依些惡意的腳本
        - 要注意一些 cdn 不要被擋掉
        - 可以在 apache, php, html 設定
            - ex. html
                ```html=
                <!DOCTYPE html>
                <html>
                <head>
                    <meta charset="UTF-8">
                    <meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src ‘self’ http://js.devco.re;img-src 'self' data:;">
                    <title>test</title>
                </head>
                <body>
                    <p>test</p>
                    <img src="example.jpg">
                </body>
                </html>
                ```
- User 和 Group 如何雙向查詢？設計資料庫的結構?
    - 若 user 是可以加入多個 group 就 3 張 table
    - 只能加一個就 2 張
- 設計教授實驗室和負責人的 RESTFUL API 怎麼設計
    - Restful API
        - url 內容的部分的 root = api
        - ![](https://hackmd.io/_uploads/ByQlqHG-T.png)
        - get
            - 將傳送的資料轉為 query string 加再 url 後面
            - 如果用 get 來更新資料會有點危險，因可能別人隨便傳給你連結就點，結果是亂帶資料的
                - 或是不小心點到錯誤網址
            - 好處是可以藉由 url 傳參數，使用者也方便知道參數
        - post 
            - 將資料放入 request 的 body
        - 用正確的 method 可以增加 SEO
    - get
        - 獲得所有實驗室資料
            - `/api/lab`
        - 獲得單一實驗室資料
            - `/api/lab/{lab_id}`
    - post
        - 建立新實驗室資料
            - `/api/lab`
    - put
        - 更新單一實驗室資料
            - `/api/lab/{lab_id}`
        - 更新單一實驗室部分資料
            - 更新實驗室負責人資料
                - `/api/lab/{lab_id}/manager`
    - delete
        - 刪除單一實驗室資料
            - `/api/lab/{lab_id}`
    - ref
        - https://igouist.github.io/post/2021/05/newbie-2-webapi/
- 圖書館的系統，會把 email serialize 出來，但不知道為什麼會這麼慢？
- 列出可能的原因，並探討可能的解決方法。
- mock spy stub 之間的差異是什麼 ？
- function 和 arrow function 之間的差別？

- SEO (search engine optimization)
    - 有其他連結連到此網站
    - 把 HTML 弄單純一點

- CSS
    - Specificity 優先順序 : `!important` > element inline > id > class > element > `*`
        - 相同優先權，看後者（較下面的 code
        - ![](https://hackmd.io/_uploads/ByjhWoK0n.png)
        - 通常不會一開始就用 id 而是 class，因若之後要擴充不容易（id 權限比 class 大）
    - 針對 atrribute (只要 element 有這個 attribute)
        - ![](https://hackmd.io/_uploads/BJXvhsFC3.png)
            - 這邊若是 `<input/>` : 沒有設 `value` 的 attribute 就不會是紅色
            - 其它自己取的 attribute 也可以
    - 針對 attribute 的 value (attribut 值須相同)
        - ![](https://hackmd.io/_uploads/HJnd3jF0n.png)
    - selector
        - `A, B` : 有 A or B
        - `A B` : A 之後（裡面）的所有 B
            - ![](https://hackmd.io/_uploads/S1cCfUHGa.png)
            - ![](https://hackmd.io/_uploads/B1RFzIHGa.png)
            - ![](https://hackmd.io/_uploads/ryPJ78Sza.png)
        - `.A.B` : 同時有 class A and class B
            - `<div class = "A B">123</div>`
                - 這個 element 同時有兩個 class
                - class 可多個，但只能有一個 id
        - `A > B` : A 之後的下一層 B，可用來指定哪一層
            - ![](https://hackmd.io/_uploads/SyC_GUrz6.png)
            - ![](https://hackmd.io/_uploads/B1RFzIHGa.png)
            - ![](https://hackmd.io/_uploads/Byy3fIHMp.png)
                - 但要注意的是裡面的所有 element color 也會被改到
        - `A + B` : A 後面的一個 B （A B 同層）
            ```html=
            <div class = "a">
            test
          </div>
          <div class = "b">
            被選
          </div>
          <div class= "b">
            沒被選
          </div>
            ```
        - `A ~ B` : A 後面的全部 B （A B 同層）
            - ![](https://hackmd.io/_uploads/HkB1aBjG6.png)
            - ![](https://hackmd.io/_uploads/B1a46Hofp.png)
            - ![](https://hackmd.io/_uploads/HyM4TSoMa.png)
    - SCSS
        - 巢狀結構的 CSS
    - display:none vs visibility:hidden
        - none : 直接讓 element 位置消失
        - hidden : 只會讓 element 內容消失
    - display
        - `block` : 會佔一整區塊 ex. `div`
        - `inline` : 只佔自己元素的大小 ex. `span`
        - `flex` : 便於排版
    - position
        1. `static` : 預設的
        2. `relative` : 可以設定元素相對位置，用 `top`, `bottom`,`left`, `right` 可以調整 element 位置
        3. `absolute` : 自己從文件中抽離而不佔位置，而是會去佔有定位（relative, absolute, fixed）的上層 element 的位置，若上層沒有就一直往上找直到有就停，若都沒有最後就會定位在 window，也就是初始的視窗（不會定位在整份 html，所以滾動視窗不會像 `fixed` 一樣會跟著滾動）
            - https://www.youtube.com/watch?v=JOdZdHnuGmM
        4. `fixed` : 隨著 scroll 移動位置
        - `z-index` : element 顯示的前後順序
    - css box model
        1. `margin` 
            - element <b>之間</b> 的距離，隔開相鄰的 element 多少
            - ![](https://hackmd.io/_uploads/S1WgPIHMp.png)
            - ![](https://hackmd.io/_uploads/rkq7DISMp.png)
        2. `padding`
            - element <b>內部</b> 內容(可能是其他 element)的距離，把 element 往裡面擠多少，同時會把外面的 element 撐大
                ```html=
                <html>
                <style>
                #test2 {
                  color : hsl(120, 100%, 25%);
                  padding: 100px 50px 50px 500px; // 上右下左
                }
                </style>
                <body>
                <div id = "test2">
                    <div id = "test3">test3</div>
                </div>
                </body>
                ```
            - ![](https://hackmd.io/_uploads/r1bv3ydkT.png)

        3. `border`
            - 邊線本身的設定
    - bootstrap
        - grid system
            - 一行總共可以切 12 格，可以不同大小的裝置設定
            - ![](https://hackmd.io/_uploads/SJ524DdyT.png)
        - 常用的套件
            - sidebar
            - pagination
            - button
            - drop-down 下拉選單
    - rowspan
        - ![](https://hackmd.io/_uploads/BJgN-q9fT.png)
    - :before 跟 :after 用途？
        - 把 content 放在元素前後面
- JS
    - handler 處理 event 發生後的事情. ex. `onclick`
    - bun : 一樣是 runtime, 但比 nodejs 快
    - `__proto__` vs `prototype`
        - `__proto__`
            - 念作 dunder prototype
            - 每一個 object 都會有一個隱藏屬性 `[[Prototype]]`，可以透過 `__proto__` 存取
            - 如果 object 的 attribute 中找不到對應的 attribute，就會去 `__proto__` 找直到找到或到達終點 `null` 為止，期間可能會經過多個 `__proto__`，途中經過的就是這個 object 的 protoype chain
                - ![](https://hackmd.io/_uploads/SJwp398k6.png)
                - ![](https://hackmd.io/_uploads/SyTR3cLkp.png)

            - 指向繼承的 `function.prototype` (原型鏈)
            - Object 就是任何東西原型鏈的頂點，再上去就是 `null`
                - 所以基本上就只有 object, null, undefined 3 種類型
                - ex. 
                    - ![](https://hackmd.io/_uploads/B1uKMPxyp.png)
        - `prototype`
            - 只有 function 有，包含此 function prototype 的 attribute，代表用此 function new 的 instance 的 `__proto__` 會指向 `function.prototype`
            - `prototype` 的屬性（variable, function）都是共享的，可以減少資源浪費
                ```js=
                 function DOG(name){

                    this.name = name;

                  }

                  DOG.prototype = { species : '犬科' };


                  var dogA = new DOG('大毛');

                  var dogB = new DOG('二毛');


                  console.log(dogA.species); // 犬科

                  console.log(dogB.species); // 犬科
    
                  DOG.prototype.species = "貓科";

                  console.log(dogA.species); // 貓科

                  console.log(dogB.species); // 貓科
                ```
                - 但若有 object 把設自己的屬性且和共享屬性相同，就會覆蓋掉共享屬性（因為先找到自己的）
                    - ![](https://hackmd.io/_uploads/SylOQwHza.png)
                    - ![](https://hackmd.io/_uploads/SySDXDSGT.png)
            - function 建立時會被加一個 `prototype` 且 constructer 為自己
                - ![](https://hackmd.io/_uploads/S1oIK9xyp.png)
        - 若用 `new` 去 create instance，該 instance 的 `__proto__` 就會繼承 fcuntion 的 `prototype`
            - ![](https://hackmd.io/_uploads/rJh9q9xJp.png)
            - ![](https://hackmd.io/_uploads/rk2Spt8Ja.png)
    - new operator 跟 Object.create 都做了繼承物件
        - 先建一個空 object
            - `const obj = {};`
        - 再把 function 的 `this` 的 property(屬性) 給這個 object
            ```js=
            function User(name) {
                this.name = name;
                pig = "456";
                this.hello1 = function() {
                    console.log("hello1", pig);
                }
            }

            const a = new User("test");
            console.log(a.name); // test
            console.log(a.pig); // undefined
            a.hello1(); // hello1 456
            ```
            
            - `this` 的值是動態改變的, 會隨著呼叫 function 的 object(function 需綁訂於 object) 的 property 而變
                ```js=
                function User(name) {
                    this.name = name;
                    pig = "456";
                    this.hello1 = function() {
                        console.log("hello1", pig);
                    }
                }

                  User.prototype.hello = function () {
                    return this.name;
                  };

                  function speak() {
                    console.log('Hello world! My name is ' + this.name);
                  }


                  const user = new User('Peter');
                  user.speak = speak;
                  user.speak(); // Hello world! My name is Peter
                ```
                - https://www.shubo.io/javascript-this/#this-%E7%9A%84%E5%80%BC%E6%98%AF%E5%8B%95%E6%85%8B%E6%B1%BA%E5%AE%9A%E7%9A%84
            - 直接呼叫 function 的 this 會指向 global
                - ![](https://hackmd.io/_uploads/H15YWWZla.png)
                - https://chupai.github.io/posts/2008/js_this/
            - https://www.shubo.io/javascript-new/
        - 再把 `obj.__proto__` = `function.prototype`
        - 差別在於會不會執行 constructor
        - ![](https://hackmd.io/_uploads/HJ84U5IkT.png)
        - no new 
            - 不用 new 代表不會建立一個新的 object，而是單純 call function 而已
            - 如果 function return `this`


    - textContent vs innerContent vs innerHTML vs innerText
        - textContent : 選取所有字元，包含空白
        - innerContent : 選取所有字元，不包含空白
        - innerHTML : 選取所有字元，包含 html 標籤
            - XSS 漏洞 : 雖然 innerHTML 不會去執行內容的 `<script>` tag，但可用 button, img 等方式執行 script。但有時候要幫內容加 tag 還是要用 innerHTML
                - sol : 用 textContent 過濾再放入使用者內容，再用 innerHTML 加上 html tag
        - innerText : 選取所有字元，僅適用於 IE
    - 同步 vs 非同步
        - 同步 : 等一行執行完才會到下一行
            - ex. nodejs : `fs.readFileSync('./README.md')`
        - 非同步 : 可先去執行別行，等之前的執行完再執行 callback function
            - ex. `fs.readFile('./README.md',readFileFinished);`
    - callback
        - 一個 function 裡面在 call 別的 function，可確保執行順序，目前這個 function 跑完，才去 callback 下一個 function
        - 常見於封裝 XMLHttpRequest 的 ajax
        - ![](https://hackmd.io/_uploads/BJ9QBQHyT.png)
        - callback hell
            - 如果要連續呼叫多個有上下關係的 api，就會變成巢狀不易維護和閱讀
            - ![](https://hackmd.io/_uploads/r1B48XB1a.png)
    - promise
        - 為了解決 callback hell 的問題而產生的 object
            - ![](https://hackmd.io/_uploads/S1eHqft16.png)

        - 3 種狀態
            - pending : 等待中
            - fullfilled : 成功，執行 `then` 裡面的 code
            - rejected : 失敗，執行 `catch` 裡面的 code
        - ex.
            ```js=
            myName()
          .then((res) => console.log('成功：'+ res))
          .catch((error) => console.log('失敗：' + error));
            ```
        - 多種寫法
            1. `Promise.all` : 一次等待所有 promise 完成才做 `then`，且多隻 api 是同時執行（不是一個執行完換下一個），適合打多隻 api 的，但是若有一隻 error 就不會顯示其他正確的 api 的訊息
                - ![](https://hackmd.io/_uploads/BkezO7Bya.png)
                    - `resolve` : 回傳成功的訊息
                    - `reject` : 回傳錯誤的訊息
                - 如果上下 api 沒有關係，可以用此來加快速度
                    - ![](https://hackmd.io/_uploads/ByunEEryp.png)
                        - 需一個一個等待，較慢
                    - ![](https://hackmd.io/_uploads/HyVA4ESya.png)
                        - 一起做，較快

            2. `Promise.allSettled` : 和 `all` 差別在，會回傳所有 api 的訊息（就算有 error），回傳時會多帶一個 `status` 代表成功 or 失敗
                - ![](https://hackmd.io/_uploads/rJqS4NrJT.png)

            3. `Promise.race` : 只做先完成的
                - ![](https://hackmd.io/_uploads/BktddXB1a.png)
        - 教學
            - https://israynotarray.com/javascript/20211128/2950137358/
    - async/await
        - 解決 promise 還要寫 then, catch，還是有可能變巢狀結構，更易讀
        - async
            - function 加上 async，代表會回傳一個 promise 的 object
        - await
            - await 會等其後的 promise object 結束（resolved or rejected），再繼續執行
            - await 需在 async function 內用
    - axios vs ajax vs fetch
        - ajax
            - 不支援 promise，要用 callback
        - fetch
            - 基於 promise，所以可以用 then、async+await，js 原生支援，不須引入
            - request body 的 data 需先 json.stringify
        - axios
            - 基於 promise，所以可以用 then、async+await
            - 較 fetch 易用
                - 可直接 `axios.post`，fetch 需寫在裡面
                    - ![](https://hackmd.io/_uploads/rJ3R14Hyp.png)

                - 不須 stringify，直接送 json
    - call stack
        - js runtime 是單執行緒的（只有 js engine 的 main thread），所以一次只會執行一行程式
            - browser js engine 把要執行（有呼叫(call)）的 function 和它的環境（ex. return address, local variable） push 到 stack 再做執行，當 function 結束會從 stack 中 pop 掉
                - 不限於 js，其他語言也會
                - stack 的容量是有限的，因此無止盡的 call function 會導致 stack overflow
            - ![](https://hackmd.io/_uploads/Hyq4nvI1p.png)
                - js engine 會把 js 內容包成一個 `main()`，再去 call 這支 main function
                    - ![](https://hackmd.io/_uploads/r1HsmeIM6.png)
                    - ![](https://hackmd.io/_uploads/ByE3mxUf6.png)
                    - ![](https://hackmd.io/_uploads/BJUhveLzp.png)
        - 但除了 js engine main thread，可以利用 browser 提供的 WebAPIs(ex. DOM(ex. onclick)、setTimeouts、AJAX) 來達成非同步，也就是讓 webapis thread 去做。等它做完後會把 event 對應的 callback function 加到 task queue，而 event loop 會持續檢查<b>當 call stack 為空</b>且 task queue 有 task，就會把 task queue 最前面的 task push 到 callstack
        - ![](https://hackmd.io/_uploads/H1OQPu8JT.png)
            - main 代表檔案本身，檔案最初就會載入，所以 setTimeout 的 callback function 會等到 main 結束後（callstack 為空）才會被 push 到 stack 執行
            - 所以這是為啥 `setTimeout(0, callback)` 會等到整個 js 執行完才會做，因為要等 stack 中的 main 執行完 event loop 才會把 task queue 中的 setTimeout push 到 call stack
        - browser 每 16.6 ms 會 render 一次頁面，但若是 callstack 有其他的 js 在跑 ex. for loop，就無法 render，此時頁面就會卡住不能動，因只有一 thread
            - ![](https://hackmd.io/_uploads/r1zdIKUya.png)
            - 但如果是用非同步的作法 ex. 每個 loop 都加上 setTimeout，就會被放進 callback queue，和 render queue 輪流執行
        - ref 
            - 教學 : https://www.youtube.com/watch?v=8aGhZQkoFbQ
            - 測試工具 : http://latentflip.com/loupe/?code=JC5vbignYnV0dG9uJywgJ2NsaWNrJywgZnVuY3Rpb24gb25DbGljaygpIHsKICAgIHNldFRpbWVvdXQoZnVuY3Rpb24gdGltZXIoKSB7CiAgICAgICAgY29uc29sZS5sb2coJ1lvdSBjbGlja2VkIHRoZSBidXR0b24hJyk7ICAgIAogICAgfSwgMjAwMCk7Cn0pOwoKY29uc29sZS5sb2coIkhpISIpOwoKc2V0VGltZW91dChmdW5jdGlvbiB0aW1lb3V0KCkgewogICAgY29uc29sZS5sb2coIkNsaWNrIHRoZSBidXR0b24hIik7Cn0sIDApOwoKY29uc29sZS5sb2coIldlbGNvbWUgdG8gbG91cGUuIik7!!!PGJ1dHRvbj5DbGljayBtZSE8L2J1dHRvbj4%3D

    - strong vs weak type
        - weak type lanaguage
            - 會自動轉換 type
            - ex. js
                ```js=
                let a = 1;
                let b = "1";
                console.log(a+b); // 11
                ```
            - ex. c/c++ : int 會被轉換成 boolean
        - strong type language 
            - 不會自動轉換 type
            - ex. java, c#
    - dynamic vs static type
        - static
            - 需先宣告 vairable type
        - dynamic
            - 不須，交給編譯器判斷
    - ![](https://hackmd.io/_uploads/SyDKOP_yp.png)
    - javascript = dynamic + 超級 week type
        - js 的 `+` 有 2 個意思：數學、字串
            - 當兩方其中有一方是字串，js 就會設定此 `+` 是字串相加
            ```js=
            111 + 222 + "333" + 111 + 222; // 333333111222
            ```
            - 但 php 會分開來 : `+` = 數學, `.` = 字串
                ```php=
                <?php
                $v1 = 111;
                $v2 = 222;
                $x = $v1 + $v2; // 333
                $x = $v1 . $v2; // "111222"
                ?>
                ```
        - js 中，幾乎所有東西都可以相加
            - ![](https://hackmd.io/_uploads/S1q_hw_1a.png)
            - 但 php 雖然是弱型別，但還是有限制
                - ![](https://hackmd.io/_uploads/ByCq2vdJ6.png)
        - 解法
            - `typescript` : 可讓 code 的型別更嚴謹（雖然不嚴謹還是可以被編譯成 .js），更易讀、維護
                - function 加入回傳 type
                - function 限制傳入參數 type
                - 有 `private`, `public`, `protected`
        - scope
            - 作用域
            1. function scope : variable 只在 function 裡有用
            2. global scope
                - 在 html 是 window object
                    - ![](https://hackmd.io/_uploads/ry0vE_uJp.png)
                        `window.aaa; // 5` 
                    - 可以在 function 內宣告全域 : 不宣告（`var`, `let`, `const`）變數就給值
                        ```js=
                        function test() {
                            aaa = 5;
                        }
                        test();
                        console.log(aaa);
                        ```
                - 在 nodejs 是 global object
            3. block scope
                - 只作用在大括號之中
                - ![](https://hackmd.io/_uploads/BJyiIOu1a.png)

        - var vs let vs const
            - var 缺點
                1. 同 scope 內可以重複宣告
                    - 且不會初始化原本的值
                        - ![](https://hackmd.io/_uploads/B1FN8uuJ6.png)
                    - 可能會不小心改到之前有宣告過的同名的變數
                2. 不支援 constant variable（不能被改的）
                3. 不支援 block scope
                    - ![](https://hackmd.io/_uploads/SJbGDdOk6.png)
                    - 因為 var 是 function scope
                    - ![](https://hackmd.io/_uploads/H1_2ncxzp.png)
                4. 會產生 global variable
                    - ![](https://hackmd.io/_uploads/rJBy3dOJT.png)
            - let 優點
                1. 同個 scope 內不可重複宣告
                2. 支援 block scope
                    - ![](https://hackmd.io/_uploads/BJLbx95MT.png)
                    - ![](https://hackmd.io/_uploads/Skovgq9za.png)
                4. 不會產生 global variable
                    - ![](https://hackmd.io/_uploads/SJMtjbSgT.png)
                    - 原因
                        - ![](https://hackmd.io/_uploads/r1lo3_OJp.png)
                    - 這樣的好處是進一步管控了 Global 變數，只能透過 Global Object 去存取，提升程式的嚴謹性和安全性。
            - const 優點
                - 除了 let 優點再加上 : 定義時必須初始化 (Initialization)，且之後不能再更改
            - ref
                - https://something-about-js-book.onejar99.com/day08
        - hosting
            - js 會將 variable, function 的 declare 拉到 scope 最上面
                - varaiable
                    ```js=
                    console.log(x);
                    var x = 5;
                    // 會變成
                    var x; // declare
                    console.log(x);
                    x = 5; // 這邊是 init
                    ```
                - function
                    ```js=
                    sayHi();

                    function sayHi(){
                        console.log('Hi');
                    }
                    // 會變成
                    // 整個 function 都被拉到最上面
                    function sayHi(){
                        console.log('Hi');
                    }
                    
                    sayHi
                    ```
                - function in variable
                    - ![](https://hackmd.io/_uploads/B19DJKdyp.png)
                    - 因會變成
                        - ![](https://hackmd.io/_uploads/HkfFJY_Ja.png)
                - 只有 `var` 會有 undefined 的效果，`let` 和 `const` 雖然也有 hosting，但不會初始化為 undefiend，所以會顯示 `reference error : is not defined`
        - strict mode
            - 在程式最上面加上 `"use strict"`
            - 會讓一些錯誤的語法變成 error
                - 未宣告變數
                    - ![](https://hackmd.io/_uploads/Skgzftu1p.png)
                - function 相同參數
                    - ![](https://hackmd.io/_uploads/HJ_KGFu16.png)
                - function name 不能和保留名稱重複
        - function 常用寫法
            1. 一般 : 有 hosting
                ```js=
                a("test");
                function a(name) {
                    console.log(name); // test
                }
                ```
            2. anonymous function : 無 hosting
                ```js=
                a("test"); // error : a is not a function
                var a = function (name) {
                    console.log(name);
                }
                ```
            3. arrow function
                - 語法更為簡潔
                    - ![](https://hackmd.io/_uploads/H1_BQZWga.png)
                - 主要和傳統 function 差在 `this`
                    - 傳統 function 的 this 是指向呼叫他的 object
                        - ![](https://hackmd.io/_uploads/rJlJl7-Zg6.png)
                        - ref
                            - https://ithelp.ithome.com.tw/articles/10207992
                    - arrow function 的 this 是指向外圍一層的 function（如果一直往外找沒有的話就會指向 window）
                    - 適合用在 `setTimeout`, `setInterval`
                        - object 寫法
                            - ![](https://hackmd.io/_uploads/B1vmfbWxa.png)
                            - ![](https://hackmd.io/_uploads/SkAEz--gT.png)
                        - function 寫法
                            - ![](https://hackmd.io/_uploads/By8C_JUfa.png)
                            - ![](https://hackmd.io/_uploads/HJchOyUGT.png)
                - 不為此 scope 產生新的 `arguments`
                    - 傳統可用 `arguments`，不用管帶入的參數
                        - ![](https://hackmd.io/_uploads/ryfmttuJa.png)
                    - 改成這樣
                        - ![](https://hackmd.io/_uploads/BkfvYtdJT.png)
        - self-invoked function
            - 自己呼叫自己的 function
            - 用 `()` 包起來，再用 `()` 執行
            ```js=
            (function () {
                console.log('Hello');
            })();
            ```
            - 若此段 code 只須執行一次，可以這樣封裝起來避免影響到 global scope（只會影響 function scope）
                - ![](https://hackmd.io/_uploads/BkY26lIG6.png)
            - 所以很適合當作初始化資料的用途
        - js is pass by value or reference ?
            - pass by value
                - 當變數是 primitive(原生型別時)
                    - string
                    - number
                    - boolean
                    - undefined
            - pass by reference
                - 當變數是 object
                    - object
                    - array
                        ```js=
                        let a = [1];
                        let b = a;
                        b[0] = 2;
                        a; // [2]
                        ```
                - 但是若是完全重新賦值，就不是用 reference，而是會重新建一個 object，所以不會互相影響
                    ```js=
                    let a = [1];
                    let b = a;
                    b = [2];
                    a; // [1]
                    ```
                    
                    ```js=
                    let aa = [1]
                      function test(aa) {
                        aa = [2];
                      }
                      test(aa);
                      console.log(aa); // [1]
                    ```
        - closure
            - 若直接用 global，可能會在其他地方被改到，所以要限制只有在相同 function 可以被改（private 概念）
            - 且因為變數只要有持續被引用，就不會從記憶體砍掉（會存到引用它的 object 的 scope，讓變數繼續存活），所以只要在 function 中引用，就可以繼續用
            - ![](https://hackmd.io/_uploads/ByEE4ftka.png)
            - 模擬 class 的 private variable 用法
                ```js=
                function testClosure() {
                  let count = 1;

                  this.a = function (num) {
                    count += num;
                  }

                  this.getCount = function () {
                    return count;
                  }

                }

                let t = new testClosure();
                t.a(8);
                console.log(t.getCount()); // 9
                console.log(t.count); // undefined

                let t1 = new testClosure();
                t1.a(100);
                console.log(t1.getCount()); // 101
                ```
                - ![](https://hackmd.io/_uploads/rJHVj-Lfp.png)
                - 如果 `this.a`, `this.getCount` 沒有引用 `count`，那 `count` 初始化以後就會被砍掉了（因只作用在 function scope 且也沒有用 `this` 指派給 object）
        - cookie vs localstorage vs sessionstorage
            - cookie
                - 由 server 決定失效時間
                - 容量小
                - 每次 request 都會一起帶過來 server
            - localstorage
                - 永久存留在 browser，不因關掉頁面刪除（除非手動刪
                - 容量大
                - 不會隨 request 帶過來
            - sessionstorage
                - 關掉頁面會刪除
                - 容量大
                - 不會隨 request 帶過來
        - js 遵守 ECMAScript 的規範
            - ex. ES6
        - ref
            - https://ithelp.ithome.com.tw/articles/10209656
    - `instanceof`
        - check constuctor 的 `prototype` 是否在 object 的 prototype chain 上
        - ![](https://hackmd.io/_uploads/BkMWpxyxT.png)


- DOM (document object model)
    - 定義：提供網頁元素的樹狀結構的表示方法和存取的 api
    - DOM tree : browser 接收到 Web Server response 的 html，會才從頭開始解析遇到的 html element 和其 attribute 成不同種類的 `Node` 的 object 而建立 DOM tree
        - ex. `HTMLDivElement`, `HTMLScriptElement`
    - CSSOM tree (CSS Object Model) : 等 DOM tree 建完，瀏覽器會從所有來源（包含外部引用、內嵌、行內以及瀏覽器的預設樣式等等）讀取 CSS 並建立 CSSOM tree，其 node 由依據 selector 將指定的樣式套用在指定的 element 上而建立，而像是 color 等樣式若沒有被定義就會繼承父 element 的
    - Render tree : 結合部分 DOM, CSSOM tree 的 node（但像是 `<script>`, `display:none` 這種不會顯示在畫面上的就不會結合）。
        - 當 render tree 建立完成，browser 依 render tree 中的 node 把 element 做 layout/reflow (排版)，將 node 相對位置改為絕對位置。
        - browser 有了 layout 的絕對位置後再計算 element 的 z-index 建立不同 layer（有前後順序），最後會再依 layout 和 layer 將排版結果利用 GPU paint(顯示)在畫面上
        - visibility: hidden 會出現在 Render Tree，但 display: none 不會，因其不屬於版面中會出現的 object
        - ![](https://hackmd.io/_uploads/H1aKha1eT.png)
        - 只要當有觸發特定 event. ex, 滾動螢幕、調整視窗大小、call DOM API 都會再做 reflow
            - 所以要避免一直觸發否則會很耗效能 : Layout Thrashing
            - ex. 不要一直抓 element style
            - ![](https://hackmd.io/_uploads/B1s3-kxxp.png)
            - ![](https://hackmd.io/_uploads/BJryfkllT.png)
            - ref
                - https://cythilya.github.io/2018/07/19/styles-and-layout/


    - root 為 document : 這份文件的開頭
    - 接下來會從最開始的 `<html>` 開始 parse(解析)，將剩下的 element parse 成 tree node。element 可以有自己的 attribute
        
        ```html=
        <html>
          <head>
            <title>example</title>
          </head>
          <body>
            <h1 class="txt">Hello World</h1>
          </body>
        </html>
        ```
        
        - ![](https://hackmd.io/_uploads/ry79LiK02.png)
    - node 關係
        - 上下層
            - Parent Node ，下層為 Child Node
        - 同層
            - Previous 以及 Next
        - ex. 
            - `<tr>` 之間為同層
            - `<tr>` `<td>` 之間為上下層
    - DOM API
        - `document.getElementById('idName')`
            - 回傳相對應的第一個 element 
        - `document.getElementsBytagName('tag')`
            - DOM 中所有 tag 相符，集合為 HTMLCollection
        - `document.getElementsByClassName('className')`
            - 找尋 DOM 中符合此 class 名稱的所有元素，集合為 HTMLCollection
        - `document.querySelector('selector')`
            - 利用 selector 來找尋 DOM 中的元素，並回傳相對應的第一個 element 。
            - 不侷限 id, class. 都可以用，還可以用其他 atrribute 選擇，使用較彈性
            - ![](https://hackmd.io/_uploads/rJnGhX8zp.png)
        - `document.querySelectorAll('selector')`
            - 利用 selector 來找尋 DOM 中的所有元素，集合為 NodeList 。
    - HTMLCollection vs NodeList
        - HTMLCollection : 只回傳 element node
            - ![](https://hackmd.io/_uploads/HJFr72tAn.png)
        
        - Nodelist : 還會回傳 attribute, text, 註解等 node
            - ![](https://hackmd.io/_uploads/H1VO73tR3.png)
    - 阻塞
        - `<script>` 
            - 嵌入和引入式都會阻塞 main thread 而是會直接去下載並執行後才繼續 DOM 的 parse
                - 但是瀏覽器會分段 render，所以可能會先印出前面的 element
            - 所以通常會放在 `<body>` 之後
        - `<link>` 
            - 會阻塞 render
            - 否則頁面會從一開始沒有 css 樣式跳成有 css 樣式（ex. 從黑色字跳成黃色），所以需一次載入，不能邊讀邊 render
            - 所以通常會放在 `<body>` 之前
        - `<img>` : 背景載入，不會阻塞
        - `DOMContentLoaded` : DCL
            - 代表 parse 完 html 的 event
        - `window.onload`
            - 已將所有載入事件完成（element 都被 parse 完）後
            - 避免 DOM tree 還沒建好就改可能會有點問題
        - `src`
            - 引入資源
        - `href`
            - 一般 `<a>` 會用 `href` 的方式做超連結
            - 而會用 `<link href = "xxx.css" rel="stylesheet"/>` 將 css 引入
                - `rel="stylesheet"` 可以讓 browser 知道這是 css
            - 我的理解是 `href` 屬性在 `<a>` 和 `<link>` 是不同東西
                - 在 `<a>` 是做超連結
                - 在 `<link>` 是做引入資源
   
       - json
           - ![](https://hackmd.io/_uploads/B1-xQ9qza.png)
           - ![](https://hackmd.io/_uploads/r17bm59Mp.png)
       - throttle(節流)
           - 某個事件觸發後，接下來的 x 秒內不再處理該事件觸發
           - ex. 限制 scroll event 不要太頻繁觸發
       - debounce(防抖)
           - 從最後一次觸發開始，x 秒後才會處理事件
           - ex. 搜尋輸入框的推薦關鍵字功能，節約查詢推薦關鍵字動作的觸發次數
- MQTT
    - 適合網路狀況差、需低耗電的設備
    - 封包小：header 最小只有 2 byte
    - keep connection，不需重新建
    - 支援多種語言的實作
    - 可以有帳號密碼機制
        - ![](https://hackmd.io/_uploads/rJm2gSCZa.png)
    - 但 mqtt 是預設明文傳輸，所以可以搭配 ssl 加密
        - ref
            - https://officeguide.cc/mqtt-broker-server-mosquitto-installation-tutorial-examples/
    - 有 3 種 qos
        0. 可能會沒收到
        1. 一定會收到, 但可能重複
        2. 保證收到且不重複
    - why use kafka
        1. 因為 mqtt 沒有暫存機制，如果中間斷線資料會收不到
        2. 在壓力測試中，consumer 出現漏接封包的狀況

- python
    - logging
        - 有 5 level，可以設只要看到哪個 level 以上，通常開發用 debug，實際在跑就用 warning, error
            - debug
            - info
            - warning
            - error
            - critical
        - 有一些方便的東西，ex. 印出錯誤行數、檔名 

## NodeJS
- sso
    - ![](https://hackmd.io/_uploads/r1QSytoMT.png)
- middleware
    - 在 request 到 response 中，借助許多 middleware 溝通，用 next 引發下一個 middleware
    - ex. express routing
    - ![](https://hackmd.io/_uploads/ryjTpMrkp.png)

- request
    - 預設只能拿到 get 的 query string，所以要拿到 post 的 request.body，就要用 `body-parser` 來 parse request 的 body
        - `bodyParser.urlencoded()`
            處理 UTF-8 編碼的資料，常見的表單（form）提交
            例如：application/x-www-form-urlencoded
        - `bodyParser.json()`
            處理 JSON 格式的資料
            例如：application/json
        - `bodyParser.text()`
            處理 type 為 text 的資料
            例如：text/html, text/css
        - `bodyParser.raw()`
            處理 type 為 application 的資料
            例如：application/pdf, application/zip
        - ex. 
            ```js=
            app.use(bodyParser.urlencoded({ extended: false }))
            app.use(bodyParser.json())
            ```
- MVC
    - ![](https://hackmd.io/_uploads/SkXQKNFk6.png)
    - controller 就是 express router
    - controller 可以再細分成 routing 和 邏輯處理的 middleware
        - 所以 `index.js` 引入 `api` 中 routing 檔案只負責路由回傳由 middleware 處理完的資料
            - `index.js`
                - 引入 routing 的檔案
            - routing
                - ![](https://hackmd.io/_uploads/HkAL6MZe6.png)
            - middleware
                - ![](https://hackmd.io/_uploads/HJsQaGZx6.png)

- cors (cross origin resource sharing)
    - express 預設不能跨域
    - ![](https://hackmd.io/_uploads/H1rYl6YJa.png)
    - 和 csp 是不同的
        - cors 是網站 A 不允許網站 B 存取網站 A 的資料
        - csp 是不允許客戶載入不合規定的網站的資源
- pm2
    - 可以用 multithread 分散流量
        - ![](https://hackmd.io/_uploads/r1bFuX-ep.png)
        - ref
            - https://ithelp.ithome.com.tw/articles/10253083


## nginx
- better than apache
    - 大量連線時存取效能較好
    - 佔較少 ram
- 一般 proxy
    - 就是把服務開在 80 port
- reverse proxy
    - 藉由不同 domain name 導向不同的服務
    - 優點
        - load balance
        - 統一在 nginx 設定、過濾 request（就不會影響到真正的 server）
            - 設定 policy 過濾（白名單、黑名單）
                - header, body
                    - 含有 sql 字句
                - ip, 網段
                    - 黑名單：`deny 123.123.123.0/28;`
                        - 常用於外部服務
                    - 白名單：`allow 123.123.123.0/28;`
                        - 常用於內部服務
                        - ex. 學校 vm
                - 防止 DDOS
                    - 同個 ip 最大連線數
                        - `limit_conn conn_limit_per_ip 100;`
                    - 同個 ip 每秒最多 request number
                        - `limit_req_zone $binary_remote_addr zone=req_limit_per_ip:10m rate=50r/s`

            - 最大上傳
            - ssl
        - cache
- conf 設定
    - `proxy_hide_header X-Powered-By;`
        - 關掉 `X-Powered-By` : 會顯示 server 用甚麼框架
    - `proxy_set_header` 
        - nginx reverse proxy 就是轉發 request 封包，所以要對轉發的封包的 header 設定
        - `proxy_set_header Host $host;`
            - 設定 header 的 host 是原本的 request 的 目的地 的 host
            - ![](https://hackmd.io/_uploads/BJ44vOqka.png)
                - 若沒加，轉送的封包目的地就會變成 localhost，而不是 klab.tw，然後因為 wordpress site 有設定 domain name，它就會要求 broweser redirect 到 klab.tw，成為 redirect loop
        - `X-Forwarded-Proto`
            - 用來代表此封包是 https or http，因為內部會轉成 http
        - `proxy_set_header X-Real-IP $remote_addr;`
            - 讓後端 server 可以取得 <b>上一個封包</b>的 source ip
            - ex. wordpress 的 wordfence 就可以去判斷 ip 黑名單等
        - `proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;`
            - 可以取得<b>最初的</b>封包的 source ip + 上一個的
            - 因為可能中間有經過其他的 proxy，所以此可以看最初的用戶端的 ip
        - ref
            - https://blog.csdn.net/qq_34556414/article/details/106634895
            - https://klab.tw/2022/05/nginx-proxy-pass-wordpress/
- upstream
    - ![](https://hackmd.io/_uploads/Bycio_qyp.png)
    - round-robin : 輪巡
    - weight round-robin : 有權重的輪巡
        - ![](https://hackmd.io/_uploads/ryrb6OcyT.png)

## NoSQL
- 相較 RDBMS 
    - 特點
        - 通常不會正規化，加快存取速度
    - 優勢
        - 當資料太大時會須擴充 DB，可水平擴充，降低擴充成本
        - 不須固定 schema，更改欄位較方便
            - 因每筆 data 是獨立的 document 儲存
    - 缺點
        - 不適合存重要的資料
            - 只保證 eventually consistency (最終一致性)，cluster 中不同 DB 在相同時間存取的資料可能會不同（因還在同步）
            - 成熟度不像 RDBMS 一樣
            - 所以重要資料或少量資料較適合用 RDMS 存，因可確保交易的 ACID，也較好看出 entity 間的關係，更易維護
- MongoDB
    - 可以自由定義資料結構 (BSON)
        - 不用像傳統的 RDBMS (Relational Database Management System) 要先規劃 schema
        - BSON : 可以放 binary value 的 JSON
    - 結構
        - ![](https://hackmd.io/_uploads/BkAlqPqyT.png)
    - shard
        - 水平擴充
            - RDBMS 只能垂直擴充、replication（ex. master-slave）
                - 但 slave 會完全複製 master 資料，所以在效能上還是會比較慢，雖然可以分 request 到不同 replication
            - 可切割不同資料放在不同 mongodb server
- Redis
    - 用 ram 儲存

## 前端做 or 後端做
- hash password
    - 以 https 傳 + backend hash
    - 因若在前端 hash，我只要知道後端的 hash value 就可以直接送此 hash value 給 server 做比對（可改前端的 js
- pagination
    - 後端做
        - 減少 request 的 data 量
        - 需頻繁送 request
    - 前端做
        - 減少 request 數量
        - 之後每次換頁比較快
        - 一開始會載入比較久
        - 通常不會把全部的頁面都看完，全部載入會有點浪費
    - 都做
        - 一開始將最常用的頁面（可能是前面幾頁 or 後面幾頁）載入
        - 之後若有沒有的頁面去後端重新撈沒有的頁面的前後幾頁 
- 驗證
    - 都做
        - 前端做可以增加 user 體驗、減少 server 負擔
        - 後端做防止改前端 js 或是直接送

## 備份
- 能還原才是好備份
- 要有異地備份
- 地方 type
    - on-site backup( 本地備份 )
    - off-site backup (異地備份 )
- 備份 type
    - full backup
        - 完整備份，不管是否只修改一部分，每次就是全部重新備份
    - incremental backup 增量備份
        - 一開始會完全備份，之後每次只備份和 <b>第一次完全備份檔</b> 不同的地方
        - 還原速度較快
    - Differential backup 差異備份
        - 一開始會完全備份，之後每次都只備份和上一次備份檔不同的地方
        - 還原速度較慢
- GFS 原則
    - ![](https://hackmd.io/_uploads/Byp9OXY1p.png)
- 備份 db
    - mysqldump
        - 手動備份
        - mysqldump 會 lock database
            1. 可以調成不要 lock（用 readview 的概念）
                - `mysqldump --single-transaction --skip-lock-tables --databases my_db1 my_db2 > my_database.sql`
                    - `--single-transaction` : 只會從開始 dump 的時候的資料開始拿
                        - 會幫剛開始的資料做 snapshot (readview 的概念)
                        - https://medium.com/@martin87713/mysql-lock-55ca187e4af2
                    - `--skip-lock-tables` : 不要 lock table
            2. 用 galera 去備份其中一個 db
            3. 選在沒人用時備份
    - automysqlbackup
        - 可設定每天固定備份
- rsync
    - 可做 incremental backup，只傳有差異的地方
## transaction
- 當有連續幾筆有相關的 sql，如果只有其中幾個有成功執行就有點尷尬了
    - ex. 有繳費紀錄、欠債紀錄這兩個 table。有人來繳費並還清債務，但繳費紀錄 table 沒有成功紀錄資料，但欠債紀錄 table 有成功還債。
- 指令
    - `beginTransaction`
        - 開始此次交易
        - lock table
    - `commit`
        - 當此次交易所有 sql 都正確執行，提交此次交易執行的 sql 內容到 db
        - unlock table
    - `rollback`
        - 取消此次交易所有 sql 執行的內容
        - unlock table
- 特性 : ACID
    - Atomicity 原子性
        - 不可切割，要馬都完成、都不完成
    - Consistency 一致性
        - 交易後，db 仍是正常
    - Isolation 隔離性
        - 交易之間要隔離，不能有 A 交易時 db 的內容是 B 交易到一半的內容
        - 等級
            1. Read Uncommitted
                - 同個交易中，query 結果會因其他交易的改動（其他交易不須 commit）而改變
            3. Read Committed
                - 同個交易中，query 結果會因其他交易的 commit 而改變
            4. Repeatable Read (RR)
                - mysql InnoDB 的 default
                - 同個交易中，相同的 query 結果會一樣（即使其他 transaction commit），因交易開始時會做 snapshot
            5. Serializable 
                - 交易 sql 開始執行時，會 lock 住 table，其他交易不能讀寫，直到此交易 commit, rollback 才 unlock
                - 效率極差，同時只能有一交易，不適合搶票系統等
        - lock
            - share mode : 一個 transaction 鎖了 share mode 後，其他 transaction 在其 commit 前只能 select 不能改。可以解決非序列化的交易錯誤問題，但較有可能 deadlock。
                - `lock in share mode`
                - ex.
                    ```sql=
                    start transaction; 
                    select * from test where id = 1 lock in share mode;
                    ```
                - 避免非序列化問題
                    - ![](https://hackmd.io/_uploads/B1kHatLza.jpg)
                    - 實做 : https://medium.com/dean-lin/%E7%9C%9F%E6%AD%A3%E7%90%86%E8%A7%A3%E8%B3%87%E6%96%99%E5%BA%AB%E7%9A%84%E6%82%B2%E8%A7%80%E9%8E%96-vs-%E6%A8%82%E8%A7%80%E9%8E%96-2cabb858726d
            - exclusive mode : 一個 transaction 鎖了 exclusive mode 後，其他 transaction 在其 commit 前啥都不能做（堵塞），可避免同時執行有 query 結果和實際上不同的情形（因預設 RR 會讓相同 transaction 的 query 保持一樣）。
                - `for update`
                - ex. 
                    ```sql=
                    start transaction;
                    select * from test where id = 1 for update;
                    ```
                - lock 可解決同時查詢的問題
                    - ![](https://hackmd.io/_uploads/S13kokkgT.png)
                    - 若沒有 lock，且票數只有一張，兩個交易就會都認為當下是還有剩的，然後都可以買了
            - deadlock 問題
                - 若 `for update` 指向空的資料，會是用 gap lock 把查詢條件的範圍 lock 起來（防止寫入，還是可以讀）
                    - 這樣其實就變成 share lock（可讀不能寫）
                - ![](https://hackmd.io/_uploads/ryi70yJxa.png)
                    - 沒有 `show_id` = 5 的 data
                    - 第三步交易 3 gap lock `show_id` 4 ~ 6
                    - 第四步交易 4 gap lock `show_id` 4 ~ 6
                    - 第五步交易 3 被 交易 4 阻塞
                    - 第六步交易 4 被 交易 3 阻塞，deadlock 被踢掉而 rollback
                - sol
                    1. Serializable : 但太慢
                    2. 不要讓 where 找到空的值
                    3. optimistic lock : 交易更新時判斷更新期間 value 是否有被別的交易更動，有就更新失敗
            - 總結
                - share lock 比較容易 deadlock
                - exclusive lock 會降低吞吐量
                - 但他們都可以解決非序列化的問題
- Durability 永久性
    - 一旦 commit，永久存在 db
- ref
    - https://notes.andywu.tw/2021/select-for-update%E5%86%8Dinsert%E9%80%A0%E6%88%90deadlock%E7%9A%84%E9%99%B7%E9%98%B1/
    - https://mgleon08.github.io/blog/2017/11/01/optimistic-locking-and-pessimistic-locking/

- ex. nodejs
    ```js=
    let conn = await pool.getConnection();
    // Start Transaction
    await conn.beginTransaction();
    try {
        // update record's paid to 1
        const rId = req.body.reason.split(',')[1];
        await conn.batch('update records set paid = 1 where `no` = ?', rId);
        // update turned = 1 
        var update_debt = await conn.batch('update debt set `turned` = 1 where `no` = ?;', req.body.no);
        // insert into financial
        var fId = await conn.batch('insert into financial(`aId`, `reason`, `money`) values(?, ?, ?);', [req.body.aId, req.body.reason, req.body.money]); // 新增到總帳務
        // insert into financial_today
        await conn.batch('insert into financial_today(`aId`, `reason`, `money`, `fId`) values(?, ?, ?, ?);', [req.body.aId, req.body.reason, req.body.money, fId.insertId]); // 新增到今日帳務
        // commit
        await conn.commit();
    }
    catch(e) {
        console.log(e);
        // 還原
        await conn.rollback();
    }
    conn.end();
    ```

## 加快網頁載入速度
- 前端
    - 盡量不要發太多次 request，因為建連線很花時間，可以一次拿多點資料
    - 若要 call 多支無上下關係的 api，可以用 promise.all，而不是 await 一個完再 call 下個
    - `<script>` 用 `defer` or `async`
        - 若 html parse 到 `<script>`，就會暫停 parse 和 redner 並去載入後再執行裡面的內容（所以傳統會把 `<script>` 寫在最下面，避免 html node 還沒 parse 出來，但在 code 中可能會需要 call DOM API 使用的狀況）
        - defer
            - 可以在背景載入（不是用 main thread
            - 載入完成會等全部 html parse 完後才會執行裡面的 code（可確保 html node 都已經建立）
        - async
            - 可以在背景載入（不是用 main thread
            - 載入完成後暫停 html parse 馬上去執行裡面的 code
        - 總結
            - ![](https://hackmd.io/_uploads/SkdiTjkea.png)
            - 可以把 `<script defer>` 在 html 中初始引入，加快載入速度
        - ref
            - https://ithelp.ithome.com.tw/articles/10216858
    - 利用診斷工具（ex. lighthouse），檢查網頁的載入是被哪邊 block 
        - lighthouse 還會給建議，也有 .js 的 code usage
            - https://pagespeed.web.dev/
            - ex.
                - ![](https://hackmd.io/_uploads/H1Dh-91Mp.png)
    - 若要使用 js library 用 cdn 版本
        - client 通過 dns server 會先去找該 cdn 的 load balancer，它會回傳比較適合的 server，再去找他拿真正資源
        - jquery
            - `https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js`
            - 而不是
                - `https://code.jquery.com/jquery-3.6.0.js`
    - 資源做 lazy load
        - ex. image lazy load
            - 只有用到的圖片才去動態載入，而不是一開始就全部載入
            - ex. https://imgur.com/
    - Code Minimize
        - 把變數或函式名稱改成很簡短，且去掉空白
        - 減少傳輸及瀏覽器解析時間
- 後端
    - 若 table 的 data 每天都會新增很多，可以區分最新的資料的 table 和以前的資料的 table，加快查詢速度
    - 部分小量的數值可以用 redis
        - ex. 當日總支出
    - 可以用 sql 完成的事情就不要拉出來再做
        - ex. 
            - 不用拉出來再算平均
                - `SELECT avg(price) FROM Products;`
            - procedure
                - 可以寫一些判斷邏輯
                - 會被編譯，效能較好
                - 但缺點是不易閱讀及修改
    - sql 盡量不要 `select *`，挑要用的欄位就好
    - db table 常用的查詢欄位使用 index
        - index 會幫此欄位建一張 index table，可以藉由此 index table 對應到原本 table 的 record
        - 優 : 速度快
            - 且 InnoDB 的 index 使用 b+ tree 儲存，所以只要用 index column 當查詢條件，時間複雜度是 O(logn)，否則就會變 full table scan 的 O(n)。而且 index（non leaf node）是存在 mem 裡面，data 才是存在 disk
            - 此狀況若要用 ssn 去 query 需要 full table scan
                - ![](https://hackmd.io/_uploads/rkXqP3GGT.png)
                - ![](https://hackmd.io/_uploads/H1LOd2fz6.png)
            - 若發現 ssn 很常被用來 query，可以幫它建 index
                - ![](https://hackmd.io/_uploads/rknzunfzT.png)
            - 實際就會變這樣查詢
                - ![](https://hackmd.io/_uploads/rJ2V_hMG6.png)
        - 缺 : 佔空間
            - 需額外存 index table，每次新增資料 index table 也都需要新增
        - note
            - 如果 `where` 用了 `!=`, `<>`, `or` 或找的資料是不同 type（會被做型別轉換）或用 `sum` 等 function 就會變回 full table scan
            - mysql 會直接把 pk 建 index
            - 選擇 index 是要看是否常用此 column 查詢
        - ref
            - https://www.jyt0532.com/2021/01/30/index/
            - https://wenwender.wordpress.com/2022/08/26/%E8%B3%87%E6%96%99%E5%BA%AB%E7%9A%84%E7%B4%A2%E5%BC%95index%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86/
        - 建立方法
            - ![](https://hackmd.io/_uploads/rkGxb3MG6.png)
            - `unique` 是只有當 index 的 column 是唯一辨識才能用
                - ![](https://hackmd.io/_uploads/r17bchzMT.png)
                - ![](https://hackmd.io/_uploads/HyTZ5hMGa.png)
        - ex.
            - student 的 pk 是學號，但因為很常需用 email 去查詢特定學生，所以可以把 email 的 column 設為 unique index
    - 需大量存取的資料可以用 redis
## webpack
- 可以 uglify 與 minify
- 可以打包後端模組或套件（ex. npm）讓前端 js 用
    - ![](https://hackmd.io/_uploads/rySKxiLfa.png)
    - 雖然在 ES6 後，可以在瀏覽器上用 import 的方式使用後端套件
        - ![](https://hackmd.io/_uploads/SJJVWj8zp.png)
            - 需 type = module
        - 缺點是若是要用 npm 套件，路徑就要寫成 `'./node_modules/pad-left/index.js'`，不易維護
- 也可以打包 圖片, css 讓前端 js 用
    - ![](https://hackmd.io/_uploads/HyRfliUMp.png)
- note
    - 為什麼很多專案（例如說 React）在部署前都要先 build？這個步驟在幹嘛你知道嗎？
        - 因為原始碼沒辦法直接放上去瀏覽器（會沒有辦法執行），所以一定要經過 webpack 打包處理，打包完的檔案才能讓瀏覽器執行。
    - 你知道 require/module.exports 與 import/export 的差別嗎？
        - require/module.exports 是一套叫做 CommonJS 的規範，Node.js 有支援，瀏覽器沒有。
            - 可以用 `require` 引入內建 module
            - 用 `module.exports` 製作 module

        - import/export 是 ES6 的規範，Node.js 部分支援(副檔名需 = `.mjs`)，瀏覽器也是部分支援(type=module)。
- ref
    - https://blog.huli.tw/2020/01/21/webpack-newbie-tutorial/


## react
- virtual DOM
    - 透過比對 virtual DOM，避免直接改動到 DOM，會需要全部 node 重新 render，performance 較差，而是針對部分改動的 node 修改
- 優
    - 模組化：react 中很容易就可以把各功能模組化
        - 減少 code reuse
        - 提升可讀性
    - 效能提升
        - 利用 virtual dom 優化效能
        - 傳統會用 browser api 或透過 jquery 對整個 DOM 重新 render，virtual dom 是 React 自身的 diff 演算法去計算出最小更新，進而去最小化更新真實的 DOM。
    - 可以用 state 處理動態 value 改變比較方便，不用自己寫 innerHTML 那些
        - ![](https://hackmd.io/_uploads/rJII54HMa.png)
        - 所以 react 被稱為 State Machines：component 依據 state 改變重新 render component
- state and props
    - state 
        - 把資料（通常是 user input, request api data, 會變化的）放自己 component 中
            - ![](https://hackmd.io/_uploads/H140NBjG6.png)
                - 可以在 constructor 中用 `this.state` 宣告 state 物件
            - 如果是不會變化的通常就不會放在 state，因若 state 改變還須再定義一次
        - 當 state 內的資料更新, 會重新 render 該 component 內容（重新執行每個 component 都有的 `render` function），針對 component 做重新渲染
            - 要用指定的 `setState` function 去改變 state, react 才會執行 render, 否則不會重新 render
            - ![](https://hackmd.io/_uploads/H1udIBjMa.png)
            - ![](https://hackmd.io/_uploads/Hy7c8BjMa.png)
            - ![](https://hackmd.io/_uploads/HJCvIrjz6.png)
    - props
        - 繼承父 component 的 atrribure or method
            - 父傳給子
                - `<Test2_Child father="Test2" name={this.insert_text} />`
            - 子接收
                ```js=
                class Test2_Child extends React.Component {
                    constructor(props) {
                        super(props);
                        this.state = {
                        };
                    }

                    UpdateData = () => { 
                            //console.log(this.props.name)
                        this.props.name("child")
                    }

                    render() {
                        console.log(this.props.father)
                        return (
                            <div>
                                <button onClick={this.UpdateData}>test2 child {this.props.father}</button>
                            </div>
                        );
                    }
                }
                ```
            - result
                - ![](https://hackmd.io/_uploads/rJRqmVSzp.png)
        - 子 component 是 read-only, 若要更改就需用父的 function 去更改
- component 的 2 種寫法
    - ![](https://hackmd.io/_uploads/Byr6EWSGa.png)
- `public/index.html`
    - 唯一一個 html file，裡面有一個 `<div id = "root"></div>` 的 element
    - 其他的 element 都是用 js 去動態生成於 root element 之下
    - ![](https://hackmd.io/_uploads/Bk-rPgrf6.png)
- JSX
    - 可以直接嵌入 DOM element, 也可以用大括號代入變數的語法
    - ![](https://hackmd.io/_uploads/HyIBJrHGT.png)
- 用 react 的困難
    - 觀念和以前一個網頁就是三支 html, js, css 的 template 的方式不太一樣
    - 需要把不同小功能切開來（才不會每次都需 render 沒有更動到的）然後互相串接，還會需要繼承各個 component 的 value
- life cycle
    1. Mount: 元件被渲染到畫面上
        - component 從 constuctor 的初始 到 render 執行完
    3. Update: 元件的內容因為資料的更動而重新渲染
        - component 因為 state 的變動而重新 render
    5. Unmount: 元件從畫面上消失，移除這個元件
        - component 被移除
- hook
    - 也可以用來宣告 state，且語法更嚴謹
- ref
    - https://sweeteason.pixnet.net/blog/post/42910964-react-%E5%88%9D%E5%AD%B8%E8%80%85%E7%AD%86%E8%A8%98%E8%88%87%E6%95%99%E5%AD%B8-%28%E4%B8%89%29---state-%E7%9A%84%E4%BB%8B%E7%B4%B9%E8%88%87%E4%BD%BF
    - https://medium.com/%E6%8A%80%E8%A1%93%E7%AD%86%E8%A8%98/react-%E5%AD%B8%E7%BF%92%E7%AD%86%E8%A8%98-0-%E5%89%8D%E8%A8%80%E8%88%87%E6%96%87%E7%AB%A0%E7%B5%B1%E6%95%B4-44603bc6bdc5
## oop vs fp
- object oriented programming
    - 將不同 component 抽象化，封裝在不同類別，降低 component 間的耦合，且不需要知道元件的實作細節（所以可用 private）
    - 特色
        - 抽象化
        - 封裝
            - 封裝成 public, protected, private
            - 盡量設 private，降低耦合
        - 繼承
            - code reuse
            - 問題
                - name confilction
                - 重複繼承
        - 多型
            - 依不同子 class 做不同行為
    - 優點
        - 低耦合
            - 若其中一個 component 改動一些實作細節也不須改動到別的 component
            - 易測試單個 component 功能
        - 高內聚
            - 易讀、維護，因元件所需用到的屬性、function 會在相同元件
    - SOLID
        - S = Single-responsibility principle (SRP) = 單一職責原則
            - 定義：一個模組應只對唯一的一個角色負責（就一個類別而言，應該只有一個引起它變化的原因）
            - 例子
                - 錯誤用法 1：一個模組的改動會改動多個角色的功能
                    - ![](https://hackmd.io/_uploads/Hkgol0xbT.png)
                - 錯誤用法 2：把不同功能寫在同個 class，不易更新新功能及維護，且會有大量重複 code
                    - ![](https://hackmd.io/_uploads/rkAuKGbW6.png)


                - sol
                    1. 直接依不同角色拆成不同 class 封裝好
                        - ![](https://hackmd.io/_uploads/S1sRbCgWa.png)
                    2. 拆成不同 class 封裝好並實作 interface，抽象化程度較高，比較好
                        - ![](https://hackmd.io/_uploads/Hk9lMRgbp.png)
                    - 第二個例子的 sol：提高內聚、降低耦合
                        - ![](https://hackmd.io/_uploads/S1dunfWb6.png)


        - O = Open–closed principle (OCP) = 開放封閉原則
            - 定義：面對擴展時是開放的，且擴充新功能時不應修改到原有的 code。
            - 原因
                - 未區分核心邏輯與附加的商業邏輯
                    - ![](https://hackmd.io/_uploads/S1qMSzb-T.png)
                        - https://wadehuanglearning.blogspot.com/2019/12/blog-post_11.html
                - sol
                    - 開放擴充點（像是透過繼承）
                        - ![](https://hackmd.io/_uploads/HyTqHMW-T.png)
                    - 用介面減少針對單一物件的依賴，就可以適應傳進來的不同物件，而不用改動核心邏輯（做好 DIP）
                        - ![](https://hackmd.io/_uploads/r11rIfZ-a.png)
                            - https://igouist.github.io/post/2020/10/oo-11-open-closed-principle/
                    - 區分好每個模組的角色和任務，就不會需要互相更改（做好 SRP）
                        - 像是一開始就先設定好擴充點
            - 例子
                - chrome 不會因為新增 plugin 而導致修改到原有的程式
        - L = Liskov substitution principle (LSP) = 里氏替換原則
            - 定義：子類別要能夠替代父類別，且替換後可以正常做父類別的內容（輸出要相同）
                - 就是在處理繼承問題
            - 原因
                - 子類別無法做出和父類別相同的內容
                    - ![](https://hackmd.io/_uploads/ByTB_1W-6.png)

                - 不要只因為 code reuse 就繼承，能不要繼承就不要，而是使用介面
                    - 因為介面可以要求子 class 要完成 interface 中的 method
                - ref
                    - https://igouist.github.io/post/2020/11/oo-12-liskov-substitution-principle/
            - 例子
                1. 簡易
                    - ![](https://hackmd.io/_uploads/r1PEKkbWa.png)
                    - https://wadehuanglearning.blogspot.com/2017/08/php-oo-lsp.html
                2. 正方形問題
                    - ![](https://hackmd.io/_uploads/rkCvvHvzp.png)
                    - ![](https://hackmd.io/_uploads/B1quwrPfp.png)
                    - ![](https://hackmd.io/_uploads/B16V_SDMa.png)
                        - 驗證長方形面積是否正確的 function
                    - ![](https://hackmd.io/_uploads/Hk_w_Bvfp.png)
                        - 驗證結果不合預期，因為正方形沒辦法回傳和長方形一樣的結果
        - I = Interface segregation principle (ISP) = 介面隔離原則
            - 定義：不應該強迫用戶依賴它們未使用的方法
            - 原因
                - 如果介面設定太多太廣的方法，造成實作的 class 無法全部都實作（或只能實作空方法）
                - sol
                    - 最小化介面的職責（單一介面不要做太多事），如果有很多不同職責的功能就拆成很多介面
                    - 一個合理的介面設計是能夠符合單一職責原則的，反過來說，我們可以用單一職責原則來檢視我們的介面設計是否良好。
            - 例子
                - 交通工具的介面要求能開關車門，結果電動機車無法實作。
            - ref
                - https://igouist.github.io/post/2020/11/oo-13-interface-segregation-principle/
        - D = Dependency inversion principle (DIP) = 依賴反向原則
            - 定義：high level module 不應該直接依賴於 low level module，兩者要依賴於 interface
            - 原因
                - 因為 high level module 會用到很多種 low level module，所以不希望有 low level module 更新連帶 high level module 也要改
            - 例子
                - ![](https://hackmd.io/_uploads/BkxIAyW-T.png)
                - sol
                    - ![](https://hackmd.io/_uploads/rJiER1Zba.png)
                    - 可以用建構子的方式把 interface 的 object 帶入 hight level module
            - ref
                - https://igouist.github.io/post/2020/12/oo-14-dependency-inversion-principle/
        - ref
            - https://igouist.github.io/series/%E8%8F%9C%E9%9B%9E%E8%88%87%E7%89%A9%E4%BB%B6%E5%B0%8E%E5%90%91/
                - 屌
            - https://medium.com/%E7%A8%8B%E5%BC%8F%E6%84%9B%E5%A5%BD%E8%80%85/%E4%BD%BF%E4%BA%BA%E7%98%8B%E7%8B%82%E7%9A%84-solid-%E5%8E%9F%E5%89%87-%E7%9B%AE%E9%8C%84-b33fdfc983ca

    - 高內聚、低耦合
        - 良好的內聚應該只關注在一件事情上，並適時地將不屬於自身職責的工作交給別人，達到所謂「該內聚而內聚，該耦合而耦合」。
    - function 寫法
        - ![](https://hackmd.io/_uploads/r1bWjGZZ6.png)
            - 不好的寫法
                - ![](https://hackmd.io/_uploads/rJFDiMWb6.png)
                - ![](https://hackmd.io/_uploads/SJUrozW-6.png)
            - 好的寫法，封裝邏輯
                - ![](https://hackmd.io/_uploads/Sy43jf-WT.png)
            - https://ithelp.ithome.com.tw/articles/10206839


- functional programming
    - 盡量將邏輯都包成 pure function，主要邏輯用 declarative 方式寫，且 1 function 盡量做一件事情就好
        - imperative : 命令式，一行一行做指令
            - ex. 用 `for` 一行一行寫邏輯
        - declarative : 宣告式，將步驟封裝好，只需宣告想要的結果
            - ex. 用 `map` 宣告
    - 優
        - 增加重用性
        - 增加可讀性
        - debug 容易，易找哪邊改到 state
    - pure function
        - 會回傳 value、不會更改到 function 外部的變數 (no side effect)，所以相同參數有相同輸出
        - ex.
            - `array.slice(start_index, end_index-1)`
                - 為 pure funtion : 不會更改 array 值
                - 回傳切割的範圍
                - ![](https://hackmd.io/_uploads/B1wwS8lla.png)
                - `array.splice(start_index, end_index-1)`
                    - 不是 pure function : 直接更改 array 值
                    - 回傳切割的範圍，原 array 剩切割完的
                    - ![](https://hackmd.io/_uploads/rkV_SLexp.png)
            - array 可以這樣帶入參數，或用 deepcopy
                - ![](https://hackmd.io/_uploads/B1_uqvxlp.png)
        - 目的
            - 並不是要讓全部 function 都變 pure function，而是要降低 function 間的耦合性。才不會專案一大，容易搞不清楚哪一行導致 state 改變
            - 解決賦值問題
                - ![](https://hackmd.io/_uploads/H1k0Xvega.png)
        - 原則
            1. Task // function只做一件事，所以會有大量的 function，但可以增加可讀性
            2. return Statement // 會回傳結果狀態
            3. Pure //純函式
            4. No shared state //兩個函式不共用同個狀態
            5. Immutable State //不可變的
            6. Composable //可被組合
            7. Predictable // 可預期的


- 兩者不衝突，可同時使用
    - 兩者都是將實作概念抽象化，但 oop 在不同種類的物件間可以比較快速的看出互相的關係（因抽象化的範圍較廣），而 fp 可讓物件中實作的細節被再被更封裝起來。所以如果沒有 oop 而只用 fp 會讓不同種類物件之間的關係很複雜。

## concurreny programming
- 定義：process 可以一次執行多件事情(multi threading)，最大化 cpu usage
- Concurrent Programming 為什麼比 Object Oriented Programming 好
    -  Concurrent object-oriented programming is a programming paradigm which combines object-oriented programming (OOP) together with concurrency. `- wiki`
    -  應該可說 concurrent programming 可以在 oop 的基礎上再加上 concurrency，讓 process 一次執行多件事情來最大化 cpu usage
- Multi-processing (多處理程序/多進程)：
    - parallelism
    - 可以利用多顆 cpu 的效能
        - multiple threads at the same time run across multiple cores
    - 資料在彼此間傳遞變得更加複雜及花時間，因為一個 process 在作業系統的管理下是無法去存取別的 process 的 memory
    - 適合需要 CPU 密集，像是迴圈計算
        - cpu loading 會持續很高的狀況
    - ![](https://hackmd.io/_uploads/H1_aFFCZa.png)
- Multi-threading (多執行緒/多線程)：
    - concurreny
    - 可以最大化使用單顆 cpu 週期
        - 當 thread 在 wait io 時可以執行別的 thread
    - 資料彼此傳遞簡單，因為多執行緒的 memory 之間是共用的（因同個 process），但也因此要避免會有 Race Condition 問題
        - multiple threads at the same time are generated by a single process
    - 適合需要 I/O 密集，像是處理 user request 再寫入 db 需大量讀寫 memory, disk、爬蟲需要時間等待 request 回覆
        - 等待時可以做別的 thread
        - ex. 聊天室：收發訊息用不同 thread
    - ![](https://hackmd.io/_uploads/r1katK0WT.png)
- 總結
    - multi-processing
        - 可以在相同的時間內完成較多的工作
        - 比較耗資源, 不同 process 間資源不共享
        - 適合 cpu bound
            - 如果 cpu bound 用 multi-threading 效能可能會下降，因為 cpu loading 很滿還需要輪不同 thread
    - multi-threading
        - 可以讓相同工作可能可以在更短時間完成
        - 比較不耗資源, 共用 memory space
        - 適合 io bound
            - 最大化 cpu 週期
- ref
    - https://www.maxlist.xyz/2020/04/09/concurrency-programming/
    - https://www.geeksforgeeks.org/difference-between-multithreading-vs-multiprocessing-in-python/
    - https://www.maxlist.xyz/2020/03/15/python-threading/

## virtualization
- vm
    - 硬體層級的虛擬化
    - 可將單台 host 的硬體資源切割並最佳化利用
        - 不一定想讓整台都在同一個環境作業
    - 同時管理多台 vm 較管理多台有自己硬體的 host 容易
    - 安全性高：不同 vm 環境以 os 隔離開來
    - 缺點：抽象化層級太高，不易切割應用程式（ex. 只是要切割不同 web application 環境卻要切割 os，太浪費資源）
    - hypervisor(vm 管理軟體) type
        - bare metal
            - 直接在 host 上架 hypervisor
            - 較快：少了透過 host 的 os 轉送到硬體
        - hosted
            - 透過 host 的 os 下載 hypervisor application
            - 較慢
- container
    - os 層級的虛擬化
    - 把不同應用程式以容器隔離
    - 優點
        - 啟動快速：共用 host 的 os kernel
        - 可移植性：將 application 會用到的檔案、環境都包在 container，不用管不同 os 的設定
        - 安全性高：用 namespace 將容器間的環境隔離開來（ex. network, storage）、用 cgroup 可以限制單個 container 的資源使用量（記憶體、檔案快取、CPU 使用、磁碟 I/O），ex. 所以有一個 container 被駭掉不會完全影響到整個 vm 的流量
            - 可隔離開發和測試環境（且部屬快速
            - 但相較於 vm 安全性低：因共用 os kernel
        - 同時多個 container：容器啟動或關閉，在硬體資源充足的情況下不互相影響
        - 擴展性高：可以很輕易的開多個 container 分散流量
    - 缺點
        - 管理複雜：因將一個 application 切成多個微服務的容器需要管理、互相溝通、如何讓外部存取、存取其他服務的權限、可以使用的資源，尤其是在部屬了多種不同的 application 在多台 VM 上（導致學習成本高）
            - ex. 要部屬資管系、學校要用的一堆系統
        - 安全性疑慮：共用 os kernel，若 kernel 有問題 所有 container 甚至 host 都會受影響
    - image
        - 可以在 image 上疊加 image（ex. 在 php image 上加 CI image），使用 overlay2 讓整個 image 看起來只有一個 file system
            - overlay2
                - 一種 UnionFS (聯合檔案系統)
    - Dockerfile 指令
        - `from`
            - 要從哪一個 image 作為 base image 再修改
        - `env`
            - 環境變數
        - `run` vs `cmd` vs `entrypoint`
            - 兩種 type
                - Shell form command param1 param2
                    - 是以 `/bin/sh -c` 的方式執行
                - Exec form ["executable","param1","param2"]
                    - 以執行檔方式執行，所以會無法取得環境變數
                        - 但可以用 `["/bin/sh", "-c"]` 執行就有了
                        - 所以也可以用不是此預設的 shell 來執行
            - run
                - 建立 container 時會執行的指令
                - 通常用來 install package
            - cmd
                - container 建立完成時執行
                - 若有多個，只有最後一個 `cmd` 有效
                - 若 `docker run` 有帶參數就不會執行
            - entrypoint
                - container 建立完成時執行
                - 若有多個，只有最後一個 `entrypoint` 有效
                - 一定會執行
            - 總結
                - run 是用來安裝一些套件（ex. `apt install nodejs`）
                - `cmd`, `entrypoint` 是 container 建完要執行的（ex. `node index.js`）事情
                    - 如果是可能會被取代的事情，可以用 `cmd`，否則就用 `entrypoint`
            - ref
                - https://hackmd.io/@tienyulin/docker3
        - `expose`
            - 開放 port
        - ex.
            - ![](https://hackmd.io/_uploads/r1OvPFDf6.png)
    - docker network
        - ![](https://hackmd.io/_uploads/r1l7TDfeT.png)
            - default 用 bridge
            - bridge 讓同台 host 上不同 network 的 container 可以用 ip 溝通
    - docker swarm 
        - ingress
            - 讓 cluster 中就算是沒有部屬該 task container 的 node 也可以用同個 port 連到
                - ![](https://hackmd.io/_uploads/SyQx1FDzT.png)
                - 可以用 worker2 的 ip:80 連到 service
            - ingress routing mash
                - ![](https://hackmd.io/_uploads/HyWGJKvGp.png)
                - 可以發現 node 的流量會轉送到 172.18.0.2 （ingress-sbox, 它是一個 network namespace）
                    - ![](https://hackmd.io/_uploads/SkCsyYwGa.png)
                        - `sudo iptables -nvL -t nat`
                - 它會把流量轉送到有 task container 的 node 上
            - overlay
                - deploy stack 時如果沒有自己設定要用哪個 network，會幫 stack 自動建一個 overlay network
                    - ![](https://hackmd.io/_uploads/rkdazKvMp.png)
                    - 這樣同個 network 下，不同 node 的 container 就可以溝通了
- 總結 
    - VM 簡化了硬體資源配置與作業系統安裝的工作
    - Container 簡化了介於作業系統與應用程式 (App) 之間的環境建置工作
- ref
    - https://hackmd.io/@ncnu-opensource/book/https%3A%2F%2Fhackmd.io%2F%40qtNgFtaqR4Or_CLAuotXjw%2FB1EpbbGk3

## ORM (Object Relational Mapping)
- 避免直接寫 raw sql，而是可以用 object 方式操作 sql
    - 因為每個人的 sql 寫法不同，用此較好維護
    - 切換不同資料庫不須更新
        - mysql, mssql
    - 避免 sql injection
- django
    - 基本可以用 filter, all 去 select

- php ORM vs query builder
    - 都是避免直接寫 raw sql，而是可以用 object 方式操作 sql
        - 因為每個人的 sql 寫法不同，用此較好維護
        - 切換不同資料庫不須更新
    - 但某些複雜情況還是得用 raw sql

## unit test 單元測試
- 藉由檢查每一個 function 回傳的物件，可以確定程式是不是有跑在預期的結果裡面
- 可以避免之後改版不小心改動到原本正常的 function
- 主要是測試 function 的邏輯
    - 固定 data 是否可以得到相同輸出
- 3A
    - Arrange
        - 準備測試會用到的 object
    - Account
        - 用 object 做一些邏輯得出結果
    - Assert
        - 確認結果是否和預期一樣
- SUT(System Under Test)
    - 待測的系統，通常是 class
- 每個 function 個別測試
    - 若有 function 中用了另一個 function (ex. call library)，就兩個 function 分開測試
        - 降低耦合 : 避免測試 A function 時呼叫 B function，但 B function 是有問題的
        - mock vs stub vs spy
            - stub
                - 當要驗證的 function 需要第三方的回傳資料（沒有帶參數）
                    - ex. call db data
                - 就製作假資料取代第三方的回傳資料
                - 實際方法
                    - 可以用物件導向的方式，把會使用到的第三方物件用建構子的方式帶入 class
                    - 測試時再將帶入的第三方物件改為自己設定的
                        - ![](https://hackmd.io/_uploads/Hyr93P8gp.png)
                        - ![](https://hackmd.io/_uploads/Sk9unP8gp.png)

            - mock
                - 當要驗證的 function 需要引入第三方 function 並依照驗證 function <b>帶入的參數</b>做事情
                - 偽造第三方 function 並檢查帶入第三方物件的參數是否正確（檢查互動的方式是否正確）
                - 最後<b>是檢查第三方 function</b> 的資料是否正確
                    - ex. call function 寫入 log 檔
                    - ![](https://hackmd.io/_uploads/H19cEoDfp.png)
                        - 原本的第三方物件
                    - ![](https://hackmd.io/_uploads/HJtUfFLeT.png)
                        - 偽造假的第三方物件
                        - 偽造的要可以回傳需驗證 function 的 value
            - stub vs mock
                - 差別在於驗證原本的物件 or 假的第三方物件
                - ![](https://hackmd.io/_uploads/ryRifFIxT.png)
                - ref
                    - https://hackmd.io/@AlienHackMd/HycK5pEpo
                    - https://ithelp.ithome.com.tw/articles/10263479
            - spy
                - 驗證原本物件對其他物件的改變是否正確
                - ![](https://hackmd.io/_uploads/BJ-PIKIgp.png)
                - ref
                    - https://medium.com/starbugs/unit-test-%E4%B8%AD%E7%9A%84%E6%9B%BF%E8%BA%AB-%E6%90%9E%E4%B8%8D%E6%B8%85%E6%A5%9A%E7%9A%84dummy-stub-spy-mock-fake-94be192d5c46
            - 總結
                - stub
                    - 偽造 SUT 所需的第三方資料
                    - 驗證整個 SUT 的回傳結果
                - mock
                    - 偽造 SUT 傳入參數的第三方模組
                    - 驗證偽造的第三方模組收到的資料
                - spy
                    - 驗證被 SUT 改動到的模組被改動後的資料
- NodeJS
    - repo : 可用 `mocha` + `supertest` + `chai` 
        - `index.js`
            ```js=
            process.env.NTBA_FIX_319 = 1;
            const express = require('express')
            const app = express()
            const http = require('http');
            const server = http.createServer(app);

            // 要測試的 api

            app.get('/test', async function (req, res) {
              res.json({suc:'test suc'});
              res.end;
              return;
            }); 

            app.get('/test/good', async function (req, res) {
              res.json({suc:'test good'});
              res.end;
              return;
            }); 

            server.listen(3001, () => {
              console.log('listening on *:3001');
            });

            module.exports = app // 這邊要 export 給 test 的 script
            ```
        - `test.js`
            ```js=
            // supertest 可以讓專案不用另外跑起來也可以測試 api，它會幫你跑
            const request = require('supertest')
            const app = require('./index')
            const { expect } = require("chai")

            // dedscribe 為一個測試區塊，通常裡面會有多個 it，代表實際要測試的更小的測試區塊
            describe('GET /test', () => {
              it('should respond "test good"', (done) => {
                request(app)
                  .get('/test/good')
                  .end((err, res) => {
                    const text = res.text;
                    // chai 的語法，讓我們比較好描述要測試的東西
                    expect(text).to.be.equal('{"suc":"test good"}')
                    done();
                  })
              }),
              it('should respond "test suc"', (done) => {
                request(app)
                  .get('/test')
                  .expect(200, '{"suc":"test suc"}', done)
              })
            })
            ```
            - chai 用法
                - ![](https://hackmd.io/_uploads/HJ5LY1-f6.png)
        - `package.json` 要加上啟動 test
            ```json=
            "scripts": {
              "start": "node index.js",
              "test": "mocha test --exit "
            }
            ```
            - `--exit` : 測試完就結束 script，最好都要加
        - 開始測試 : `npm test`
            - ![](https://hackmd.io/_uploads/ByRuX1WfT.png)
    - gitlab ci
        - `.gitlab-ci.yaml`
            ```yml=
            image: node:latest

            stages:
              - build
              - test
              - deploy

            default:
              tags:
                - linux
              before_script:
                - now_time=$(date)
                - echo "${now_time}"
                - echo "started by ${GITLAB_USER_NAME}"
                - chmod 0777 ./node_modules/.bin/mocha

            build-job:
              stage: build
              script:
                - echo "running scripts in the build job"
                - npm install

            test-job1:
              stage: test
              script:
                - echo "running scripts in the test job 1"
                - npm test
              dependencies:
                - build-job

            test-job2:
              stage: test
              script:
                - echo "running scripts in the test job 2"

            deploy-job:
              stage: deploy
              script:
                - echo "running scripts in the deploy job"
            ```
        - pipeline
            - ![](https://hackmd.io/_uploads/rkkV81bzp.png)
        - test-job1
            - ![](https://hackmd.io/_uploads/HyYLUkbfT.png)
    - ref
        - https://medium.com/@stupidcoding/%E5%9C%A8node-js%E5%AF%AB%E6%B8%AC%E8%A9%A6-mocha-chai%E6%96%B7%E8%A8%80%E5%BA%AB-supertest%E6%A8%A1%E6%93%AC%E9%80%A3%E7%B7%9A-sinon%E6%9B%BF%E8%BA%AB-nyc%E7%B5%B1%E8%A8%88%E8%A6%86%E8%93%8B%E7%8E%87-f736c423b893
        - https://medium.com/cubemail88/node-js-%E7%94%A8-mocha-%E5%81%9A%E5%96%AE%E5%85%83%E6%B8%AC%E8%A9%A6-16dd9125e632

### 整合測試
- 整合和其他模組間的測試
- ex. db 和 web application

### end-to-end 測試
- 以客戶角度對整個系統測試
- 可以是人工也可以自動

### CI/CD
- Continuous Integration (持續整合) 與 Continuous Deployment (持續部署)
- CI
    - 系統 環境安裝 + 單元測試
- CD
    - 系統 環境安裝 + 單元測試 + 自動部屬
- 作用
    - push code 之後將剩下作業自動化
    - 通常包含：建置（安裝環境）、測試、部屬
- 優點
    - 自動化單元測試、部屬
    - 可以快速更新新版本（有小問題可以馬上更新），減少環境安裝、部屬時間
    - 降低操作錯誤性
- 流程通常會分 3 個 `stage` （也可以有多個）
    1. `build`
        - 環境安裝
        - 也可以省略此直接用該環境的 image
            - ex. `image: lorisleiva/laravel-docker:latest`
    2. `test`
        - 單元測試
        - 利用 `dependencies` 取得 `build` 安裝的環境
            - 所以可以測試不同環境（ex. php5, php6）
    3. `deploy`
        - 部屬到正式或測試環境
    - 只要有其中一個有問題，就不會往下執行
        - 建置成功才跑測試，測試成功才做佈署，這樣的先後關係在 GitLab CI 稱之為 `pipeline`
        - ![](https://hackmd.io/_uploads/r1N5PNIlT.png)
        - ![](https://hackmd.io/_uploads/B1lpJplMT.png)
    - 如果不分 `stage` 就會由上而下執行且不管上面有沒有成功下面的還是會執行
- `.yml`
    - 實際在哪個 stage 要執行那些指令是以 `job` 為單位
        - 每個 `job` 都是獨立的 container
        - 所以如果要 `job` 間要共用一些環境，就要用 `dependencies`
    - `runner` : `job` 的執行器
        - 預設每個 job 的 runner 各自獨立
        - 可以用 gitlab 上提供的，也可以用自己架的
            - 自己架
                - 要提供 ssh 金鑰登入
            - gitlab 上提供的 shared runners
                - settings -> CI/CD
                    - ![](https://hackmd.io/_uploads/r1lYRhgfa.png)
                - runners -> expand
                    - gitlab 提供的 runner, 每個 runner 都有 tag, 去區分不同環境的 container
                        - ![](https://hackmd.io/_uploads/ryDMJaDMa.png)
                        - ![](https://hackmd.io/_uploads/rk7rkaDzp.png)
    - `tag` : 指定哪一種類的 runner
    - `only` : 指定 branch
        - 通常 `deploy` stage 會指定 master branch（只有 master branch 的 code 會被 deploy 到正式環境）
    - `before_script` : `job` 執行 `script` 前會執行的指令
    - `script` : `job` 真正執行的指令
    - ex.
        - ![](https://hackmd.io/_uploads/HyrVtrIl6.png)
            - https://ithelp.ithome.com.tw/articles/10187654
        - ![](https://hackmd.io/_uploads/HkfHKH8xT.png)
            - https://nick-chen.medium.com/%E6%95%99%E5%AD%B8-gitlab-ci-%E5%85%A5%E9%96%80%E5%AF%A6%E4%BD%9C-%E8%87%AA%E5%8B%95%E5%8C%96%E9%83%A8%E7%BD%B2%E7%AF%87-ci-cd-%E7%B3%BB%E5%88%97%E5%88%86%E4%BA%AB%E6%96%87-cbb5100a73d4
        ```yml=
        stages:
          - build
          - test
          - deploy

        default:
          tags:
            - windows
          before_script:
            - Set-Variable -Name "time" -Value (date -Format "%H:%m")
            - echo ${time}
            - echo "started by ${GITLAB_USER_NAME}"

        build-job:
          stage: build
          script:
            - echo "running scripts in the build job"

        test-job1:
          stage: test
          script:
            - echo "running scripts in the test job 1"

        test-job2:
          stage: test
          script:
            - echo "running scripts in the test job 2"

        deploy-job:
          stage: deploy
          script:
            - echo "running scripts in the deploy job"
        ```
        - https://editor.leonh.space/2022/gitlab-ci/
    - 查看此次 commit 的 pipeline
        1. 點進 repo 的 branch
        2. Build -> Pipelines （此 repo 的所有 branch 的 pipeline 都會在這）
            - ![](https://hackmd.io/_uploads/r1D7xU8g6.png)
    - 查看單一 pipeline 中各別 job 執行狀況
        1. 點進 repo 的 branch
        2. Build -> Jobs
            - ![](https://hackmd.io/_uploads/BydFRSUga.png)
            - 點進 job 看 log
                - ![](https://hackmd.io/_uploads/SJXuRBLeT.png)
    - ex. nodejs
        ```yml=
        image: node:latest
        stages:
          - build
          - test
          - deploy

        default:
          tags:
            - linux
          before_script:
            - now_time=$(date)
            - echo now_time
            - echo "started by ${GITLAB_USER_NAME}"

        build-job:
          stage: build
          script:
            - echo "running scripts in the build job"
            - npm install
            - node index.js

        test-job1:
          stage: test
          script:
            - echo "running scripts in the test job 1"
            - curl 127.0.0.1:3001
          dependencies:
            - build_job

        test-job2:
          stage: test
          script:
            - echo "running scripts in the test job 2"

        deploy-job:
          stage: deploy
          script:
            - echo "running scripts in the deploy job"
        ```
        - ref
            - https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Nodejs.gitlab-ci.yml

## python 
- oo
    ```py=
    # 物件導向的最基礎，我們定義class，然後用它宣告我們的物件/實體(instance)
    # class 可以定義attribute(成員)、method(方法)，還有一些既定方法像是初始化__init__
    # class裡的method，跟function基本上是一樣的，但第一個參數是self，會指向此實體本身

    # 定義一個class叫做Person
    # 我們習慣把class的名稱定義成UpperCaseCamelCase，字的開頭大寫
    class Person:

      # 定義class attribute，所有實體會共用它
      kind = 'Person'

      # 初始化，當宣告實體的時候這個方法會被呼叫
      # 用self.xxx來定義實體的 attribute，每個被宣告的實體會有自己的attribute 
      def __init__(self, name, age):
        self.name = name; 
        self.age = age;

      # 定義實體的方法
      def say_hi(self, test):
        print(f'Hi, my name is {self.name}, I am {self.age} years old {test}')

    # 使用定義好的Class宣導實體(instance)
    mike = Person('Mike', 5) # 帶進去的參數Mike與5分辨對應__init__裡的name與age
    john = Person('John', 10)

    # 呼叫實體的方法
    mike.say_hi('god') # output: Hi, my name is Mike, I am 5 years old
    john.say_hi('good') # output: Hi, my name is John, I am 10 years old

    # 印出實體attribute
    print(mike.name) # output: Mike
    print(john.name) # output: John

    # class attribute是共用的，不管是class本身或是instance都能使用
    print(mike.kind) # Person
    print(john.kind) # Person
    Person.kind = "pig"
    print(mike.kind) # pig
    print(john.kind) # pig
    ```
- set
    - 過濾重複資料
        ```py=
        a=[1,1]
        list(set(a)) # [1]
        ```
- 帶入 variable
    - `print(f"{a}")`
- 相反字串, list
    - `a[::-1]`
- `for i in range(5)` 
    - 一開始會先建 [0~4] 所以每次 loop i 會照順序被改
    - 所以在 loop 改 i 的值（ex. `i=500`）, 下次 loop 還是會變成 0~4 中的順序的值 
- `a[:3]` = `a[0:3]`
    - a index = 0~2

- link list 資結
    ```py=
    # Definition for singly-linked list.
    class ListNode:
         def __init__(self, val=0, next=None):
             self.val = val
             self.next = next
    ```
    - 參數可設 default
- class 是共用 address
    ```py=
    a = test(5)
    a.printN() # 5
    c=a
    c.n = 8
    a.printN() # 8

    class test :
        def __init__(self, n) :
            self.n = n

        def printN(self) :
            print(self.n)
    ```
- linked list
    - 變數是 object 的話，變數內容是指向 object 的 address
        ```py=
        a = [1,2,3]
        b = a
        a[1] = 8 # a, b 指向相同 object 的 address, 所以 a 改 b 會改
        print(b) # [1,2,8]

        a = [1,2,3]
        b = a
        a = [1,2,8] # a 指向其他 address, 而 b 還是指向原本 object 的 address
        print(b) # [1,2,3]
        ```
    - 錯誤想法
        ```py=
        # head = head of a linked list
        # new head, it's value = head.val
        new_head = head
        temp_new_head = new_head
        head = head.next # 一定要加這行
        # 找全部不是 val 的 node
        while head != None :
            if head.val != val :
                new_head.next = head # 不然這邊一開始 new_head = head, 所以這樣的意思等於 head.next = head, 就會無限迴圈
                new_head = head
            head = head.next
        return temp_new_head
        ```
- heap
    ```py=
    import heapq # 需引入

    a = [2, 1, 4, 0, 8]

    # 轉為 min heap（若要轉為 max heap 可以先將 data 都 * -1）
    heapq.heapify(a)
    print(a) # [0, 1, 4, 2, 8]

    # 以下 pop, push 都須先轉為 heap

    # pop min
    heapq.heappop(a) # 0
    print(a) # [1,2,4,8]

    # push
    heapq.heappush(a, 3)
    print(a) # [1, 2, 4, 8, 3]
    ```
    - 題目
        - https://leetcode.com/problems/kth-largest-element-in-a-stream/
        - https://leetcode.com/problems/last-stone-weight/
        - https://leetcode.com/problems/top-k-frequent-words/
- dfs bfs
    1. ![](https://hackmd.io/_uploads/BJZ4_vMGa.png)
        - https://leetcode.com/problems/path-sum/description/
        - dfs
            ```python=
            # Definition for a binary tree node.
            # class TreeNode:
            #     def __init__(self, val=0, left=None, right=None):
            #         self.val = val
            #         self.left = left
            #         self.right = right
            class Solution:
                def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool:
                    # dfs

                    # leaf node's sum
                    leaf_sum = []

                    # dfs traversal
                    self.dfs(root, leaf_sum)

                    # 最後檢查是否 target in leaf node's sum
                    if targetSum in leaf_sum :
                        return True
                    else :
                        return False

                def dfs(self, root, leaf_sum) :
                    if root != None :
                        # left and right child
                        if root.left != None :
                            # add parent value
                            root.left.val += root.val
                            # 繼續往左做到底
                            self.dfs(root.left, leaf_sum)
                        if root.right != None :
                            # add parent value
                            root.right.val += root.val
                            # 繼續往右做到底
                            self.dfs(root.right, leaf_sum)
                        # 是 leaf node, 加到 leaf node's sum
                        if root.left == None and root.right == None :
                            leaf_sum.append(root.val)
            ```
            - bfs
                ```python=
                def bfsSol(self, root: Optional[TreeNode], targetSum: int) -> bool:
                    # bfs

                    # leaf node's sum
                    leaf_sum = []

                    # init queue, put the root
                    queue = [root]

                    # bfs traversal
                    while len(queue) > 0 :
                        # dequeue
                        node = queue[0]
                        queue.remove(node)

                        # 因可能 root is None
                        if node != None :
                            # left and right child
                            if node.left != None :
                                # add parent value and enqueue
                                left = node.left
                                left.val += node.val
                                queue.append(left)
                            if node.right != None :
                                # add parent value and enqueue
                                right = node.right
                                right.val += node.val
                                queue.append(right)

                            # check is leaf
                            if node.left == None and node.right == None :
                                leaf_sum.append(node.val)

                    # 最後檢查是否 target in leaf node's sum
                    if targetSum in leaf_sum :
                        return True
                    else :
                        return False
                ```
    2. https://leetcode.com/problems/average-of-levels-in-binary-tree/description/
- dictionary
    - 撈 key, value
        - `for key, value in dict.items() :`
    - 撈 key
        - `for key in dict.keys()`
    - 撈 values
        - `for val in dict.values()`
    - 確認 key 是否存在
        - `key in dict`
            - True or False
- list
    - 可以相加不能減： `a = [1,2] + [3] # [1,2,3]`
    - 放到最前面 : `a.insert(0, 4) # [4,1,2,3]`
    - 相乘 : `[1,2] * 2 # [1,2,1,2]`
- permutation 排列（n! 種）
    ```python=
    class Solution:
        def permute(self, nums: List[int]) -> List[List[int]]:
            output = [] # pass by reference
            self.perm(nums, [], len(nums), output)
            return output

        # deep copy of list
        def copy(self, obj) :
                n_obj = []
                for i in obj :
                    n_obj.append(i)
                return n_obj

        # 排列
        def perm(self, nums, cur, total_len, output) :
            # 長度到了就結束
            if len(cur) == total_len :
                # 是結果，所以需 deepcopy
                output.append(self.copy(cur))
            else :
                # 每個點都做所有 value 的排列
                for i in range(len(nums)) :
                    # 這個點這一次要用的 value
                    temp = nums[i]
                    # 加到目前 list
                    cur.append(temp)
                    # 從原本 nums 中移除讓下個點不能用
                    nums.remove(temp)
                    # 下個點以剩下的 nums 繼續做
                    self.perm(nums, cur, total_len, output)
                    # 這個點做完此 value，換一個 value 所以要移除
                    cur.remove(temp)
                    # 把這次用的 values 插回去原本 nums 的 index
                    nums.insert(i, temp)
    ```
- combination 組合（C n 取 k）
    ```python=
    class Solution:
        def combine(self, n: int, k: int) -> List[List[int]]:
            output = []
            self.dfs(n, k, [], 1, output)
            return output

        # 遞迴 k 次
        def dfs(self, n, k, cur, now, output) :
            # 長度 = k, 結束
            if len(cur) == k :
                output.append(cur.copy())
            else :
                # 每個點只能用(起點是)前面點用過以後的 value(i+1)
                for i in range(now, n+1) :
                    cur.append(i)
                    self.dfs(n, k, cur, i+1, output)
                    cur.remove(i)
    ```
- note
    - `//` : 沒小數
    - `/` : 有小數
## dns
- 一個 dns server 可以有多個 dns zone
    - 每個 dns zone 會有自己的 SOA
        - SOA (Start of Authority) : zone file 的第一筆record，主要記載這個 zone 相關的資訊，例如管理員信箱、用來辨別是否有更新的序號、refresh、TTL（超過時間就要向 dns server 重要一次 dns record）
            - serial 序號
                - 讓 secondary name server 知道有更新過
            - refresh
                - secondary name server 多久要來和 primary 確認是否有更新資料
    - 可以一個 dns zone 裡面放很多筆 dns record，但可能有些 domain 是想要有特殊設定（可能是網路內部才能存取的 domain）
- DNS load balance
    - 可以設定多個 NS 的 dns server，就可以 load balance
    - zone transfer
        - 為了讓資訊同步，將 primary nameserver 的 zone file 複製一份到 secondary nameserver 的這個溝通過程，就稱為 zone transfer。
- DNS cache poisoning (DNS spoofing)
    - dns resovler 要 dns query 時，偽造錯的 dns 紀錄給它
    - 只要偽造的 server 可以比正確的 server 還要早傳紀錄給 resolver 及包含一些正確資訊可能就可以
        - 知道要回傳給 resolver 的 port （可以大量送）
        - 應用於中國的防火牆
- DNSSEC (DNS security)
    - 將 dns record 用非對稱式加密
    - 可以確保 dns record 完整性，不會被竄改
- dns recon
    - 取得 dns server 的所有 dns record
    - 一般來說不能亂給別人看到所有紀錄，因為可能有些是內部使用的 domain
    - type
        - passive recon
            - 透過 browser search engine 找 subdomain
            - 但只能找到網頁的服務（ftp 就找不到）
        - active recon
            - 利用 dns server 漏洞直接向 dns server 拿完整資料
    - ref
        - https://tech-blog.cymetrics.io/posts/crystal/dns-hacking-3/
- rndc (remote name daemon control)
    - 可用 rndc command 遠端或本地控制管理Bind，並以加密方式來傳送資料
- 當網管時管理 dns 哪邊沒做好
    - 之後可以加上 dnssec
- 不安全的 DNS 服務會遭什麼攻擊，有什麼辦法可以解決？
    - 被竄改 dns record
        - DNSSEC
    - 不存在的 dns record 是否真的不存在
        - NSEC
            - 將 dns record 依首個字的字母大小排序，若找不到 record 會回傳下一筆（依字母排序的）存在的 dns record
            - 這樣就無法偽造下一筆的 dns record 不存在
            - ex.
                - ![](https://hackmd.io/_uploads/HkII9Zxfp.png)
- deploy bind9 on ubuntu
    - https://magiclen.org/ubuntu-server-dns/
## 前後端分離
- 不分離
    - server side render
    - 前端改，後端可能需要重跑
- 分離
    - client side render
        - 讓 SEO 評分降低，因只能爬到一點資料
    - 降低前後端耦合
        - 增加開發效率、分配工作
        - 更易維護、找問題
- path traversal
    - hacker 利用 `../` 存取其他 path 的檔案 or api
    - ![](https://hackmd.io/_uploads/B1RyuiMM6.png)
    - ![](https://hackmd.io/_uploads/r1Y7OofMT.png)
    - ![](https://hackmd.io/_uploads/BJB-djMMa.png)
    - sol
        - api 都要驗證 token
## 基因演算法GA (Gene Algorithm)
- 因為要找出最佳解會花太久時間，此可以快速找出近似最佳解
    - 如果是要找出一個外送員要送到 n 個客戶的最佳路線問題就是 O(n!)
- 步驟
    1. 將 data 編碼成基因
        - 我們基因是由 city + car 組成
            - `c3 c2 v1 c1 c8 v2 ...`
            - `vx` : 第 x 個 car
            - `cx` : 第 x 個 city
    3. 產生 init population (隨機的初代群集)
    4. 幫 population 中的個別染色體做 fitness 評分
        - 一般會有多個情況影響到 fitness
            - 單一 car 超過最大最小限制公里 fitness 要加倍懲罰
            - 單一 car 跑的城市數量超過最大最小也要懲罰
                - 跑太少懲罰比要跑太多高
            - 所有 car 跑的公里數的標準差太大也要懲罰
                - 而且這個懲罰倍數要很高，因為跑的公里數要平均最重要
        - 要算哪個條件要懲罰幾倍較麻煩
        - 所以 fitness 算出來異常也比較麻煩, 要過濾掉多個條件
    5. 對 population 做天擇，保留 fitness 較好的染色體
        - selection tournament
            - 天擇方法
            1. 從 poplation 隨機選幾個染色體
            2. 從幾個染色體中挑 fitness 比較好的染色體做為新的 population
    6. 將 population 中部分的染色體做兩兩的交配，利用他們的基因產生新的下一代的染色體的基因，這些新染色體會組成下一代的 population
        - 採雙點交配法
            - 會隨機切一半是 parent1 的基因的順序，剩下另一半會是 parent2 基因的順序
- ref
    - https://hackmd.io/@labNote/HyPHPwsRj
    - https://hackmd.io/9Os2l76XTem9u9eUx_ZmBA?view#Travelling-salesman-problem%EF%BC%88TSP-%E6%97%85%E8%A1%8C%E6%8E%A8%E9%8A%B7%E5%93%A1%E5%95%8F%E9%A1%8C%EF%BC%89
## terraform
- 和 ansible 一樣都可以用來實現 IaC
- 若想要新開一個測試環境，需要手動
    - 去 aws 租一台 server, 並設定環境（ex. 網路）
    - install docker, db ... 並設定
    - 把 repo 放上去並跑起來
- 但 terraform 可以把這些流程用腳本自動化
    - ![](https://hackmd.io/_uploads/S1yYT1HfT.png)
    - provider : 雲端服務的提供者, ex. aws, azure
- ref
    - https://www.huanlintalk.com/2023/07/terraform-overview.html

## galera
- synchronous replication
    - 採用兩階段的 commit 可以確保所有 replicas 的 commit 都是同步，只可能是全部都有或是都沒有
    - 優
        - 不管啥時 failover 所有 replicas 資料都會是同步的
        - 可以用 haproxy 做 load balance 增加 performance
        - 可以用 haproxy 做 failover
    - 速度慢
        - 但隨著 cluster 中的 replicas 增多，交易 delay 會大幅增加，因須同步更多台 replicas
        - 且因每一台 replicas 都可以被寫入，所以須處理多個 replicas 的 confict
## master-slave
- asynchronous replication
    - master 會直接完成交易並將 event 寫入到自己的 log，slave 再定期去和 master 拿新的紀錄
    - 所以 master 不會知道 slave 有哪些紀錄，也不能保證任何 slave 和 master 的資料是同步的
        - 所以當 master 有問題要 failover/failback(故障自動轉移) 時可能會有已經 commit 的紀錄卻沒有同步到 slave 的問題
    - 速度快
        - 不用處理 commit 同步問題
        - 只有 master 能被寫入，不須處理多個 replicas 的 confict
  - ref
      - https://galeracluster.com/library/documentation/tech-desc-introduction.html  

- master-slave + keepalived
    - ![](https://hackmd.io/_uploads/Bk1H-XjGT.png)
    - 先將 A B 做戶為主從的設定
    - 再 A B 都安裝 keepalived 並設定一個虛擬 ip
    - 客戶就連這個虛擬 ip 就可以

## vue.js
- 概念滿像是把 html 裡面會用到的內容（屬性, value）都分出來元件化
- html 放在 `<template>`, code 放在 `<script>`
- 用 MVVM declarative render 概念帶入變數 : `{{變數}}`
    - MVVM
        - model
            - 處理資料
        - view
            - 畫面呈現
        - viewModel
            - 將 model 資料即時轉換給 view 做呈現
- html 用 v-for(for loop) 取出元素
    - ![](https://hackmd.io/_uploads/S1FGhXjf6.png)
    - ![](https://hackmd.io/_uploads/ByhKpQjzT.png)
- computed : 可以做依些運算再回傳值
    - ![](https://hackmd.io/_uploads/SyPk6XofT.png)

## ref
- https://hackmd.io/@splitline/BkALfYY5r