Solidity
如何從 web3 訪問動態列表?
在我的應用程序中,玩家可以創建和加入遊戲。我將每個遊戲都儲存在一個映射中(uint => Game),並在創建遊戲時發送一個 GameCreated 事件。我的問題是有效地在客戶端獲取開放(尚未加入)遊戲的列表。
我可以遍歷所有 Game 對象並過濾掉那些已加入的對象,但這是不可擴展的。起初我認為監聽 GameCreated 事件會有所幫助,但我仍然必須使用 GameJoined 事件進行過濾,因為我認為我不能修改已經記錄的日誌。這具有相同的可擴展性問題。我可以通過僅獲取過去一周的日誌或其他方式來緩解它,但我不太喜歡這個選項。
我考慮製作一個動態大小的數組,其中每個項目都是打開遊戲的遊戲 ID,但為了更新這個列表,我需要能夠在加入遊戲時刪除條目,這需要將索引儲存在每個遊戲對象。但是當一個條目被刪除時,所有其他遊戲對像都需要使用新索引進行更新,這只是一團糟。
理想情況下,這個應用程序有一天會獲得大量流量,所以我需要一個有效的解決方案。我在這裡有什麼明顯的遺漏嗎?您遇到過更好的解決方法嗎?我希望我可以僅使用目前打開的遊戲迭代映射,但我認為這是不可能的。
你預計會有多少場比賽?
Solidity 可以一次返回整個數組,因此您可以將每個遊戲保存在一個數組中,一次呼叫將其全部返回,然後使用客戶端過濾。您可以嘗試模擬一個返回 10,000 個遊戲的函式,看看普通的舊 JS .filter() 是否能足夠快地處理它。(雖然我不知道節點能夠以多快的速度返回整個列表。)
但是讓我們說這行不通。我的下一個建議是連結列表。它不是 Solidity 原生的,您可能必須為它編寫一個庫以方便使用,但您可以在恆定時間內添加和刪除。我不確定汽油會貴多少(或更少?),但我懷疑它不會貴多少。
您需要像這樣使用雙向鍊錶:
struct GameListItem { uint prev; uint next; } mapping (uint => GameListItem) public openGamesList; uint public firstOpenGame; function addOpenGame (uint gameID) internal { require (gameID != 0, "Game ID is NULL"); GameListItem storage item = openGamesList [gameID]; require (item.next == 0, "Game is already in the list"); if (firstOpenGame == 0) { item.next = gameID; item.prev = gameID; firstOpenGame = gameID; } else { GameListItem storage first = openGamesList [firstOpenGame]; uint lastID = first.prev; GameListItem storage last = openGamesList [lastID]; item.next = firstOpenGame; item.prev = lastID; first.prev = gameID; last.next = gameID; } } function removeOpenGame (uint gameID) internal { require (gameID != 0, "Game ID is NULL"); GameListItem storage item = openGamesList [gameID]; uint nextID = item.next; require (nextID != 0, "Game is not in the list"); uint prevID = item.prev; if (nextID == gameID) firstOpenGame = 0; else { GameListItem storage next = openGamesList [nextID]; GameListItem storage prev = openGamesList [prevID]; next.prev = prevID; prev.next = nextID; if (firstOpenGame == gameID) firstOpenGame = nextID; } item.next = 0; item.prev = 0; }