iT邦幫忙

2024 iThome 鐵人賽

DAY 17
0
Software Development

六邊形戰士程式設計系列 第 17

D17 - 樹狀搜尋問題 監控版 實作篇

  • 分享至 

  • xImage
  •  

想像我們有一個深不見底的樹狀結構,而且同一時間有多個使用者同時在使用它,這導致在查詢過程中有比較小的機率,可能出現資料一致性問題,例如以下情況

  1. 想像我們有以下結構的樹
    A  - A1 - A11 - ...
       - A2 - ...
       - A3 - ...
    
  2. 使用者 X 取得節點 A
  3. 使用者 Y 把 A2 刪掉了
  4. 使用者 X 根據 A 的子節點 A1, A2, A3 依序往下搜尋,發現找不到 A2 了

有時候因為開發成本以及效能考量,我們暫時無法解決「某些節點有時候會找不到的問題」,但至少我們可以監控這種現象出現的頻率,有了監控以後,當某個節點出現永久的損壞,我們就有機會提早發現並修復它,也可以追蹤造成它損壞的原因。

複習一下 D13 - 樹狀搜尋問題 非同步版 實作篇,會發現我們其實已經做過類似的事情

const getNameById = (target:string, parentId?:string) => async(id:string):Promise<string|undefined> =>  {
    const result = await getNode(id)
    if(result._tag==="NodeNotFoundError") {
        console.error(`get id ${id} failed, skip this node`) // 1. 最初版進行 "監控" 的點
        return undefined
    }
    if(result._tag==="Node" && result.text===target) return result.text
    const children = await Promise.all(result.childrenIds.map(getNameById(target,id)))
    return children.find(text=>text!==undefined) 
}

然而這樣的設計會違反「開放封閉原則」,因為「監控」是和「節點模組」不相關的,我們應該基於節點模組去擴展出監控功能,而不是直接把監控的邏輯嵌入到程式碼裡面,例如以下這樣

interface MyNode {
  _tag: "Node";
  id: string;
  text: string;
  childrenIds: readonly string[];
}

interface NodeNotFoundError {
  _tag: "NodeNotFoundError";
  id: string;
}

const getNode: (
  id: string,
  onNodeNotFoundError: OnNodeNotFoundError
) => Promise<MyNode | NodeNotFoundError> = async (id) => {
  const map: Record<string, MyNode> = {
    a: { _tag: "Node", id: "a", text: "a", childrenIds: ["a1", "a2", "a3"] },
    a1: { _tag: "Node", id: "a1", text: "a", childrenIds: ["a11"] },
    a11: { _tag: "Node", id: "a11", text: "a", childrenIds: [] },
    a2: { _tag: "Node", id: "a2", text: "a", childrenIds: [] },
    a3: { _tag: "Node", id: "a3", text: "a", childrenIds: [] },
  };

  if (map[id]) return map[id];
  else {
    return { _tag: "NodeNotFoundError", id };
  }
};

type OnNodeNotFoundError = (error: NodeNotFoundError) => void; // 1. 定義一個錯誤處理的鉤子

const getNameById =
  (
    target: string,
    onNodeNotFoundError: OnNodeNotFoundError // 2. 在執行階段才當作參數傳入
  ) =>
  async (id: string): Promise<string | undefined> => {
    const result = await getNode(id, onNodeNotFoundError); // 3. 再次把鉤子當作餐數傳入
    if (result._tag === "NodeNotFoundError") return undefined;
    if (result._tag === "Node" && result.text === target) return result.text;
    const children = await Promise.all(
      result.childrenIds.map(getNameById(target, onNodeNotFoundError))
    );
    return children.find((text) => text !== undefined);
  };

這樣做總算成功解開了節點和監控兩大模組的耦合,然而這樣做還是會有一些問題!

預計明天利用颱風假好好討論可能造成的問題以及解決方法


上一篇
D16 - 樹狀搜尋問題 非同步版 Python 篇
下一篇
D18 - 樹狀搜尋問題 監控版 TS優化篇
系列文
六邊形戰士程式設計30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言