5. 错误处理¶
当我们为 Transaction DSL 框架添加一个 Action 时,
这个 Action 会组合其它的 Action 。比如, __sequential
Action ,里面会
放入一系列的其它 Action ,而 __procedure
则会包含两个 Action ,
一个是 normal Action ,一个是 __finally
Action。
做为 Action 的编写者,你不能假设你的 Action 所组合的是那种具体的 Action 。 因而任何 Action 都必须遵从某种约定。 所有的 Action ,在组合其它 Action 时,唯一可以作出的假设是每一个 Action 都 遵从这些约定。下面我们将会讨论这些约定。
5.1. Action 外部行为规范¶
Action 从外部看,总共有4个状态,它们的状态转换关系如下图所示:
下面我们对其进行详细说明。
5.1.1. IDLE¶
任何一个 Action ,单纯从外部看,在没有发生任何调用之前, Action 必然 处于 IDLE 状态。
而 IDLE 状态下,唯一合法的调用是 exec
,如果 exec
返回 CONTINUE
代表Action进入 WORKING 状态。
而 WORKING 的含义是,此 Action 需要进一步的异步消息激励。
5.1.2. WORKING¶
在 WORKING 状态下,
exec
不可再被调用,否则应返回FATAL_BUG
;如果有事件到达,可以调用
handleEvent
进行处理;其可能结果如下:
如果调用
stop
,其可能结果如下:
如果调用
kill
,Action立即应进入 DONE 状态。
5.1.3. STOPPING¶
在 STOPPING 状态下,
exec
不可再被调用,否则应返回FATAL_BUG
;如果调用
stop
,不应对Action产生任何影响,而直接返回CONTINUE
;如果调用
kill
,应立即进入 DONE 状态如果调用
handleEvent
, 其可能结果如下:
5.1.4. DONE¶
在 DONE 状态下,
exec
,stop
,handleEvent
都不可再被调用,否则应返回FATAL_BUG
;如果调用
kill
,应该对Action状态无任何影响,依然处于 DONE 状态。
注意
一个Action的
handleEvent
,只要返回SUCCESS
,CONTINUE
,包括大部分错误(某些错误,比如FATAL_BUG
, 表示在此Action已经处于不应该再被调用handleEvent
的状态), 都代表这条消息被 accepted ;而返回
UNKNOWN_EVENT
则明确代表此消息没有被此Action accepted 。一个消息被 accepted ,并不代表一个消息被 consumed 。如果没有被 consumed , 代表此消息依然可以被其它Action处理。
当你的Action组合其它Action时,你对其它Action的假设,只需要符合上述外部行为规范即可。但对于我们将要实现的Action内部,我们也要进行
一些概念上的定义,以保证Action与Action之间组合时,尤其在进行错误处理时,可以相互协调,保证整个 Transaction
行为的正确性。
5.2. Action 内部状态¶
- I-IDLE:
Action已经被构造,但尚未调用
exec
之前。
- I-DONE:
Action已经结束其处理,无论成功还是失败。
如果一个Action在调用 exec
之后,直接返回 SUCCESS
或者任何错误,代表这个Action已经进入 I-DONE 状态。
如果一个Action在调用 exec
之后,直接返回 CONTINUE
,代表这个Action已经
进入 I-WORKING 或者 I-STOPPING 状态。
这一点与外部的观察并不一致,因为外部无法从 CONTINUE
返回值辨别出其内部处于二者中的哪一种。
无论是哪一种,从外部看,这个Action都还没有运行结束,因而需要进一步的消息激励。但从内部看,却有着本质的区别:
- I-WORKING:
状态却表示其处于正常处理状态;
- I-STOPPING:
则代表Action内部已经进入异常处理状态。
如果内部处于 I-WORKING 状态,如果一个Action未处于 免疫模式 ,
则 stop
调用应强迫Action进入失败处理。
5.3. 错误传播¶
5.3.1. 方式¶
注意
错误的传播,主要有三种方式:
最直接,也是最典型的,通过
返回值
。这发生于一个Action运行结束,进入 I-DONE 状态时; 这属于一个从内层上下文
向外层上下文
传播错误的方式。但一个Action内部发生错误后,并没有直接进入 I-DONE 状态,而是需要进一步的消息激励, 因而会处于 I-WORKING 或 I-STOPPING 状态。 但此错误需要立即为外界所感知,从而尽快对此错误作出响应。 此时,可以通过
运行时上下文
的嵌套父子关系,由内层上下文
直接逐级上报,向外传播
;外层上下文
由于任何原因,比如,最典型的原因是,通过内层Action的返回值,或者内层上下文
的上报,得到了一个错误, 需要将错误传递给其它内层上下文
。 此时,可以通过stop
调用,带着cause值,将错误由外向内
传播。
简单的说就是:
由内向外传播
内层Action的返回值(此时Action进入 I-DONE 状态)
内层上下文
向外层上下文
的直接传递(此时调用返回值是CONTINUE
,因而Action 处于 I-WORKING 或 I-STOPPING 状态 )
由外向内传播:
stop(cause)
5.3.2. 模式¶
每一个可嵌套Action都有4种模式:
- 正常模式:Normal Mode
错误既可以向内传播,也可以向外传播;
- 沙箱模式:Sandbox Mode
错误不可通过
运行时上下文
向外传播可能允许通过
返回值
返回最终的错误;允许外部的错误通过
stop
传播进来;
- 免疫模式:Immune Mode
错误不可向内传播
但允许内部的错误通过
运行时上下文
或者返回值
向外传播
5.3.3. stop的设计原则¶
注意
stop
(立即结束的情况) 或随后的 handleEvent
(经多次消息激励后的情况)的返回值原则如下:
如果
stop
并没有导致一个Action处理失败,即Action依然完成了它本来的职责, 则依然返回SUCCESS
;如果
stop
本身没有失败,但Action并没有完成它本来应该完成的任务,则返回stop cause
;如果
stop
导致了的其它失败,则返回其它错误(Loop 除外);
5.4. 部分 Action 行为定义¶
5.4.1. __asyn¶
注意
当一个 __asyn
处于 I-WORKING 状态,即其正在等待消息激励时,如果被调用 stop
:
如果用户实现有错误(返回
CONTINUE
却发现其并没有等待任何消息),直接返回USER_FATAL_BUG
。否则,返回
stop cause
。
5.4.2. __sequential¶
5.4.3. __concurrent¶
注意
当 __concurrent
处于 I-WORKING 状态,如果此时调用其 stop
:
stop
每一个处于 I-WORKING 状态的线程, 将cause
值继续往内层传递;如果所有的线程都最终以
SUCCESS
结束,则返回SUCCESS
;如果某个或某些线程返回任何错误,整个
__concurrent
结束时,返回最后一个错误。
注意
当 __concurrent
处于 I-WORKING 状态,此时某一个线程发生错误:
记录下此错误;
对其余任何还处于 I-WORKING 状态的线程,调用其
stop
,原因为刚刚发生的错误;如果某个线程最终返回
stop cause
,忽略此错误;在整个
stop
过程中,坚持使用同一个原因值;哪怕某些线程立即返回其它错误值;如果在整个
stop
过程中,有一个或多个直接返回其它错误值(非stop cause
), 等stop
调用完成后,将最后一个错误记录下来,更新原来的错误值;如果所有线程都在调用
stop
后立即结束,则直接返回最后一个错误值;进入 I-DONE 状态;如果仍然有一个或多个线程,其
stop
调用返回CONTINUE
,则__concurrent
应 直接给外层上下文通报最后一个错误,并返回CONTINUE
, 由此进入 孤岛模式 以及 I-STOPPING 状态。随后在
handleEvent
的过程中,返回的每一个错误,都即不向外扩散,也不向内扩散; 仅仅更新自己的last error;最终结束后,返回最后一个错误值。进入 I-DONE 状态。
5.4.4. __procedure¶
__procedure
分为两个部分:Normal Action,与 __finally
Action。
注意
Normal Action的执行如果处于 I-WORKING 状态,此时进行 stop
:
直接对Normal Action调用
stop
;
如果直接返回
SUCCESS
,则直接以成功状态,进入__finally
;如果直接返回错误,则直接以错误进入
__finally
;两种情况下,在
__finally
里读到的环境状态都是Normal Action结束时的返回值;
如果Normal Action返回
CONTINUE
,则__procedure
进入 孤岛模式 。随后Normal Action的
__handleEvent
如果返回SUCCESS
或错误,其处理方式与1所描述的情况相同;
5.4.5. protected __procedure¶
注意
一个处于 I-WORKING 状态的 protected __procedure
可以被 stop
,
其处理方式与 procedure stop 相同。
注意
protected __procedure
天然处于 沙箱模式 ,即,直到其运行结束之前,不会向外围运行时上下文通报任何错误。