互联网规模金融交易的自动化审计框架(译文-来自:Uber)
介绍与大多数向客户收取服务费用的公司一样,优步需要记录其与乘客、食客、收入者和其他客户的财务互动。我们建立的系统确保每一次内部和外部资金流动(付款、促销赠款/赎回或到期、信贷、通行费、税收)都得到完整说明。这是一个复杂的合规性计算活动,在每天数百万次旅行的互联网规模上进行。我们已经写过这方面的文章,您可以阅读这篇博客来深入了解Uber的金融计算平台。
作为一个财务记录系统,它需要满足定期进行的外部审计的严格要求。财务审计是一个定期(每个月或每个季度)发生的过程,以确保财务交易的完整性、准确性和可追溯性。这验证了向监管机构和街道发布的财务报告的准确性。
动机例如,让我们尝试了解审计与旅行相关的历史交易的挑战。
图1:优步交易的工作流程
让我们假设一名乘客在2022年1月从家到机场旅行,费用为60美元。6-7个月后,乘客再次从家中前往机场,但现在需支付50美元。在这两次行程中,乘客都使用了相同起点和终点的UberX。现在,用户担心收费不符,提出了争议。
现在可能有很多不同的东西:
由于燃油价格或其他一些因素,同一服务(例如UberX)的定价可能会发生变化。可能会针对特定服务取消或引入新的州税。国家可能在一段时间内补贴/取消了一些通行费。可能会因下雨或其他因素造成拥堵,导致行程时间延长。此外,由于维修工作,一些道路可能会被封锁。在您的旅行中可能会应用全市或个人促销活动。除了提到的因素之外,还可能存在其他业务逻辑更改。现在考虑对数百万次旅行(在过去的一个季度、一年或几年内)执行此操作。
从软件工程的角度来看,对于给定的输入(源事件)和输出(生成的内部交易),我们可以认为它有足够的日志来调试给定执行实例到底发生了什么(以及为什么),包括但不包括仅限于源和执行逻辑日志。
这里需要解决的两个主要挑战是金融交易的可再现性和可追溯性。
再现性我们遵循零证明的复式记账系统(符合GAAP标准),因此每次变动都会进行相应的分录;借贷。
鉴于当时的事实(例如,路线、定价、促销、会计规则和流量),我们应该能够生成完全相同的财务条目。这种将事实转化为目标条目或报告的能力称为可再现性。
可追溯性鉴于一份报告或日记账分录,审计员可能想知道我们为什么要预订它们,或者可能想知道哪些旅行或活动导致了这些日记账分录。从报告或报告中的分录,将其映射回用于生成特定日记账分录的行程/订单、会计规则和事实的能力称为可追溯性。
跟踪哪些交易受到给定错误或错误会计规则影响的能力有助于影响分析以及这些交易的更正。如果没有可追溯性,在数百万笔交易中找到受影响的交易集就像大海捞针。
外部审计流程
图2:在Tesseract审计框架之前遵循的高级手动审计流程
审计师希望确保会计账簿正确并符合现行法律法规。鉴于他们的时间有限,他们无法真正核实每笔交易的账目。因此,作为审计抽样的一部分,他们从不同地区和业务线(例如,UberX、Pool、Eats)中挑选了数百次不同的行程,并为样本手动生成财务条目。根据预期日记帐分录和已预订日记帐分录的比较,可以推断出总差异。因此,交易的可再现性和可追溯性非常重要。
举个例子,从几次随机旅行中,如果发现预期会计和报告会计之间的差异为1美分/旅行,则推断一个季度将是1500万美元。公司可能需要为这样的差异支付巨额罚款。
高度或频繁的差异以及未能按时完成审计将破坏信任,并且还会产生法律影响。
事件和业务规则如何随时间变化?以下是一些关键的设计注意事项:
逻辑变化:你们中有些人可能想知道:为什么这么复杂?它不像前面示例中提到的为每个行程获取事实,对所有行程重复相同的过程,并在其上执行规则那么简单。这是因为业务规则和流程也会随着时间而变化,需要考虑在内。
优步在70多个国家开展业务,每个国家都有自己的规定。修改和更新并不少见。此外,随着我 们扩展和增强我们的业务模型,我们需要进行业务更改并更新业务流程。因此,您可能需要根据发生的时间(或更准确地说,我们的系统处理时间)对不同的行程使用不同的逻辑。具体来说,我们应该能够将每笔交易映射回事实、特定业务规则和使用的流程。
环境变化:现在基于对可重复性的需要,人们可能会得出结论,对于逻辑或代码中的给定更改,可能需要考虑很多规则,但它们仍然是有限的。如果我们可以假设每个更改都有一个服务二进制文件,根据需要,我们可以通过适当的服务二进制文件处理所有事件,并重现完全相同的条目。不幸的是,这还不是全部。服务通过设计与不同的系统交互,如数据存储、队列、缓存以及其他服务。外围系统和技术的这种不断变化的环境使得几乎不可能始终如一地重现允许您运行相同二进制文件并获得相同结果的确切条件。因此,除了为每个代码/规则更改维护二进制文件之外,
规模:我们需要确保该解决方案可以扩展到互联网规模的运营。
成本:在设计系统时,我们需要牢记手动工作以及随之而来的存储和维护成本。
执行时间处理时间:审计操作的总成本取决于执行时间。此外,根据上市公司的规定,财务报告是一个时间紧迫且有时间限制的过程。
鸟瞰图来自多个源的事件通过消息队列(在我们的例子中是ApacheKafka®)到达事件处理器。根据事件的类型和涉及的实体,创建执行计划。
执行计划会安排需要执行的规则以及执行的顺序。该计划可以在执行期间根据附加参数进行扩展,因此本质上是动态的。
每个步骤都在规则执行器中执行,规则执行器从规则存储库中执行适当的规则。每天都会为财务交易捕获审计事件。
图3:事件的高级流程图
规则执行示例这里,让我们举个例子来更好地理解它。
图4:机场费用的示例审计事件(左)和规则执行的示例伪代码(右)
这里的流程函数接受一个输入并对其运行一系列规则集以生成最终的日记帐分录。Ruleset1和ruleset2是通过规则执行器执行的不同规则集。每个步骤或对规则执行器的每次调用都会提供一个特定的结果。它查询规则存储库,并从一系列规则中,根据输入参数执行适当的规则。每个步骤的输出要么是最终会计日记帐分录的一部分,要么由后续步骤/规则集使用。
例如,此处的规则集1可能是负责确定要使用的实体的步骤(例如,UberIndia被视为与UberUS不同的实体)。该实体可能取决于使用的城市或产品等字段。并且可能有其他步骤依赖于规则执行的实体。因此,这里的规则执行器通过规则集运行输入,选择适当的规则并返回一个实体后规则评估。
从这里可以看出,流程和步骤会根据上一步的结果发生变化。
现在,如果我们将执行步骤视为节点,我们会观察到整个执行计划只不过是一个有向无环图(DAG)。请注意,这不是依赖关系图——它只是显示规则以特定顺序执行。
图5:规则执行的有向无环图
此处,在执行规则集1时,根据输入参数选择了r1规则。同样,r2表示运行ruleset2时选择的规则。
此处的图表表明,r1先执行,然后执行r2,并且根据r2的结果,对于不同的条目,执行了r31(a)、r32、r33和r31(b)。请注意,根据输入,为同一规则集选择了不同的规则r31(a)和r31(b)。
经过一系列这样的步骤之后,每个分支末尾的最终结果将是一个会计分录。
因此,如果我们可以为每个交易存储这个DAG以及在每个节点执行的特定规则,我们就不再需要代码来实现可再现性和可追溯性。我们可以重构或添加或删除代码中的新方法/条件,它会自动得到处理,因为我们正在维护哪些规则正在执行以及以什么顺序执行的图表。
数据模型
图6:会计处理说明
因此,有了初始输入(让我们称之为规则参数,因为它们只是规则中使用的参数)和规则执行流程,我们实际上可以构建一个可用于审计目的的自给自足的事件。
此审计事件包含重现相同日志条目所需的一切(规则参数是数据,DAG是逻辑)。此外,如果我们只是为每个记账的会计交易记录审计事件,我们实际上可以将特定的交易或条目追溯到业务流程和规则(存储为DAG的一部分)以及导致它的事实。因此,同一审计事件解决了再现性和可追溯性。
在这里,如图所示,审计事件可能有多个项目,如促销、信用、税收、通行费;每个项目可能有单独的规则参数和规则执行流程。因此,审计事件并不总是单一的规则参数和规则执行流程。
除了多个项目之外,在某些情况下,我们会针对特定行项目涉及多方(例如,机场、通行费、骑手、司机、接机优步)。
例如,机场费用需要向乘客收取,并支付给当局。此外,它增加了总预订量,然后抵消了我们的收入,因为我们不会保留它。
图7:事件的高级流程图
如果我们在规则流编排器上放置一个装饰器,就可以确保正确捕获执行流;对于生成的每笔交易,我们都会有一个相应的审计事件。此外,我们仅将来自源事件的相关数据属性存储为审计事件中的规则参数。然后将这些事件推送到安全队列(在我们的例子中是安全的Kafka)。
该队列可由服务、ApacheFlink®作业或任何其他类型的消费者使用,以持续侦听审计事件。这个队列使我们能够做很多事情。
作为金融科技平台,我们希望持续消费审计事件,并验证对于每笔生成的交易我们都有相应的审计事件来证明审计事件的完整性。消费者可以利用通用逻辑(有向无环图的深度优先搜索遍历)来处理审计事件。可以通过将审计事件与实际记录的交易进行比较来验证审计事件的准确性。在这种情况下,逻辑可以被视为一个插件,允许我们合并由财务审计员开发的代码,并证明我们的系统按预期运行,而无需向审计员透露我们的底层代码库。
审计事件存储在HDFS中以便长期保留,便于审计查询和其他用例。
重新计算和和解
图8:使用输入参数(Params)和有向无环图重新计算的图示
左侧的JSONblob包含与机场费用相关的审计事件项。它包括审计UUID、交易UUID、输入参数和执行流程本身。
在此上下文中,rulePlan描述了输入参数通过业务规则集传递的过程。它指定执行的确切规则。接下来是实体规则集和一系列其他相关流程。本质上,rulePlan是前面提到的定向树的JSON表示。
通过执行计划运行输入参数,生成类似于右侧提到的日志条目。这些分录可以与实际登记的交易和分录进行比较或链接,以确保可重复性。
图9:描述审计事件重新计算和协调的高级流程图
整体而言,我们有多个上游发出所有类型的不同资金流动(例如,旅行完成、收费失败、促销和信贷授予或兑换)。我们的服务(金融计算服务)使用所有这些Kafka消息并按照前面流程中提到的方式处理它们。因此,每个上游事件都被转换为一个或多个事务,每个事务都包含一些日志行和一些与事件相关的元数据。
我们的服务使用Git中提交的适当会计规则来生成这些交易。作为财务控制,所有这些承诺都得到会计团队的批准,因此遵守SOX合规性和控制。
事务被写入一个不可变的、仅附加的事务存储中。我们使用我们内部的NoSQL数据存储Schemaless作为我们的交易存储。对于写入的每个事务,我们都会将压缩的审计事件发布到安全且无损的Kafka主题。此处的HDFS用作审计事件的持久存储,因此我们可以查询数据或针对我们的用例对其运行重新计算。可以查询这些Hive表以支持临时审计请求并提供可追溯性。
我们在这个batchstore上有每天的工作来读取每天的数据并在其上运行recompute以验证在Schemaless中写入的事务是否与recompute生成的数据同步。recompute方法是一个可插入模块,我们在Java中将其实现为Hive用户定义函数。因此,重新计算可以像另一个配置单元查询一样执行,而无需任何工程支持。
重新计算函数使用不同的规则引擎库以不同的语言编写。因此,如果Go服务使用的实际库中出现任何错误或不需要的更改,我们可以及早发现潜在的回归。此外,如果审计员需要,他们可以提供自己的功能,并通过他们的库运行相同的事件,以确保规则执行按预期工作。
结果有了这个框架,我们能够将解决方案扩展到每天超过10亿笔会计交易,并且能够在未来横向扩展。
财务审计有时会要求我们复制与给定旅行相关的所有交易。每次旅行都由多个事件组成,因此并不总是在同一天或同一月发生。通过transactionUUID在Hive中使用可查询数据,数据分析师和审计员能够轻松地对数据进行切片和切块以衡量影响,并确保旅行样本的可重复性和可追溯性。我们将整体人工工作量减少了60%。
我们能够使用标准Hive配置在短短几个小时的SLA内重新计算该季度发生的每个审计事件,但这可以通过向其添加更多计算来横向扩展。
此功能支持数百条规则和代码更改,无需对任何审计组件、重新计算方法或引擎进行任何更改。
此外,持续审计的能力有助于我们每天监控系统的健康状况。这使我们能够每天对我们的财务系统进行审计,及时发现和修复审计中的任何问题。
结论借助Tesseract审计框架,我们可以确保Uber财务系统处理的每一个事件即使在互联网规模上也是可审计和可追溯的。架构可扩展以支持新数据源的加入,而无需修改框架。会计运营团队可以修改规则以满足业务需求,而不必担心未来的审计差异问题。就下一步而言,我们希望不断改进成本优化、可靠性和存储占用空间,通过自助服务功能和其他改进让会计业务的生活更轻松。
作者:
哈西特·巴特
HasitBhatt是Uber认知平台团队的高级软件工程师。他是概念化Tesseract框架的首席工程师之一,实施了关于如何设计审计事件并在未来重放以简化审计流程的概念证明。他是审计事件捕获和重新发布的设计和执行以及为审计建立自动化完整性管道的核心贡献者。
索拉布·卡斯帕利亚
SaurabhKathpalia是优步交付团队的高级软件工程师。在金融科技团队工作期间,他是概念化Tesseract框架的首席工程师之一。他是审计事件生成的设计和执行、建立自动化审计管道、与审计机构合作批准审计流程等方面的核心贡献者。
杰拉姆·库马尔
JayramKumar是Uber的一名高级软件工程师,也是金融科技团队的负责人。他领导并为Uber的金融系统构建了可靠、高效的交易处理服务。目前,他的工作重点是扩展和增强财务会计和报告平台,支持互联网规模的通用审计功能。
哈里·斯里尼瓦桑
HariSrinivasan是优步金融科技工程总监。他领导着一支技术娴熟的工程师团队,负责监督技术平台的开发,该平台可促进优步内部所有交易和资金流动的无缝财务会计。Hari的团队专注于创新,始终致力于创建能够在互联网范围内高效运行并融合零接触自动化的技术解决方案。他们突破技术界限以确保最高水平的准确性,保证交易的完整性和不可否认性,同时为时间紧迫的流程提供低延迟报告。
出处:https://www.uber.com/en-US/blog/automated-audit-framework-for-internet-scale-financial-transactions/