作为业界少有的EB级别数据分布式平台,MaxCompute系统每天支撑上千万个分布式功课的运转。在这个量级的功课数目上,毫无疑问平台需要支撑的功课特点也多种多样:既有在”阿里体量”的大数据生态中独有的包含数十万算计节点的超大型功课,也有中小规模的分布式功课。同时不同用户对于不同规模/特点的功课,在运转时间,资材使用效率,数据吞吐率等方面,也有着不同的期待。
Fig.1 MaxCompute线上数据分析
基于功课的不同规模,当前MaxCompute平台提供了两种不同的运转形式,下表对于这两种形式做了总结对比:
Fig.2 离线(batch)形式 vs 一体化调理准及时(smode)形式
从上图可以看到,离线功课和一体化调理的准及时功课,在调理方式,数据传输,使用资材来源等多个方面,都有非常显著的区别。可以说,这两种运转方式分别代表了在海量数据场景上按需请求资材来优化吞吐量和资材利用率,以及在处理中等(少量)数据时通过算计节点的全量预拉起来(以及数据直传等手段加速)降低施行时延的两个极端。而这些区别,最终会通过施行时间和功课资材利用率等方面体现出来。很显然,以高Throughput为主要优化目标的离线形式,和以追求低Latency的准及时形式,在各方面的功能指标会有很大的区别。比如以1TB-TPCH标准benchmark为例,此报告施行时间(功能)和资材消耗两个维度来做比较。可以看到,准及时的(SMODE)在功能上有着非常明显的优势(2.3X),但是这样的功能提升也并不是没有代价的。在TPCH这个特定的场景上,一体化施行的SMODE形式,在获取了2.3X功能提升的同时,也消耗了3.2X的系统资材(cpu * time)。
Fig.3 功能/资材消耗比较:离线(batch)形式 vs 一体化调理准及时(smode)形式
这个观察结论其实并不意外,或者从某种程度上是by design的。拿下图一个典型SQL产生的DAG来看,所有算计节点都在功课提交伊始就被拉起,虽然这样的调理方式允许数据得以(在需要的时候)pipeline起来,从而可能加速数据的处理。但并不是所有的施行计划里的所有上下游算计节点都可以有理想化的pipelined dataflow。事实上对于许多功课而言,除了DAG的根节点(下图中的M节点)以外,下游的算计节点在某种程度上都存在着一定程度的浪费。
Fig.4 一体化调理准及时(smode)形式下,可能的资材使用低效
这种空转造成的资材使用的低效,在数据的处理流程上存在barrier算子而无法pipeline,以及在DAG图比较深的情况下会尤为明显。当然对于希望极致优化功课运转时间的场景而言,通过更多的资材消耗,来获取极致的功能优化,在一些场景上是有其合理性的。 事实上,在一些business-critical的在线服务系统中,为了保证服务总是能迅速响应并处理峰值数据,平均个位数的CPU利用率也并非少见。但是对于算计平台这种量级的分布式系统,能否在极致功能以及高效的资材利用率之间,获取一个更好的平衡呢?
答案是肯定的。这就是我们在这里要介绍的混合算计形式:Bubble Execution
1. Bubble Execution 概述
DAG框架的核心架构思想,在于对施行计划的逻辑层与物理层的清晰分层设计。物理施行图是通过对逻辑图中的节点、边等的物理特性(如数据传输介质,调理时机,资材特性等)的物化来实现的。对比在Fig.2中描述的batch形式和smode形式,DAG提供了在一套灵活的调理施行框架之上,统一离线形式和准及时一体化施行形式的实现。如同下图所示,通过调整算计节点和数据连接边的不同物理特性,不仅能对现有的两种算计形式做清晰的表述,在对其进行更通用化的扩展后,还可以探索一种全新的混合运转形式,也就是Bubble Execution。
Fig.5 DAG框架上的多种算计形式
直观上来理解,如果我们把一个Bubble当作一个大的调理单位,Bubble里面的资材一起请求运转,并且里面上下游节点的数据均通过网络/内存直连传输。与之相对的,Bubbles之间连接边上的数据传输,则通过落盘方式来传输。那么离线和准及时功课施行,其实可以认为是Bubble施行的两个极端场景:离线形式可以认为是每个stage都单独作为single-bubble的特例,而准及时框架则是将功课所有算计节点都规划到一个大Bubble里面,来做一体化调理施行的另一个极端。DAG AM已经将两种算计形式统一到一套调理施行infra之上。使得在两种形式上进行优点互补成为可能,为引入Bubble Execution奠定了基础。
Bubble Execution通过灵活自适应的子图(Bubble)切割,在现有的两个极端之间,提供了一种选取更细粒度,更通用的调理施行方法,达到功课功能和资材利用率之间获取优化的tradeoff的方法。在根据输入数据量、算子特性、功课规模等信息进行分析后,DAG的Bubble施行形式可以将一个离线功课切分出多个Bubbles,在Bubble里面充分利用网络/内存直连和算计节点预热等方式提升功能。这种切分方式下,一个DAG运转图中的算计节点,可以都被切入某个Bubble,根据所在DAG中的位置被切入不同Bubbles,还可以完全不被切入任何Bubble(依然以传统离线功课形式运转)。这种高度灵活的混合运转形式,使整个功课的运转能更加灵活的自适应线上多种多样功课的特点,在实际生产中具有重要的意义:
Bubble形式使更多功课的加速成为可能:一体化调理的准及时功课具有基于完全规模(线上默认2000)的”一刀切”式的准入条件。这一方面是出于有限资材的公平使用,另一方面也是为了控制节点failure带来的cost。但对于中大型功课,虽然完全规模可能超过准入门限,但是其里面的不同子图,有可能是规模合适,且可以通过数据pipeline等方法来加速的。此外线上部分算计节点由于其本身的特性(比如包含UDF等用户逻辑需要安全沙箱),无法使用预热的准及时资材池施行,而当前非黑即白的形式,会使得一个功课中,只要包含一个这种算计节点,整个功课都无法使用加速形式施行。Bubble形式能较好的解决这些问题。Bubble形式将enable线上两个资材池的打通:当前离线资材(cold)和准及时资材池(warm)作为两种特性不同的线上资材,完全隔离,各自管理。这种分离的现状,可能导致资材的浪费。比如对于大规模功课,因为完全无法利用准及时资材池,排队等待离线资材,而同时准及时资材池可能正处于空闲状态,反之亦然。Bubble形式能通过在功课里面拉通不同资材的混合使用,使得两者各自补充,削峰填谷。Bubble形式可以完全上提高资材的利用率:从资材利用的角度来看,对于可以满足准及时形式准入的中型功课,由于准及时形式一体式调理拉起的运转形式,虽然运转速度能有所提升,但客观上会造成一定程度资材的空转与浪费(尤其是DAG图较深以及算计逻辑有barrier时)。这种情况下,按照节点数目,算计barrier等条件,将一体化形式拆解成多个Bubble。这能够有效的减少节点大量的空转消耗,而且在拆分条件合理的情况下,功能方面的损失也可以做到较低。Bubble形式能有效降低单个算计节点failure带来的代价:一体化的准及时形式施行,由于其数据pipeline的特性,功课的容错粒度和其调理粒度是紧密挂钩的:都是all-in-one。也就是说,只要有一个节点运转失败,整个功课都要重新运转。因为功课规模越大,运转过程中可能有节点失败的概率也就越大,这样的failover粒度无疑也限制了其能支持的最大功课规模。而Bubble形式则提供了一个更好的平衡点:单个算计节点的失败,最多只影响同处于一个Bubble的节点。此外Bubble形式对于各种failover做了细粒度的各种处理,我们将在下文描述。
我们可以通过标准的TPCH-1TB测试benchmark来直观评测Bubble施行形式的效果。在上层算计引擎(MaxCompute优化器以及runtime等)保持不变,并且Bubble的大小维持在500(具体Bubble切分规则下文介绍)时,做一下Bubble施行形式与标准离线形式,以及准及时形式,在功能(Latency) 以及资材消耗(cpu * time)两个方面的比较:
Fig.6.a 功能(Latency)比较:Bubble形式 vs 离线(batch)形式 vs 一体化调理准及时(smode)形式
从运转时间来看,Bubble形式显然要远优于离线形式(完全2X的功能提升),而较准及时的一体化调理形式而言,Bubble的施行功能也并没有太明显的下降。当然在一些数据可以非常有效利用pipeline处理的query(比如Q5, Q8等),准及时功课还是有一定的优势。但SMODE功课在施行时间上的优势并不是没有代价的,如果同时考虑资材消耗,在下图中,我们可以看到,准及时功课的功能提升是建立在资材消耗远远大于Bubble形式的前提之上的。而Bubble在功能远优于离线形式的同时,其资材消耗,则完全上是相近的。
Fig.6.b 资材消耗(cpu * time)比较:
Bubble形式 vs 离线(batch)形式 vs 一体化调理准及时(smode)形式
综合起来看,Bubble Execution可以很好的结合batch形式和准及时形式的优点:
在施行时间层面,对于TPCH测试集中的任意query,bubble形式的施行时间都比batch形式要短,完全上22个Queries总耗时缩减将近2X,接近service mode形式的耗时;在资材消耗层面,bubble形式基本上和batch形式相当,相比于service mode形式有大幅度的减少,完全缩减2.6X。
Fig.6.c Bubble形式与离线/准及时形式的完全比较
值得说明的是,在上面的TPCH Benchmark比较中,我们把Bubble切分条件简单化了,也就是完全上之限制bubble的大小在500,而没有充分考虑barrier等条件,如果在切分bubble的时候进一步调优,比如对于数据可以有效pipeline起来的节点,尽量保证切分在bubble里面,那功课的施行功能和资材利用率等方面都还可以进一步得到的提升,这是我们在实际生产系统上线过程中会注重考虑的。具体上线的效果见Section 3。
在了解了Bubble施行形式的完全设计思想与架构后,接下来展开来讲一下具体Bubble形式的实现细节,以及将这种全新的混合施行形式推上线所需要的具体工作。
2. Bubble的切分与施行
采用Bubble Execution的功课(以下简称Bubble功课)和传统的离线功课一样,会通过一个DAG master(aka. Application Master)来管理整个DAG的生命周期。AM负责对DAG进行合理的bubble切分,以及对应的资材请求和调理运转。完全而言,Bubble里面的算计节点,将按照算计加速度原则,包括同时使用预拉起的算计节点以及数据传输通过内存/网络直传进行pipeline加速。而不切在bubble里面的算计节点则通过经典离线形式施行,不在bubble里面的连接边(包括横跨bubble boundary的边)上的数据,均通过落盘方式进行传输。
Fig.7 混合Bubble施行形式
Bubble切分方法,决定了功课的施行时间和资材利用率。需要根据算计节点的并发规模,节点里面算子属性等信息综合考虑。而在切分出bubble之后,Bubble的施行则涉及到节点的施行,与数据pipeline/barrier的shuffle方式怎么做到有机的结合,这里分开做一下描述。
2.1 Bubble 切分原理
Bubble Execution的核心思想在于将一个离线功课拆分成多个Bubble来施行。为了切分出有利于功课完全高效运转的bubble,有几个因素需要综合考虑:
算计节点里面算子特性:对于同时拉起bubble所有算计节点的调理形式而言,数据在bubble里面的上下游节点之间能否有效的进行pipeline处理,很大程度上决定了在bubble里面,下游节点是否会因处于空转状态带来资材浪费。所以在切分bubble的逻辑中,当节点包含barrier特性的算子而可能阻塞数据的pipeline时,将考虑不将该节点与其下游切入同一个bubble。单个Bubble里面算计节点数目的多少:如同之前讨论的,一体化的资材请求/运转,当包含的算计节点过多时,可能无法请求到资材,或者即使能请求到其failure代价也可能无法控制。限定Bubble的大小,可以避免过大的一体化运转带来的负面作用。聚合算计节点,切割Bubble的迭代方向:考虑到bubble大小的限制,从上而下切分bubble与从下而上切分bubble两种方式,可能导致切分的结果的不同。对于线上大部分功课而言,处理的数据往往呈倒三角型,对应的DAG也大多数是倒三角形态,所以默认采用自底向上的算法来切割bubble,也就是从距离root vertex最远的节点开始迭代。
在上述的几个因素中,算子的barrier属性由上层算计引擎(e.g., MaxCompute的optimizer)给出。一般而言,依赖global sort操作的算子(比如MergeJoin, SorteAggregate等),会被认为会造成数据阻塞(barrier),而基于hash特性操作的算子则对于pipeline更加友好。对于单个Bubble里面允许的算计节点数目,根据我们对线上准及时功课特点的分析和Bubble功课的实际灰度实验,选定的默认上限在500。这是一个在大多数场景下比较合理的值,既能保证比较快速的拿到全量资材,同时由于处理数据量和DoP基本成正相关关系,这个规模的bubble一般也不会出现内存超限的问题。当然这些参数和配置,均允许功课级别通过配置进行微调,同时Bubble施行框架也会后继提供功课运转期间动态及时调整的能力。
在DAG的体系中,边连接的物理属性之一,就是边连接的上下游节点,是否有运转上的前后依赖关系。对于传统的离线形式,上下游先后运转,对应的是sequential的属性,我们称之为sequential edge。而对于bubble里面的上下游节点,是同时调理同时运转的,我们称连接这样的上下游节点的边,为concurrent edge。可以注意到,这种concurrent/sequential的物理属性,在bubble应用场景上,实际与数据的传送方式(网络/内存直传 vs 数据落盘)的物理属性是重合的(Note: 但这两种依然是分开的物理属性,比如在必要的时候concurrent edge上也可以通过数据落盘方式传送数据)。
基于这样的分层抽象,Bubble切分算法,本质上就是尝试聚合DAG图的节点,将不满足bubble准入条件的concurrent edge还原成sequential edge的过程。最终,由concurrent edge联通的子图即为bubble。在这里我们通过一个实际的例子来展示Bubble切分算法的工作原理。假设存在下图所示的DAG图,图中的圆圈表示算计顶点(vertex),每个圆圈中的数字表示该vertex对应的实际算计节点并发度。其中V1和V3因为在功课提交初始,就因为其里面包含barrier算子,而被标注成barrier vertex。圆圈之间的连接线表示上下游的连接边(edge)。橙色线代表(初始)concurrent edge,黑色线代表sequential edge,初始状态图中的sequential edge根据barrier vertex的输出边均为sequential edge的原则确定,其他边默认均初始化为concurrent edge。
Fig.8 示例DAG图(初始状态)
在这个初始DAG基础上,按照上面介绍过的完全原则,以及本章节最后描述的一些实现细节,上图描述的初始状态,可以经过多轮算法迭代,最终产生如下的Bubble切分结果。在这个结果中产生了两个Bubbles: Bubble#0 [V2, V4, V7, V8],Bubble#1 [V6, V10], 而其他的节点则被判断将使用离线形式运转。
Fig.9 示例DAG图Bubble切分结果
在上图的切分过程中,自底向上的遍历vertex,并秉承如下原则:
若当前vertex不能加入bubble,将其输入edge均还原为sequential edge(比如DAG图中的V9);
若当前vertex能够加入bubble,施行广度优先遍历算法聚合生成bubble,先检索输入edge连接的vertex,再检索输出edge连接的,对于不能联通的vertex,将edge还原为sequential edge(比如DAG图中遍历V2的输出vertex V5时会因为total task count超过500触发edge还原)。
而对任意一个vertex,只有当满足以下条件才能被添加到bubble中:
vertex和当前bubble之间不存在sequential edge连接;vertex和当前bubble不存在循环依赖,即:Case#1:该vertex的所有下游vertex中不存在某个vertex是当前bubble的下游;Case#2:该vertex的所有下游vertex中不存在某个vertex是当前bubble的下游;Case#3:该vertex的所有下游bubble中不存在某个vertex是当前bubble的下游;Case#4:该vertex的所有下游bubble中不存在某个vertex是当前bubble的下游;
注:这里的下游/下游不仅仅代表当前vertex的直接后继/前驱,也包含间接后继/前驱
Fig.10 切分Bubble过程可能存在循环依赖的几种场景
而实际线上bubble的切分还会考虑到实际资材和预期运转时间等信息,比如算计节点的plan memory 是否超过一定数值,算计节点中是否包含UDF算子,生产功课中算计节点基于历史信息(HBO)的预估施行时间是否超长,等等,这里不再赘述。
2.2 Bubble的调理与施行
2.2.1 Bubble调理
为了实现算计的加速,Bubble里面的算计节点的来源默认均来自常驻的预热资材池,这一点与准及时施行框架相同。与此同时我们提供了灵活的可插拔性,在必要的情况下,允许Bubble算计节点从Resource Manager当场请求(可通过配置切换)。
从调理时机上来看,一个Bubble里面的节点调理策略与其对应的输入边特性相关,可以分成下面几种情况:
不存在任何input edge的bubble root vertext(比如 Fig.9中的V2):功课一运转就被调理拉起。只有sequential edge输入bubble root vertex(比如 Fig.9中的V6):等待下游节点完成度达到配置的min fraction比例(默认为100%,即所有下游节点完成)才被调理。Bubble里面的vertex(即所有输入边都是concurrent edge,比如 Fig.9中的V4, V8, V10),因为其完全是通过concurrent edge进行连接的,会自然的被与下游同时触发调理。Bubble边界上存在mixed-inputs的bubble root vertex(比如 Fig.9中的V7)。这种情况需要一些特殊处理,虽然V7与V4是通过concurrent edge链接,但是由于V7的调理同时被V3通过sequential edge控制,所以事实上需要等待V3完成min-fraction后才能调理V7。对于这种场景,可以将V3的min-fraction配置为较小(甚至0)来提前触发;此外Bubble里面我们也提供了progressive调理的能力,对这种场景也会有帮助。
比如图7中的Bubble#1,只有一条SequentialEdge外部依赖边,当V2完成后,就会触发V6 + V10(通过concurrent edge)的完全调理,从而将整个Bubble#1运转起来。
在Bubble被触发调理后,会直接向SMODE Admin请求资材,默认使用的是一体化Gang-Scheduling(GS)的资材请求形式,在这种形式下,整个Bubble会构建一个request,发送给Admin。当Admin有足够的资材来满足这个请求时,会将,再包含预拉起worker信息的调理结果发送给bubble功课的AM。
Fig.11 Bubble与Admin之间的资材交互
为了同时支持紧张资材上以及Bubble里面动态调整的场景,Bubble同时还支持Progressive的资材请求形式。这种形式允许Bubble内的每个Vertex独立请求资材和调理。对于这种请求,Admin只要有增量的资材调理即会将结果发送给AM,直到对应Vertex的request完全满足。对于这种场景上的独特应用这里暂时不做展开。
在准及时施行框架升级后,SMODE服务中的资材管理(Admin)和多DAG功课管理逻辑(MultiJobManager)已经解耦,因此bubble形式中的资材请求逻辑,只需要和Admin进行交互,而不会对于正常准及时功课的DAG施行管理逻辑带来任何影响。另外,为了支持线上灰度热升级能力,Admin管理的资材池中的每个常驻算计节点均通过Agent+多Labor形式运转,在调理具体资材时,还会根据AM版本,进行worker版本的匹配,并调理满足条件的labor给Bubble功课。
2.2.2 Bubble数据Shuffle
对于穿越Bubble bourndary上的sequential edge,其上传输的数据和普通离线功课相同,都是通过落盘的方式来进行数据传输。这里我们主要讨论在Bubble里面的数据传输方式。根据之前描述的功课bubble切分原则,bubble里面的通常具备充分的数据pipeline特性,且数据量不大。因此对于bubble里面concurrent edge上的数据,均采用施行速度最快的网络/内存直传方式来进行shuffle。
这其中网络shuffle的方式和经典的准及时功课相同,通过下游节点和下游节点之间建立TCP链接,进行网络直连发送数据。这种push-based的网络传送数据方式,要求上下游必须同时拉起,根据链式的依赖传递,这种网络push形式强依赖于Gang-Scheduling,此外在容错,长尾规避等问题上也限制了bubble的灵活性。
为了更好的解决以上问题,在Bubble形式上,探索了内存shuffle形式。在这一形式下,下游节点将数据直接写到集群ShuffleAgent(SA)的内存中,而下游节点则从SA中读取数据。内存shuffle形式的容错,扩展,包括在内存不够的时候将部分数据异步落盘保证更高的可用性等能力,由ShuffleService独立提供。这种形式可以同时支持Gang-Scheduling/Progressive两种调理形式,也使其具备了较强的可扩展性,比如可以通过SA Locality调理实现更多的Local数据读取,通过基于血缘的instance level retry实现粒度更精细的容错机制等等。
Fig.12 Network Shuffle VS Memory Shuffle
鉴于内存shuffle提供的诸多可扩展优势,这也是线上Bubble功课选用的默认shuffle方式,而网络直传则作为备选方案,允许在容错代价很小的超小规模功课上,通过配置使用。
2.3 Fault-Tolerance
作为一种全新的混合施行形式,Bubble施行探索了在离线功课和一体化调理的准及时功课间的各种细粒度平衡。在线上复杂的集群中,运转过程中各种各样的失败在所难免。而bubble这种全新形式下,为了保证失败的影响最小,并在可靠性和功课功能之间取得最佳的平衡,其对于失败处理的策略也更加的多样化。
针对不同的异常问题,我们设计了各种针对性容错策略,通过各种从细到粗的力度,处理施行过程中可能涉及的各种异常场景处理,比如:向admin请求资材失败、bubble中的task施行失败(bubble-rerun)、bubble多次施行失败的回退(bubble-renew),施行过程中AM发生failover等等。
2.3.1 Bubble Rerun
目前Bubble在里面算计节点失败时,默认采用的retry策略是rerun bubble。即当bubble内的某个节点的本次施行(attempt)失败,会立即rerun整个bubble,取消正在施行的同一版本的attempt。在归还资材的同时,触发bubble重新施行。通过这种方式,保证bubble内所有算计节点对应的(retry) attempt版本一致。
触发bubble rerun的场景有很多,比较常见的有以下几种:
Instance Failed:算计节点施行失败,通常由上层引擎的runtime错误触发(比如抛出retryable-exception)。Resource Revoked:在线上生产环境,有很多种场景会导致资材节点重启。比如所在的机器整机oom、机器被加黑等。在worker被杀之后,重启之后的worker会依照最初的启动参数重新连回admin。此时,admin会将这个worker重启的消息封装成Resource Revoked发送给对应的AM,触发bubble rerun。Admin Failover: 由于Bubble功课所使用的算计资材来自于SMODE的admin资材池,当admin由于某些原因Failover,或者SMODE完全服务被重启时,分配给AM的算计节点会被停止。Admin在Failover之后不感知当前各个节点被分配的AM信息,无法将这些重启的消息发送给AM。目前的处理方法是,每个AM订阅了admin对应的nuwa,在admin重启之后会更新这个文件. AM感知到信息更新后,会触发对应的taskAttempt Failed,从而rerun bubble。Input Read Error:在算计节点施行时,读不到下游数据是一个很常见的错误,对于bubble来说,这个错误实际上有三种不同的类型:Bubble内的InputReadError:由于shuffle数据源也在bubble内,在rerun bubble时,对应下游task也会重跑。不需要再做针对性的处理。Bubble边界处的InputReadError: shuffle数据源是下游离线vertex(或也可能是另一个bubble)中的task产生,InputReadError会触发下游的task重跑,当前bubble rerun之后会被delay住,直到下游血缘(lineage)的新版本数据全部ready之后再触发调理。Bubble下游的InputReadError: 如果bubble下游的task出现了InputReadError,这个事件会触发bubble内的某个task重跑,此时由于该task依赖的内存shuffle数据已经被释放,会触发整个bubble rerun。
2.3.2 Bubble Renew
在Admin资材紧张时, Bubble从Admin的资材请求可能等因为等待而超时。在一些异常情况下,比如bubble请求资材时刚好onlinejob服务处于重启的间隔,也会出现请求资材失败的情况。在这种情况下,bubble内所有vertex都将回退成纯离线vertex状态施行。此外对于rerun次数超过上限的bubble,也会触发bubble renew。在bubble renew发生后,其里面所有边都还原成sequential edge,并在所有vertex重新初始化之后,通过回放里面所有调理状态机触发事件,重新以纯离线的方式触发这些vertex的里面状态转换。确保当前bubble内的所有vertex在回退后,均会以经典离线的形式施行,从而有效的保障了功课能够正常terminated。
Fig. 13 Bubble Renew
2.3.3 Bubble AM Failover
对于正常的离线功课,在DAG框架中,每个算计节点相关的里面调理事件都会被持久化存储,方便做算计节点级别的增量failover。但是对于bubble功课来说,如果在bubble施行过程发生了AM failover重启,通过存储事件的replay来恢复出的bubble,有可能恢复到running的中间状态。然而由于里面shuffle数据可能存储在内存而丢失,恢复成中间running状态的bubble内未完成的算计节点,会因读取不到下游shuffle数据而立刻失败。
这本质上是因为在Gang-Scheduled Bubble的场景上,bubble完全是作为failover的最小粒度存在的,所以一旦发生AM的failover,恢复粒度也应该在bubble这个层面上。所以对于bubble相关的所有调理事件,在运转中都会被当作一个完全,同时当bubble开始和结束的时候分别刷出bubbleStartedEvent和bubbleFInishedEvent。一个bubble所有相关的events在failover后恢复时会被作为一个完全,只有结尾的bubbleFInishedEvent才表示这个bubble可以被认为完全结束,否则将重跑整个bubble。
比如在下图这个例子中,DAG中包含两个Bubble(Bubble#0: {V1, V2}, Bubble#1: {V3, V4}),在发生AM重启时,Bubble#0已经TERMINATED,并且写出BubbleFinishedEvent。而Bubble#1中的V3也已经Terminated,但是V4处于Running状态,整个Bubble #1并没有到达终态。AM recover之后,V1,V2会恢复为Terminated状态,而Bubble#1会重头开始施行。
Fig 14. AM Failover with Bubbles
3. 上线效果
当前Bubble形式已经在公共云全量上线,SQL功课中34%施行Bubble,日均施行包含176K个Bubble。
我们针对signature相同的query在bubble execution关闭和打开时进行对比,我们发现在完全的资材消耗基本不变的基础上,功课的施行功能提升了34%,每秒处理的数据量提升了54%。
Fig 15. 施行功能/资材消耗对比
除了完全的对比之外,我们针对VIP用户也进行了针对性的分析,用户Project在打开了Bubble开关之后(下图中红色标记的点为打开Bubble的时间点),功课的平均施行功能有非常明显的提升。
Fig 16. VIP用户开启Bubble后平均施行时间对比