一. 背景
视频业务作为B站内容生态的心脏,承载了海量的视频内容和用户互动。它不仅是用户获取信息、享受娱乐的窗口,更是UP主展示创意、分享知识的舞台。在设计和实现视频系统时,我们致力于平衡用户体验、内容分发的效率,同时确保平台的稳定性和可扩展性。
在这个过程中,稿件生产扮演着至关重要的角色。我们通过提供强大的视频上传、编辑和管理工具,满足创作者的需求,让他们能够轻松地制作和分享内容。同时,我们实施严格的内容审查和版权管理措施,以保障社区生态的健康发展。我们向创作者提供更好的服务,向B站内容生态供给更多的内容。
二.遇到的问题
-
随着B站业务迅速发展,平台规模及影响力较大,黑产、菠菜、冲塔用户、搬运号等问题逐步凸显出来,严重影响社区生态,对平台管控能力提出了挑战。
-
在审核&机审团队不断迭代审核能力的同时,我们发现由于稿件生产过程中,多个环节间存在并发场景,引发"条件竞争"问题,导致漏审风险。
三.软件开发中的并发问题
条件竞争 (Race Condition) 是指一个系统的运行结果依赖于不受控制的事件的先后顺序。当这些不受控制的事件并没有按照开发者想要的方式运行时,就可能会出现 bug。这个词最早被应用在电子系统中。
-
在逻辑电路中,模块可能会因为源信号的不同,或逻辑元件的传播延迟差距,产生不符合预期的输出。在计算机领域中,为了提升运行效率、有效利用资源,会采取多线程、多进程、协程、分布式系统等众多手段来同时执行更多任务,但这些形式都可以归为并发模型。
-
在执行复杂任务的时候,我们一般会将任务拆分成多个小任务去执行,比如我们想做番茄鸡蛋盖饭时,可以把任务拆分成切番茄、打鸡蛋、煮饭、炒番茄鸡蛋、装盘等多个环节;按顺序执行的话会花费很多时间,所以在生活中我们总是在煮饭的同时处理其他事情,这就是并发的应用。
-
在软件中,我们会把任务抽象成执行流,而并发则是描述同时执行多个执行流;那么并发为什么会导致条件竞争呢?让我们继续前面的例子,在我们做番茄鸡蛋盖饭的时候,炒番茄鸡蛋的用时,和煮饭的用时总会有细微的变化,有时是饭先煮好、有时是番茄鸡蛋先炒好;那么如果煮饭的和炒菜的不是同一个人,他们接到的指令都是饭/菜做好后直接装到盘子里,那么饭先煮好装盘,最后做好的是番茄鸡蛋盖饭;菜先炒好,菜品就变成了饭盖番茄鸡蛋了。
观察上面的例子,我们发现不确定的耗时、和引入了盘子共同导致了出品的不确定性,两个人都可以控制盘子、同时有人改变了盘子的状态(装入了菜品)。在计算机领域中,我们在小尺度下的耗时一定是不确定的,那么引入竞争的条件则是以下三点:1. 存在并发 2. 存在共享对象 3. 会变更共享对象的状态。
根据引入竞争的条件,我们可以很清晰地得出,要消除条件竞争,要么破坏并发条件,要么不使用共享对象。那是不是用了共享对象的,就不能并发了呢?显然不是,我们只要找到访问竞争对象的窗口,在竞争窗口(race windows)中保持同步处理,就可以规避掉条件竞争问题。
常规的方案通常是通过引入同步原语来规避,包括互斥锁、原子操作、串行化等方案。
四. 稿件场景下的安全性演讲
在审核场景中,我们遇到的是用户编辑和审核通过并发引起的条件竞争 (Race Condition) ,将问题简化一下,可以看作是下图场景。
前人通过提交前重新查询数据,比对mtime,判断数据发生变动时刷新页面,大幅降低了出现概率,这个方案可能是早期在改造成本和业务安全需求之间做出的妥协选择,但在今天已经无法满足平台对内容的安全性需求了。我们需要彻底消除因技术原因导致的漏审情况,这要求我们系统性地解决稿件生产过程中的所有条件竞争问题,确保内容审核的严密性和可靠性。
稿件生产流程经历了多次改造,但鉴于业务本身的高复杂性,简化后仍包含几十个子流程,横跨多个业务域,难以通过分析代码、观察日志的常规手段发现条件竞争问题。需要建设观测手段,辅助对这类复杂问题的发现及分析。
基于之前构建的《内容生产全链路业务观测体系》,进一步建设内容安全对账,通过对稿件生命周期分析,针对性监控漏审数据,并通过旁路手段快速止损;同时回捞历史漏审数据进行分析,将问题进行归纳分类,整体构思解决方案。
一般情况下,可以选择互斥锁、乐观锁、串行化管道来解决并发问题,将并发场景退化成串行场景。但软件开发没有”银弹“,我们需要根据改造成本、存储成本、用户体验等多方面要素来选择解决方案。
通过分析当下的case,稿件生产流程中虽然遍布竞争场景,但涉及安全性的可以只关注两部分:用户编辑会引入风险,稿件开放会漏出风险。由于当前所有稿件开放动作均为审核侧触发,我们仅需关注用户编辑与审核提交之间竞争。
用户编辑与审核提交均为同步接口触发,是需要实时反馈是否更新完成的,使用消息队列做串性化处理显然会劣化体验,只能考虑引入锁破坏竞争条件。为了用户及审核人员的体验,锁的触发概率和影响时长都要尽可能的小,同时用户编辑的可用性要求会高于审核人员。
上图是我们的审核流程,涉及多个审核场景。在该场景下,用户编辑与用户编辑、用户编辑与审核提交、多场景审核两两之间都会存在条件竞争,我们分开考虑。
审核由审核平台保障不会有数据同时被多审核场景提交,所以暂不考虑该场景。
用户编辑与用户编辑之间并发一般是非常规场景,针对该场景我们加入了编辑限频,同时规避并发问题和减少黑灰产编辑,一举两得。
审核环节和用户提交的竞争比较复杂。在审核环节中,从送审读取稿件数据,到审核提交数据,常常会有数十分钟的时间,显然这段时间不让用户编辑是不合理的。因此我们采用CAS方式降低报错概率。
稿件编辑、更新状态的时候,会同时更新数据版本字段;在更新时会指定更新的版本,如果指定的版本低于当前DB中版本,更新失败。在审核提交因更新的版本低于当前版本报错时,会自动触发重新送审流程,让审核同学审一遍最新版本的稿件。
引入更新CAS和限频锁后,我们的旁路观测系统从每周漏审x条成功清零。虽然方案不算很优雅,但很好的解决了我们当下的问题。
五.未来展望
1.稿件生产流程并发竞争处理
当前阶段只解决了眼下最紧急的安全性上的并发竞争问题,但稿件生产过程中处处存在竞争问题,依然会出现卡流程、更新错误、计算异常等问题,亟需系统性治理。因此要将整个生产流程分为实时、近实时两部分,实时场景做好乐观锁版本控制,近实时消息队列部分做好串型化,保障整个稿件生产流程中数据产出符合预期。
2.稿件版本追溯&版本合并
为了审核提交CAS,本次在稿件引入了版本号概念,但当前版本号只是一个数字没有深入挖掘,后续可以围绕版本号做更深一步的处理,建设版本更新记录,使审核、技术人员可以对自己上一次变更做负向操作,减少误操作造成的损失。
同时在版本提交冲突时,可以做简易的merge操作,比如提交版本和当前版本中的变更不存在需要审核的要素,可以直接应用审核结果,减轻审核同学重审工作量。
3.稿件域变更字段管控
当下稿件系统中更新数据滥用严重,很多场景明明没有修改某字段,也用上次读到的对应数据进行覆盖写操作,引入了很多数据冲突风险。需要建设变更权限能力,严格管控各场景对数据的更新范围。比如标签服务只能变更标签,不能更改标题或状态。这样能细化共享数据粒度,降低数据冲突风险,同时也能减少相关业务方误操作或跨业务域操作的出现。
-End-
作者丨Prolic