Benchling交互式分析功能幕后揭秘
作者:Wonja Fairbrother 和 Eli Levine
科学研究是迭代的过程。为了设计下一个实验,科学家需要分析之前实验的结果。Benchling中的交互式分析功能允许科学家执行实时数据转换、可视化和分析,而无需将数据转移到其他系统中。在本文中,我们将描述Benchling交互式分析功能背后的架构,并分享我们在开发过程中做出的关键决策¹。
交互式分析允许科学家:
-
从多个来源选择数据:
- Benchling实体和结果数据
- 仪器数据
- 笔记本表格
- 通过API和UI上传的数据
-
在不离开Benchling的情况下实时转换、可视化和分析数据:
- 数据转换:过滤、聚合、窗口函数等
- 可视化:折线图、条形图、散点图等
- 科学分析方法:IC50和各种曲线拟合函数
整体架构
支持交互式分析的架构包括:
- Benchling Web应用程序
- 在EKS上运行的可自动扩展的无状态内部服务,负责执行转换
- 用于输入和输出数据的临时S3存储位置,在Web应用程序和服务之间共享
应用程序的前端负责接收用户的输入数据集和转换配置。Web应用程序的后端从适当的来源收集所有输入数据,将数据序列化并上传到S3,然后向服务发送同步转换请求。
服务的API包含一个主要端点,该端点接收转换参数的JSON有效负载。服务可以接受单个转换,或要执行的多个转换列表。在此端点中,服务下载并反序列化输入数据,使用分析引擎执行转换,然后将结果数据序列化并上传到S3。每个请求都会启动自己独立的内存分析引擎,因此数据不会存储在内存之外或请求之间。这样,服务可以同时为多个客户端的请求提供服务。
当请求完成时,Web应用程序下载并反序列化输出数据,并将其显示给用户。前端支持在任何转换步骤的输出数据上进行图表绘制和可视化。然后,用户可以以迭代方式应用更多转换,输出随后成为下一个转换的输入。
所有这些输入和输出数据文件在S3中会很快堆积起来。但是,由于分析是一个探索性和迭代的过程,它们是临时的——因此它们存储在具有生命周期配置的"临时"S3存储桶中,以降低存储成本。
服务内部
数据存储和表示
由于转换的实现细节未向用户公开,我们可以自由选择最适合我们和架构的数据格式。但是,需要考虑一些因素:
- 每列的类型信息应在数据转换过程中保留
- 原始数据类型确定对特定列的有效操作和可视化
- Benchling类型(对Benchling中一级对象的引用,如实体、DNA序列或协议)用于允许应用程序中的对象链接
- 类型感知对于构建成功的机器学习模型至关重要,并大大简化了特征工程和AI就绪数据的准备
- 数据格式应针对读取密集型分析查询进行优化
- 序列化/反序列化延迟应尽可能低,以支持交互式体验
考虑到这些因素,我们选择Parquet作为首选数据存储格式,Arrow作为内存数据表示。Parquet是一种紧凑高效的列式文件格式,Arrow是一种与语言无关的列式数据结构平台。结合使用Parquet和Arrow可以带来许多优势。列式内存布局允许在分析工作负载中实现O(1)随机访问、高效的列修剪、谓词下推和改进的数据压缩。Arrow的Parquet序列化器还具有轻量级模式编码机制,可以在保持数据交换快速的同时附加类型信息。
我们在Benchling支持其他文件格式(CSV、JSON、Avro)用于分析数据,但对交互式分析查询使用Parquet可提供我们所需的速度。为了在格式之间切换的灵活性,我们有一个在服务之间共享的BenchlingDataFrame包装接口。此接口处理序列化和类型模式簿记。服务有一个I/O模块,用于与S3通信并使用BenchlingDataFrame接口序列化/反序列化数据。
分析内核
对于服务的分析内部,我们的目标是尽可能保持轻量级,同时牢记我们的低延迟目标。我们的用户习惯于在其本地机器上执行这些类型的分析,因此体验需要尽可能接近,以最小化任何用户体验的降级。
转换分为两类:基本和高级。将基本操作视为您可能在Excel中执行的操作,如过滤、透视或创建新列。高级操作是那些涉及更复杂计算、统计或机器学习的操作。
我们预计用户将执行的大多数转换是基本转换。此外,我们主要处理表格数据。这使得选择使用DuckDB作为转换的基础案例变得简单明了。DuckDB是一个无服务器SQL OLAP引擎,支持大于内存的处理、多语言API、直接查询不同文件格式(Parquet、CSV、JSON)、活跃的社区,以及每个新版本中添加的许多有用扩展和集成。它针对表格数据的快速分析查询进行了优化,完全适合我们的用例。DuckDB还可以直接操作Arrow和Pandas对象。
虽然DuckDB支持广泛的SQL函数和复杂的SQL查询,这些查询大多涵盖基本转换,但我们希望能够使用不同的库和工具进行涉及高级统计函数的转换。为了实现这一点,我们有一个ExecutionEngine包装器类,可以为给定操作选择要使用的正确底层方法。在这里,我们使用Arrow也给了我们一个优势——Arrow在系统之间具有零拷贝互操作性。这使我们能够使用DuckDB进行类似SQL的操作,并使用Pandas等进行其他操作,为任何输入Arrow表选择执行每个分析操作的最佳底层工具。例如,当服务收到四参数逻辑回归(4PL)转换的请求时,ExecutionEngine可以简单地将Arrow表零拷贝转换为Pandas DataFrame,并调用使用Pandas或Numpy而不是DuckDB的正确函数。
总结
我们使用Apache Parquet与Apache Arrow相结合,实现快速序列化和优化的读取密集型分析查询,并使用我们的BenchlingDataFrame接口保留数据类型信息。服务的ExecutionEngine包装器和Arrow的零拷贝互操作使我们能够使用DuckDB进行常见操作,同时在需要时可以选择调用其他包。
关键架构决策
在设计新系统时,沿途必须做出决策。在这里,我们重点介绍我们团队遇到的一些显著决策点,以及我们解决这些问题的思考过程。
我们从一个概念验证(POC)开始这个项目,该验证使用了Benchling环境中可能的最简单架构:一个无状态服务,对存储在S3中的数据集同步执行数据转换。我们从一个服务开始,因为Benchling的基础设施团队已经建立了出色的支持,用于快速启动Kubernetes服务。这是一条铺好的道路,未知数很少。我们从一个无状态服务开始,因为状态需要管理,这会引入复杂性,这阻碍了快速将端到端原型交到客户手中。
无状态与有状态服务
交互式分析其数据的用户通常会按顺序执行许多数据操作,例如加载数据集、应用过滤器、应用另一个过滤器并执行聚合。我们必须做出的一个决定是是否应在用户的交互式分析会话期间实现中间数据的缓存。基本上:保存操作的结果,以便下一个用户操作执行得更快。
我们考虑了许多可能的中间数据缓存解决方案。一种方法是采用在某种程度上类似于JupyterHub的架构,使用专用于用户会话的后端内核。用户会话中的所有用户交互都将路由到同一个专用节点,该节点将在内存中缓存数据,以便后续调用以低延迟访问。另一种方法是保留一个节点集群,其路由逻辑基于用户会话或数据集哈希。想法是相同的:后续调用可能会遇到已在内存中的数据集。在开始构建这些方法之一之前,我们花时间研究了走这条路的必要性。
没有缓存的单个数据操作(如过滤器)的生命周期大致如下:(1)从S3读取数据(主要是网络开销),(2)将数据加载到DuckDB中,(3)执行实际数据操作,(4)将结果写入S3。步骤(3)和(4)是不可变的——即使有缓存,也必须执行它们。(3)是实际的数据操作,而(4)中将数据写入S3是UI呈现图表和表格所必需的。要回答的主要问题是S3网络传输+加载到Arrow的时间是否足够重要,值得投资某种中间数据缓存。
在代表性环境中进行的快速测试表明,即使对于某些最大的数据集,我们预计始终从S3加载的额外开销大约为350ms,这在产品延迟要求范围内。在这种情况下,更简单的解决方案被认为是足够的,至少在Benchling需要支持显著更大的数据集之前是这样。
服务与按需容器
关于大数据的话题,存在一种可能性,即我们的服务可能无法处理非常大的数据集和/或大量转换,而不会耗尽分配给单个副本的内存。为了在这些场景中扩展,有人可能认为我们应该启动一个具有足够内存来处理请求的容器,而不是将所有请求发送到内存受限的服务。
那么,为什么不一开始就对所有请求使用按需容器呢?主要原因是延迟——我们无法可行地将启动容器的时间成本(大约几秒钟)添加到转换的往返时间中,特别是对于像过滤这样的简单操作。而且,如上所述,目前绝大多数分析都是在适合服务工作节点的数据上完成的。因此,在大多数情况下,使用服务是可接受且更可取的。
尽管如此,拥有在容器中运行转换的选项将使Benchling的一些拥有更大数据的大型客户能够使用交互式分析。此外,我们计划允许客户以自动化方式对传入数据运行保存的分析转换集。这种真正异步的非交互式用例非常适合按需容器,因为没有用户坐在屏幕前等待结果,这给了我们额外的启动时间成本。我们的基础设施团队的内部计算框架允许我们打包我们的服务映像并使用它来运行一次性作业,这使得这个选项在未来易于集成。
有效负载与S3中的结果数据
实际上,用户只需要检查转换结果的预览或摘要,特别是如果结果很大。在这种情况下,服务可以简单地在响应有效负载中返回可显示的结果预览,从而消除了Web应用程序从S3下载和反序列化完整输出的需要。看起来这个选项对于POC来说是最简单的,但它需要对Web应用程序的后端进行大的重构。后端表示这些表格数据的事实上的方式要求下载完整对象并将其反序列化到内存中。因此,我们决定在初始版本中使用现有功能,并通过从S3读取来在UI中显示结果。既然我们处于改进和优化架构的阶段,我们正在重构Web应用程序的数据集表示以允许部分加载。从那里,服务可以仅传回显示所需的内容,而Web应用程序可以直接使用它,进一步减少端到端的转换延迟。
交互式分析是Benchling的一个强大补充,使科学家和研究人员能够轻松执行复杂的数据分析,而无需在应用程序之间切换。希望我们能够向您展示一些使产品运行起来的内部机制,以及构建它时所做的架构决策。我们很高兴看到它对推进研究的影响,并期待我们的用户以创新的方式使用它来推动他们的研发。
[1]:本文可能描述了正在测试中且可能发生变更的功能。