l API Services是什麼?

 

上面的灰底小字告訴我們,這是個選項,可開可不開。啟用後可以透過port 31401http的方式跟區塊鏈互動,並且會消耗大量的磁碟空間。

 

Pi節點程式的API Services就是「Horizon API」。

 

Horizon是一個RESTful API,可以讓人們輕鬆地透過httpStellar Core溝通,而不必擔心底層細節,也不用懂得Stellar SDK所以如果節點沒有開port,啟用API Services就沒意義,因為外部連不進來。除非你自己會寫程式,要在本機提交交易。(但有另一件詭異的事,如果你會寫程式,也一定是在自己的機器上提交交易,不會用別人的機器,所以一般人啟用API Services到底有什麼意義?)

 

它可以讓你做到:

l   向網路提交交易

l   讀取有關區塊當前狀態的訊息(如帳戶餘額)

l   探索歷史資料(例如賬戶之前提交給網路的交易)

 

²  為什麼需要Horizon呢?

 

Stellar Core著重的是驗證交易並維護「當前」區塊的狀況,並未針對查詢最佳化。它使用的XDR檔難以閱讀,且缺乏有用的索引,因此很難查找。未優化的查詢實際上會減慢節點的速度,在最壞的情況下,這可能會導致它失去共識。

 

Horizon旨在允許開發人員提交交易並有效地使用網路資料。它從Stellar Core攝取(Ingest XDR資料並將其解碼為更容易使用的JSON,讓閱讀區塊的當前狀態或探索歷史資料變得容易。同時讓Stellar Core可以專注於共識的工作。

 

²  為什麼執行Horizon會消耗大量的磁碟空間?

 

HorizonStellar Core攝取資料,將其處理成「transaction metadata」(例如balance historyaccount 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 Core64個區塊建立一次快照,這意味著攝取將延遲到第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_URLShttps://history.testnet.minepi.com/ (跟stellar-core.cfg[HISTORY.cache] getURL相同)。

 

改完之後,再重新啟動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個條目,我的節點CPUi7-12700處理時間約3小時45分,資料庫空間26GB)。

 

狀態攝取完成後,它將繼續從檢查點區塊(本例中為10118015+1)之後的下一個區塊開始區塊攝取,以更新transaction metadata狀態:

 

Horizon的主頁面( http://localhost:31401/ )可看到區塊攝取的進度。

 

Horizon攝取區塊很吃系統資源,在這個過程中,我的CPU使用率增加約30%~40%。機器效能不好的人請三思。

 

l 初始化Horizon資料庫

 

當你把事情搞砸了怎麼辦?永遠要知道怎麼復原才開始動手。

 

1.      先停止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 cursorset cursor)。但cursor的值並不影響Horizon從哪裡開始攝取,反而是Horizon會去修改cursor的值。

查看cursor

stellar-core http-command getcursor?id="HORIZON"

 

如果exp_ingest_last_ledger10212300)大於實際的區塊序號(10212256),Horizon啟動時,仍然會把缺少的區塊補齊。

 

但在隨後的狀態驗證卻會出錯。

 

反之,如果exp_ingest_last_ledger小於實際的區塊序號(10269450),Horizon啟動時,exp_ingest_last_ledger會被重設為0,並重新從History Archive Snapshot攝取資料(就跟Horizon第一次啟動一樣)。

(此處的10269350offer_compaction_sequence,最新的檢查點區塊是10270463

 

l API追區塊的迷思

 

網路上流傳一個說法,當節點剛安裝,或是按了「Remove all blockchain data」,或是本地區塊落後網路很多時,要先把API Services關閉,等區塊同步了,再將API Services打開。

 

其實這種說法一點根據都沒有,也沒有必要,因為Horizon根本沒有正常運作。如果說Horizon有在攝取區塊,會消耗大量CPUI/O資源,效能比較差的機器可能會處理不來。但現在開著API Services大概也只多1% CPU資源而已,根本沒影響。

 

關於區塊延遲原因及Catchup的過程,請參考 https://yuanrui919.github.io/latency/ https://yuanrui919.github.io/catchup/

 

回首頁