Smallpond
Smallpond 的设计目标是简化大规模数据处理流程,降低 AI 开发者的技术门槛
轻量级数据处理框架,专注于 PB 级数据的高效处理
"--num_executors_per_task",default=5,
"mode", choices=["executor", "scheduler", "ray"], default="executor"
args.mode == "ray": DataFrame(sp, plan.root_node).compute()
args.mode == "executor"
runtime_ctx: RuntimeContext = load(args.runtime_ctx_path)
exec_procs = [Process(target=run_executor,args=(runtime_ctx, args.exec_id, numa_node_id), )
for numa_node_id in range(runtime_ctx.numa_node_count)]
args.mode == "scheduler"
jobmgr = JobManager(
exec_plan = jobmgr.run(
DataFrame 将逻辑计划(plan.root_node)包装成一个分布式数据处理任务
两套API
01.Ray Core as the task scheduler.
api/dataframe
from smallpond.execution.task import Task
from smallpond.logical.node import
from smallpond.logical.optimizer import Optimizer
from smallpond.logical.planner import Planner
from smallpond.session import SessionBase
class DataFrame :封装了 wrapper around a `Node` and a `Session` required for execution
Session 封装 JobId, RuntimeContext import ray
Node
task 也调用了 import ray
02. built-in scheduler and only supports one-time execution of static data flow graphs
01.nodes` to construct static data flow graphs.submit them to smallpond
02.tasks` and wait for all tasks to complete.
api/execution
dataset = CsvDataSet(paths, OrderedDict(schema), delim)
plan = DataSourceNode(self._ctx, dataset)
示例1-High-level API
Initialize smallpond environment.
sp = smallpond.init()
df = sp.read_parquet("path/to/dataset/*.parquet")
df.write_parquet("path/to/output")
init -- a local ray cluster as worker node
data_root
job_id job_time job_name
job的名称默认是当前脚本的文件名
bind_numa_node num_executors
ray cluster
session = Session(
atexit.register可以确保在程序退出时执行必要的清理工作,如关闭文件、释放资源等,从而避免资源泄露或其他潜在问题
示例2-Low-level API
driver = Driver()
ctx = Context()
dataset = ParquetDataSet(input_paths)
node = DataSourceNode(ctx, dataset)
node = DataSetPartitionNode(ctx, (node,), npartitions=npartitions)
node = SqlEngineNode(ctx, (node,), "SELECT * FROM {0}")
plan = LogicalPlan(ctx, node)
driver.run(plan)
Driver 是对 JobManager的封装 ,读取来自命令的参数并把参数值传给JobManger
Scheduler and Executor 是底层的API, 来调度和执行task-- scheduling and executing tasks
node: 封装数据处理的工作流--A typical workflow
# Create a global context 创建一个全局的上下文
创建数据集
创建数据源的node,创建执行引擎,创建逻辑执行计划
数据目录
Data Root https://github.com/deepseek-ai/smallpond/blob/main/docs/source/internals.rst
data_root
└── 2024-12-11-12-00-28.2cc39990-296f-48a3-8063-78cf6dca460b # job_time.job_id
├── config # configuration and state
│ ├── exec_plan.pickle
│ ├── logical_plan.pickle
│ └── runtime_ctx.pickle
├── log # logs
│ ├── graph.png
│ └── scheduler.log
├── queue # message queue between scheduler and workers
├── output # output data
├── staging # intermediate data
│ ├── DataSourceTask.000001
│ ├── EvenlyDistributedPartitionProducerTask.000002
│ ├── completed_tasks # output dataset of completed tasks
│ └── started_tasks # used for checkpoint
└── temp # temporary data
├── DataSourceTask.000001
└── EvenlyDistributedPartitionProducerTask.000002
核心组件:
查询引擎:基于 DuckDB,提供高效的 SQL 查询能力。
存储适配层:与 3FS 集成,负责数据读写和缓存管理。
任务调度:轻量级调度器,支持并行处理和流水线优化。
扩展接口:支持插件机制,可添加新数据源或自定义处理逻辑
dataset plan
提交plan-- 方式一: the JobManager to create a Job in the cluster to execute it
方式二: Driver 来执行
dataframe
dataframe.py
Session 类 继承自 SessionBase,用于管理数据处理会话 class Session(SessionBase)
DataFrame 类 代表一个分布式数据集合,是数据处理的核心类。
ray.wait(
ray.get(dataset_refs, timeout=0)
使用了 dataset
使用了 plan plan: Node SqlEngineNode ShuffleNode HashPartitionNode EvenlyDistributedPartitionNode
使用了 LogicalPlan Optimizer optimized_plan
planner.node_to_tasks planner.visit(self.optimized_plan)
session.py SessionBase 类 负责初始化数据处理环境,包括配置管理、任务调度、日志输出等
class SessionBase:
from smallpond.logical.node import Context
from smallpond.execution.task import JobId, RuntimeContext
self._ray_address = ray.init( address="local", )
worker.py ,这个文件是 SmallPond 工作节点的入口脚本。它负责解析命令行参数、启动 Ray 进程
任务调度
Driver作为入口点,解析参数后根据模式调用不同的执行逻辑。Driver 是对 JobManager的封装 ,读取来自命令的参数并把参数值传给JobManger
JobManager负责作业的整体管理,创建执行计划并启动Scheduler。
Scheduler 分配任务给 Executor,
Executor实际执行具体的Task
args.mode == "executor":
runtime_ctx: RuntimeContext = load(args.runtime_ctx_path)
run_executo
executor = Executor.create(runtime_ctx, exec_id)
executor.run()
通过逻辑计划(Logical Plan)和执行计划(Execution Plan)描述数据处理流程
"-e", "--engine_type", default="duckdb", choices=("duckdb", "arrow")
smallpond/execution/driver.py
"--tags", nargs="*", default=[], help="Tags for submitted platform task"
manager.py JobManager 类负责作业的整体管理,包括创建执行计划、启动调度器和执行器
保存逻辑计划、执行计划和调度状态,调用了调度器scheduler,使用观察者模式
以及使用配置文件的方式来执行 executor
初始化运行时上下文( RuntimeContext)
execution/manager.py
from smallpond.execution.task import ExecutionPlan, JobId, RuntimeContext
class JobManager(object) def run(
sched_state_observers: Optional[List[Scheduler.StateObserver]] = None
sched_state_exporter = SchedStateExporter(runtime_ctx.sched_state_path)
sched_state_observers.insert(0, sched_state_exporter)
class SchedStateExporter(Scheduler.StateObserver)
dump(runtime_ctx, runtime_ctx.runtime_ctx_path, atomic_write=True)
scheduler.py Scheduler类 是任务调度的核心,负责将任务分配给可用的执行器(Executor),并监控任务的状态。
Scheduler 维护任务队列,处理任务的重试和失败,确保作业的顺利完成
维护任务队列和执行器状态(RemoteExecutor/LocalExecutor)
smallpond/execution/scheduler.py
ExecutorState(Enum)
RemoteExecutor LocalExecutor(RemoteExecutor):
class Scheduler(object):
class StateObserver(object):
from smallpond.execution.workqueue import (
StopExecutor,
StopWorkItem,
WorkItem,
WorkQueue,
WorkQueueInMemory,
WorkQueueOnFilesystem,
)
self.running_works: Dict[str, WorkItem]
executor.py,其中定义了 Executor 类,负责实际执行任务。
它使用工作队列(WorkQueue)来处理工作项(WorkItem),并管理任务的执行,通过 SimplePool 实现多进程并发。
通过底层的3FS进行exeutor服务发现和task分发的
--runtime_ctx_path 通过配置文件路径--pickle来关联
help="The path of pickled runtime context passed from scheduler to executor",
runtime_ctx: RuntimeContext = load(args.runtime_ctx_path)
for output_name, nodes in nodes_groupby_output_name.items():
class Executor(object):
task = SimplePoolTask(func, args, name)
from smallpond.execution.workqueue import (
StopExecutor,
StopWorkItem,
WorkItem,
WorkQueue,
WorkQueueOnFilesystem,
WorkStatus,
)
self.running_works: Dict[str, Tuple[SimplePoolTask, WorkItem]]
task.py定义了各种任务类型,如DataSourceTask、MergeDataSetsTask、PartitionProducerTask等
RuntimeContext
调用了 duckdb
logical
###逻辑计划和执行计划的分离,实现了系统的模块化和可扩展性。
node.py 这是整个系统的核心之一。 Node 类的定义,它是所有逻辑节点的基类
DataSourceNode 和 DataSinkNode
PythonScriptNode、ArrowComputeNode 和 SqlEngineNode
create_task 方法,它用于创建任务,这是执行计划的基础 使用 DuckDB 引擎来执行查询
LogicalPlanVisitor 类,它使用了访问者模式来遍历逻辑计划
全局上下文类,用于管理逻辑计划的用户定义函数(UDF)
create_function 和 create_duckdb_extension 用于注册 UDF 或 DuckDB 扩展。
optimizer.py 。这里定义了一个 Optimizer 类,它的作用是优化逻辑计划
planner.py 。这个文件定义了一个 Planner 类,它负责将逻辑计划转换为执行计划
exec_plan = ExecutionPlan(self.runtime_ctx, root_task, logical_plan)
### 支持插件机制,可添加新数据源或自定义处理逻辑
dataset.py,对不同数据集类型的定义和处理逻辑
load_partitioned_datasets
RowRange 类
Dataset 是所有数据集的基类,提供了数据集的基本功能
ParquetDataSet 类 继承自 Dataset,专门处理 Parquet 文件。
CsvDataSet ArrowTableDataSet
udf.py 定义了 UDF(用户自定义函数)相关的类型和工具,用于扩展系统的功能
UDFType 是一个枚举类,它包装了 DuckDB 的数据类型 UDFContext 和其子类,它们负责管理 UDF 的上下文和绑定逻
IO-文件系统
arrow.py 中定义了一个 RowRange 数据类, 帮助管理数据文件中的行范围,可能用于分片或并行处理数据集。
需要将数据均匀分配到多个任务或线程中进行处理的场景
PyArrow 的小字符串和小二进制类型转换为大字符串和大二进制类型
filesystem.py
测路径是否属于 HF3FS 文件系统,并提取挂载点路径
包括路径管理、对象序列化和反序列化,特别支持 HF3FS 文件系统和 Zstandard 压缩。
通过 Platform 抽象层支持不同集群环境(如 MPI)
class Platform:
定义了一些默认值和日志记录功能。
start_job stop_job default_job_id
进程管理--popen = subprocess.Popen os.kill uuid.uuid4()
Grafana | 查询、可视化、警报观测平台
class MPI(Platform):
架构特点
清晰的架构设计:
通过 抽象基类提供的默认实现和统一接口,减少了重复代码,提高了代码的可维护性
通过抽象基类和具体平台实现的分层设计,使得代码结构清晰,易于理解和维护
支持多种平台的实现和扩展,可以根据需求快速添加新的平台支持--插件机制
contrib
contrib 模块包含了一系列附加的、可选的扩展功能,如数据类型、函数、工具和插件等
动态加载模块 实验性的功能和特性
CopyArrowTable 和 StreamCopy。测试或验证数据传输完整性的
ArrowComputeNode 和 ArrowStreamNode,这意味着它们是基于 Apache Arrow 的计算和流式处理节点
log_dataset.py 日志来监控数据处理的各个阶段 LogDataSet
通过继承 PythonScriptNode 和 PythonScriptTask,展示了如何将自定义任务集成到数据处理框架中
添加各种各样的自定义逻辑
WARC(Web ARChive)格式是网络资源存档中使用的常见文件格式,全称为Web Archive File Format, 网站归档
OFD格式在电子文件管理和档案信息化 OFD是自主可控的版式格式-网页归档
ImportWarcFiles 类中使用了 warcio.ArchiveIterator 来解析 WARC 文件
高阶的API的流程
smallpond init
api/dataframe
from smallpond.execution.task import Task
from smallpond.logical.node import
from smallpond.logical.optimizer import Optimizer
from smallpond.logical.planner import Planner
from smallpond.session import SessionBase
DataFrame is the main class in smallpond
功能: Loading Data Partitioning Data Transformations
Consuming Data Execution
class DataFrame :封装了 wrapper around a `Node` and a `Session` required for execution
Session 封装 JobId, RuntimeContext import ray
Node
调度: import ray
ray.wait(tasks, timeout=0)
ray.get([task.run_on_ray() for df in dfs for task in df._get_or_create_tasks()])
计划 node optimizer planner session
数据:from smallpond.logical.dataset import *
Task--涉及到数据-调度
import duckdb
import ray
from smallpond.execution.workqueue import WorkItem, WorkStatus
参考
https://github.com/deepseek-ai/smallpond
https://github.com/deepseek-ai/smallpond/blob/main/docs/source/api.rst