大家之前使用 mongodb_plugin 、mysql_plugin 或其他資料持久化外掛程式的時候,可能會發現 transaction 和 trace 的資料重複duplicate ( 多機環境下)。 在最初的時候只能在持久化的時候做去重處理,但 eos 之後已經推出了 read only 模式,可以避免資料出現 duplicate 的情況, 但筆者發現很多人不知道有這種模式,也不清楚這種情況的發生由來。接下來我們來分析下為什麼會出現這種情況,以及 read only 模式起到了什麼作用。
首先 read only 模式不能用於出塊節點,所以我們以乙個同步節點的立場來講述。
寫乙個持久化外掛程式,我們必須要有資料來源,也就是這幾個訊號,我們從這裡獲取資料,這裡使用的是觀察者模式,每當訊號源有新資料 emit 的時候就會呼叫我們定義的函式,具體觀察者模式的實現在這裡就不描述了,參考 mongodb_plugin **。
signalpre_accepted_block;
signalaccepted_block_header;
signalaccepted_block;
signalirreversible_block;
signalaccepted_transaction;
signalaccepted_confirmation;
我們會在 controller.push_transaction 發現這兩個函式的觸發。
transaction_trace_ptr push_transaction( const transaction_metadata_ptr& trx,
fc::time_point deadline,
uint32_t billed_cpu_time_us,
bool explicit_billed_cpu_time = false )
// ...
} /// push_transaction
ok, 看到這一步我們就知道 push_transaction 執行了 2 次同樣的 trx 才會導致這2個訊號 duplicate。
為什麼會執行 2 次呢?
trx 是通過什麼來廣播的呢, 塊廣播以及交易廣播, 那我們從這入手。
交易廣播
每個節點會接受全網上的交易,嘗試執行, 如果成功,則他繼續向其他節點廣播這個交易
// net_plugin.cpp
void net_plugin_impl::handle_message( connection_ptr c, const packed_transaction &msg)
// ...
// 接受交易, 並執行 push transaction
spatcher->recv_transaction(c, tid);
chain_plug->accept_transaction(msg, [=](const static_variant& result) ", ("m",result.get()->what()));
} else
peer_elog(c, "bad packed_transaction : $", ("m",trace->except->what()));
}dispatcher->rejected_transaction(tid);
});}// chain plugin.cpp
void chain_plugin::accept_transaction(const chain::packed_transaction& trx, next_functionnext)
第一次 push_transaction 的執行找到啦。
try else if( receipt.trx.contains() ) else
// ...
}//...
return;
} catch ( const fc::exception& e )
第二次執行 push transaction 也找到啦。
解決問題
問題找到了,接下來解決問題。
出現兩次呼叫 push_transaction 的操作,那麼肯定要禁掉其中乙個,才會使訊號只觸發一次,那同步區塊的步驟肯定不能禁掉, 塊廣播和交易廣播,我們只能選擇禁止交易廣播的執行,所以為什麼出塊節點不能用 read only 模式( ps: 交易廣播都被你禁掉了,我還怎麼打包區塊???黑人問號臉)
交易廣播有 2 個途徑乙個是接受鏈上的交易傳播, 乙個是通過 chain_api_plugin 的 push_transaction api 推送,所以禁掉這兩個就可以了。沒錯, read only 模式的作用就是禁止 2 途徑。
// net_plugin.cpp
void net_plugin_impl::handle_message( connection_ptr c, const packed_transaction &msg)
// ...
// 接受交易, 並執行 push transaction
spatcher->recv_transaction(c, tid);
chain_plug->accept_transaction(msg, [=](const static_variant& result) ", ("m",result.get()->what()));
} else
peer_elog(c, "bad packed_transaction : $", ("m",trace->except->what()));
}dispatcher->rejected_transaction(tid);
});}// controller.cpp
transaction_trace_ptr controller::push_transaction( const transaction_metadata_ptr& trx, fc::time_point deadline, uint32_t billed_cpu_time_us )
嗯,問題來了,如何開啟 read only 模式呢。
很簡單,在config.ini 加上read-mode = read-only 即可。
總結:
Fabric 原始碼解析 原始碼目錄解析
這裡對重要的一些目錄進行說明 bccsp 與密碼學 加密 簽名 證書等等 相關的加密服務 將fabric中用到的密碼學相關的函式抽象成了一組介面,便於拓展。bddtests 一種新型的軟體開發模式 行為驅動開 需求 開發 common 一些公共庫 錯誤處理 日誌處理 賬本儲存 策略以及各種工具等等 ...
EOS 原始碼解析 區塊回滾對交易的影響
在主網上玩耍的小夥伴們肯定遇到過區塊回滾導致自己的交易沒有上鏈。這種情況讓有些人誤以為區塊回滾會丟棄交易。其實區塊回滾並不是導致交易沒上鏈的主要原因,主要原因是交易過期了才導致交易被丟棄。流程描述 每個交易是會被廣播到全網每個節點上面的 ps 當然傳播過程中過期的話,當我沒說哈 假如出塊節點 a 打...
MyBatis原始碼解析 Mapper是如何獲得?
希望讀者們能將這個基礎的流程熟讀於心 此處的configuration在構造defaultsqlsession的時候會載入進來 public class defaultsqlsession implements sqlsession try catch exception e 這個問題我們後續再進行...