原文地址:https://docs.corda.net/upgrading-cordapps.html#flow-versioning
任何初始化其他 flows 的 flow 必須要使用 @InitiatingFlow 注解,像下邊這樣定義:
annotation class InitiatingFlow(val version: Int = 1)
version 屬性默認值為1,定義了 flow 的版本。當(dāng)flow 有任何一個新的 release 的時候并且這個 release 包含的變動是非向下兼容的,這個數(shù)值應(yīng)該增加。一個非向下兼容的改動是一個改變了 flow 的接口的變動。
Flow 的接口是如何定義的?
Flow 的接口是通過在 InitiatingFlow 和 InitiatedBy flow 之間有序的 send 和 receive 調(diào)用來定義的,包括發(fā)送和接受的數(shù)據(jù)的類型。我們可以將 flow 的接口如下圖這樣表示:

在上邊的圖中,
InitiatingFlow:
- 發(fā)送了一個
Int - 接收了一個
String - 發(fā)送了一個
String - 接收了一個
CustomType
InitiatedByflow 恰恰相反: - 接收了一個
Int - 發(fā)送了一個
String - 接收了一個
String - 發(fā)送了一個
CustomType
只要IntiatingFlow和InitiatedByflows 遵循這個有序的一系列的動作,那么 flows 就可以按照任何你覺得合適的方式來實現(xiàn)(包括添加不共享給其他節(jié)點的業(yè)務(wù)邏輯)。
哪些是非向下兼容的改動?
Flow 可以有兩種主要的方式會變?yōu)榉窍蛳录嫒莸模?/p>
-
send和receive調(diào)用的順序變化:- 一個
send或者receive從InitiatingFlow或者InitiatedByflow 中被添加或者刪除了 -
send和receive調(diào)用的順序變了
- 一個
-
send和receive調(diào)用的類型變了
當(dāng)運行不兼容版本的 flows 會發(fā)生什么?
帶有非兼容接口的 InitiatingFlow 和 InitiatedBy flows 可能會出現(xiàn)下邊的行為:
- flows 會沒有明確原因地停住了并且永遠也不會終止,通常是因為一個 flow 在等待這著一個回復(fù),但是這個回復(fù)永遠不會從另一方返回來
- 其中的一個 flow 會帶有異常地結(jié)束:“Expected Type X but Received Type Y”,因為
send或者receive類型不正確 - 其中的一個 flow 會帶有異常地結(jié)束:“Counterparty flow terminated early on the other side”,因為一個 flow 向另外一個 flow 發(fā)送了一些數(shù)據(jù),但是后邊這個 flow 已經(jīng)結(jié)束了
我應(yīng)該如何升級我的 flows?
- 更新 flow 并且測試。在
InitiatingFlow注解中增加 flow 版本號。 - 確保已經(jīng)存在的所有版本的 flow 已經(jīng)運行完了并且沒有未結(jié)束的
SchedulableFlows在網(wǎng)絡(luò)中的任何節(jié)點中。這個可以通過清理節(jié)點的方式來實現(xiàn),接下來會講到。 - 關(guān)閉節(jié)點
- 用包含新的 flow 的 CorDapp JAR 文件替換掉原來的 CorDapp JAR
- 啟動節(jié)點
如果你關(guān)掉了所有的節(jié)點并且同時更新了他們的 flow 的話,可能產(chǎn)生任何的不兼容的改動。
對于一些節(jié)點可能仍舊繼續(xù)運行某個 flow 的以前版本的情況,這樣你的新版本 flow 可能會跟一個舊版本進行溝通,更新的 flows 需要具備向下兼容性。這可能是任何真正的部署中都會發(fā)生的問題,你可能不會很容易地去在整個網(wǎng)絡(luò)中去協(xié)調(diào)推出一個新的 code。
我該如何確保 flow 的向下兼容性?
InitiatingFlow 版本號會被包含在 flow session handshake 中并且通過 FlowLogic.getFlowContext 方法暴露給雙方。這個方法需要一個 Party 作為輸入,然后會返回一個 FlowContext 對象,這個對象描述了在對方節(jié)點上正在運行的 flow。它含有一個 flowVersion 的屬性,可以使用這個屬性來在不同的 flow 版本間來定制你自己的 flows,例如:
@Suspendable
override fun call() {
val otherFlowVersion = otherSession.getCounterpartyFlowInfo().flowVersion
val receivedString = if (otherFlowVersion == 1) {
otherSession.receive<Int>().unwrap { it.toString() }
} else {
otherSession.receive<String>().unwrap { it }
}
}
上邊的代碼演示了當(dāng) flow 的第一個版本期望收到一個 Int,但是后續(xù)的版本變成了期望收到一個 String。這個 flow 在跟其他仍然運行著包含舊的 flow 的舊的 CorDapp 之間還是能夠進行溝通的。
我該如何處理關(guān)于 in-lined subflows 的接口變化?
下邊是一個 in-lined subflow:
@StartableByRPC
@InitiatingFlow
class FlowA(val recipient: Party) : FlowLogic<Unit>() {
@Suspendable
override fun call() {
subFlow(FlowB(recipient))
}
}
@InitiatedBy(FlowA::class)
class FlowC(val otherSession: FlowSession) : FlowLogic() {
// Omitted.
}
// Note: No annotations. This is used as an inlined subflow.
class FlowB(val recipient: Party) : FlowLogic<Unit>() {
@Suspendable
override fun call() {
val message = "I'm an inlined subflow, so I inherit the @InitiatingFlow's session ID and type."
initiateFlow(recipient).send(message)
}
}
In-lined subflows 是當(dāng)跟對方初始一個新的 flow session 的時候被調(diào)用的 flows。假設(shè) flow A 調(diào)用 in-lined subFlow B,B 初始了一個跟對方的會話(session)。對方使用的 FlowLogic 類型決定應(yīng)該調(diào)用哪個對應(yīng)的 flow 應(yīng)該是由 A 決定的,而不是 B。這意味著 in-lined flow 的 response logic 必須要在 InitiateBy flow 里被顯式地實現(xiàn)。這個可以通過調(diào)用一個匹配的 in-lined counter-flow,或者在對方的被初始的父的 flow 中顯式地實現(xiàn)。In-lined subflows 也會從他們的父 flow 中繼承 session IDs。
因此,一個 in-lined subflow 的一個借口的改動必須要考慮對父 flow 接口也要有一個改動。
一個 in-lined subflow 的例子是 CollectSignaturesFlow。他有一個沒有 InitiateBy 注解的 response 的 flow 叫 SignTransactionFlow。這是因為這兩個 flows 都是 in-lined。這兩個 flows 是如何彼此交流的是通過調(diào)用他們的父 flows 來定義的。
在代碼中,in-lined subflows 看起來就是一個常規(guī)的 FlowLogic 的實例,但是沒有 InitiatingFlow 或者 InitiatedBy 注解。
In-lined subflows 是沒有版本的,因為他們的版本是繼承于他們的父 flow 的(InitiatingFlow 和 InitiatedBy)。
不是 InitiatingFlow 或者 InitiatedBy flow,也不是由一個 InitiatingFlow 或者 InitiatedBy flow 調(diào)用的 in-lined subflows ,更新的時候可以不考慮向下兼容的問題。這種類型的 flows 包括用來查詢 vault 的 utility flows,或者對外部系統(tǒng)進行查詢的 flows。