l API Services是什麼?
上面的灰底小字告訴我們,這是個選項,可開可不開。啟用後可以透過port 31401、http的方式跟區塊鏈互動,並且會消耗大量的磁碟空間。
Pi節點程式的API Services就是「Horizon API」。
Horizon是一個RESTful API,可以讓人們輕鬆地透過http跟Stellar Core溝通,而不必擔心底層細節,也不用懂得Stellar SDK。所以如果節點沒有開port,啟用API Services就沒意義,因為外部連不進來。除非你自己會寫程式,要在本機提交交易。(但有另一件詭異的事,如果你會寫程式,也一定是在自己的機器上提交交易,不會用別人的機器,所以一般人啟用API Services到底有什麼意義?)
它可以讓你做到:
l 向網路提交交易
l 讀取有關區塊當前狀態的訊息(如帳戶餘額)
l 探索歷史資料(例如賬戶之前提交給網路的交易)
² 為什麼需要Horizon呢?
Stellar Core著重的是驗證交易並維護「當前」區塊的狀況,並未針對查詢最佳化。它使用的XDR檔難以閱讀,且缺乏有用的索引,因此很難查找。未優化的查詢實際上會減慢節點的速度,在最壞的情況下,這可能會導致它失去共識。
Horizon旨在允許開發人員提交交易並有效地使用網路資料。它從Stellar Core攝取(Ingest) XDR資料並將其解碼為更容易使用的JSON,讓閱讀區塊的當前狀態或探索歷史資料變得容易。同時讓Stellar Core可以專注於共識的工作。
² 為什麼執行Horizon會消耗大量的磁碟空間?
Horizon從Stellar Core攝取資料,將其處理成「transaction metadata」(例如balance history、account effects等資料依照時間序列處理),然後儲存在Horizon DB(使用PostgreSQL資料庫),所以會消耗磁碟空間。
隨著時間的推移,歷史紀錄不斷累積,從而增加資料庫使用的存儲空間。可以使用 --history-retention-count flag 或 HISTORY_RETENTION_COUNT 環境變數來設定希望保留的區塊數量,但是Pi沒有設定,所以會無限增長。
結論,到底要不要執行Horizon?我的建議是看個人,如果你的節點有開port、磁碟空間足夠,想對整個Pi Network多盡一份力,那就執行吧。
附上老尼的回答:
l 絕大多數人的Horizon都沒有正常運作!
你以為只要打開API Services小紅點,它就有正常運作嗎?其實並.沒.有!來看看幾個證據:
這是一台運作了半年的節點,區塊大小有9GB多,將近10GB了,但Horizon資料庫只有8MB,而且裡面的表格的資料筆數都是0。
SELECT pg_size_pretty( pg_database_size('horizon') );
Horizon的主頁面( http://localhost:31401/ )顯示最後攝取的區塊編號是0。
%appdata%\Pi Network\docker_volumes\supervisor_logs\horizon-stdout---supervisor-xxxxxx.log 全都是如下的錯誤:
原來當Horizon開始執行時,Horizon不會立即開始攝取,必須等到Stellar Core建立其第一個歷史存檔快照(Stellar Core每64個區塊建立一次快照,這意味著攝取將延遲到第64個區塊)。所以Horizon必須先向歷史主機取得最新的歷史存檔快照,但是歷史主機的URL設定錯誤,因此永遠無法開始攝取。
Horizon的設定檔位於 %appdata%\Pi Network\docker_volumes\stellar\horizon\etc\horizon.env。內容如下,正是HISTORY_ARCHIVE_URLS這個環境變數設定錯誤。原因是CT在發佈pi-node-docker image檔之後才改了歷史主機,反正沒什麼大影響,所以就沒有修正image檔了。
正確的HISTORY_ARCHIVE_URLS是https://history.testnet.minepi.com/ (跟stellar-core.cfg中[HISTORY.cache] get的URL相同)。
改完之後,再重新啟動API小紅點即可。
P.S.
啟動/停止Horizon最好用API Services小紅點介面,而不是supervisorctl指令。
因為,API Services小紅點開啟的情況下,即使用supervisorctl stop horizon指令關閉,Pi Node Software仍會再把它帶起來。
反之,API Services小紅點關閉的情況下,即使用supervisorctl start horizon指令啟動,Pi Node Software仍會再把它關閉。
但如果只是要重新啟動,可以用supervisorctl restart horizon。
查看 %appdata%\Pi Network\docker_volumes\supervisor_logs\horizon-stdout---supervisor-xxxxxx.log 的訊息。
首先是有關狀態(state)攝取的訊息:
在狀態攝取期間,每處理100000個條目(entry),會記錄目前處理的數量(至2022/11/27為止,測試網有將近29500000個條目,我的節點CPU是i7-12700,處理時間約3小時45分,資料庫空間26GB)。
狀態攝取完成後,它將繼續從檢查點區塊(本例中為10118015+1)之後的下一個區塊開始區塊攝取,以更新transaction metadata狀態:
Horizon的主頁面( http://localhost:31401/ )可看到區塊攝取的進度。
Horizon攝取區塊很吃系統資源,在這個過程中,我的CPU使用率增加約30%~40%。機器效能不好的人請三思。
l 初始化Horizon資料庫
當你把事情搞砸了怎麼辦?永遠要知道怎麼復原才開始動手。
先停止Horizon,關掉API Services小紅點。
2. 刪除Horizon資料庫:DROP DATABASE horizon;
3. 建立Horizon資料庫:CREATE DATABASE horizon;
4. 賦予stellar帳號存取資料庫權限:GRANT ALL ON DATABASE horizon TO stellar;
5. 初始化資料庫:stellar-horizon db init
6. 重新開啟API Services小紅點。
l 設定Horizon保留的歷史區塊數量
隨著時間的推移,不斷有新的區塊產生,Horizon資料庫的空間也將無限增長。但除非你需要維護歷史檔案,否則應該將Horizon配置為僅在資料庫中保留一定數量的區塊。可以使用 --history-retention-count flag 或 HISTORY_RETENTION_COUNT 環境變數來設定希望保留的區塊數量。Horizon子系統每小時都會清除過期資料,或者,Horizon提供了一個強制清除的命令: stellar-horizon db reap 。
修改 %appdata%\Pi Network\docker_volumes\stellar\horizon\etc\horizon.env ,加入一行 export HISTORY_RETENTION_COUNT=nnn 。範例中的17280個區塊大約是一天。
然後重新啟動Horizon。
一個小時後可以看到清除的紀錄,只保留17280個區塊(10494012 - 10476733 + 1 = 17280)。每個小時也都會執行。
最早歷史區塊也跟著變動:
或者是用stellar-horizon db reap指令手動清除:
詳細來講,是先檢查history_ledgers的最大最小區塊序號,如果差距小於HISTORY_RETENTION_COUNT,就不用刪除資料。
SELECT COALESCE(MAX(sequence), 0) FROM history_ledgers;
SELECT COALESCE(MIN(sequence), 0) FROM history_ledgers;
接著查詢exp_ingest_last_ledger,基本上應該是跟前述的最小區塊序號相同。
SELECT key_value_store.value FROM key_value_store WHERE key_value_store.key = 'exp_ingest_last_ledger';
將此數字減去HISTORY_RETENTION_COUNT,即得到new_elder區塊序號,再由new_elder查詢history_ledgers中的id,即得到要刪除的鍵值。會刪除以下7個表格的資料:
DELETE FROM history_effects WHERE history_operation_id >= 0 AND history_operation_id < $1;
DELETE FROM history_operation_participants WHERE history_operation_id >= 0 AND history_operation_id < $1;
DELETE FROM history_operations WHERE id >= 0 AND id < $1;
DELETE FROM history_transaction_participants WHERE history_transaction_id >= 0 AND history_transaction_id < $1;
DELETE FROM history_transactions WHERE id >= 0 AND id < $1;
DELETE FROM history_ledgers WHERE id >= 0 AND id < $1;
DELETE FROM history_trades WHERE history_operation_id >= 0 AND history_operation_id < $1;
l 從哪裡開始攝取區塊?
當Horizon啟動時,並不是將Core DB全部的資料都抓過來。它會先檢查horizon DB兩個資料:
一是,最後攝取的區塊紀錄(exp_ingest_last_ledger只是一個數字,不代表horizon DB內真的有這個區塊)
SELECT value FROM key_value_store WHERE key = 'exp_ingest_last_ledger';
二是,實際上horizon DB所儲存的最大區塊序號
SELECT COALESCE(MAX(sequence), 0) FROM history_ledgers;
理論上這兩個數字應該是相同的。如果是0(第一次啟動Horizon),就會從Stellar Core目前的區塊開始攝取,否則就從該數字+1的區塊開始攝取。
另外,Stellar Core還用cursor來保留資料不會被Stellar Core garbage collection清除(如果Horizon要攝取的區塊已被清除,會出現「ledger does not exist」錯誤),大部分情況下cursor的數字也是跟前者相同(說大部分情況是因為可以手動drop cursor或set cursor)。但cursor的值並不影響Horizon從哪裡開始攝取,反而是Horizon會去修改cursor的值。
查看cursor:
stellar-core http-command getcursor?id="HORIZON"
如果exp_ingest_last_ledger(10212300)大於實際的區塊序號(10212256),Horizon啟動時,仍然會把缺少的區塊補齊。
但在隨後的狀態驗證卻會出錯。
反之,如果exp_ingest_last_ledger小於實際的區塊序號(10269450),Horizon啟動時,exp_ingest_last_ledger會被重設為0,並重新從History Archive Snapshot攝取資料(就跟Horizon第一次啟動一樣)。
(此處的10269350是offer_compaction_sequence,最新的檢查點區塊是10270463)
l 關API追區塊的迷思
網路上流傳一個說法,當節點剛安裝,或是按了「Remove all blockchain data」,或是本地區塊落後網路很多時,要先把API Services關閉,等區塊同步了,再將API Services打開。
其實這種說法一點根據都沒有,也沒有必要,因為Horizon根本沒有正常運作。如果說Horizon有在攝取區塊,會消耗大量CPU、I/O資源,效能比較差的機器可能會處理不來。但現在開著API Services大概也只多1% CPU資源而已,根本沒影響。
關於區塊延遲原因及Catchup的過程,請參考 https://yuanrui919.github.io/latency/ 和 https://yuanrui919.github.io/catchup/ 。