Introducing Bobbin: 一款无磁盘、纯API的Tangled AppView
摘要
Bobbin 是 Tangled 的一款全新只读、纯 API 的 AppView,通过 XRPC 提供 Tangled 数据,无需持久化存储,每次重启时从头开始回填。
<p><a href="https://lobste.rs/s/uoiw3u/introducing_bobbin_diskless_api_only">评论</a></p>
查看缓存全文
缓存时间: 2026/07/13 07:50
# 介绍 Bobbin
来源:https://blog.tangled.org/bobbin/
大家好!我是 Lewis,这是我的第一篇 Tangled 博客。在 Tangled,我的工作涉及大量研发——说白了就是瞎折腾然后发现新东西。今天我要聊的就是这样一个发现,它最终演变成了这个新的 Tangled API。
Bobbin 是一个只读、仅用于 API 的 [AppView](https://atproto.com/guides/glossary#app-view),专为 Tangled 打造。它通过 [XRPC](https://atproto.com/specs/xrpc) 提供 `sh.tangled.*` [词表](https://atproto.com/specs/lexicon) 的读取端能力,客户端无需逐个查询 [PDS](https://atproto.com/guides/glossary#pds-personal-data-server) 就能渲染 Git 仓库、议题、拉取请求、评论、星标和关注关系。它没有永久存储,所有数据仅存在于内存中,每次重启都会从上游回填。
> 我和许多人都觉得 "AppView" 这个术语过于抽象,甚至命名欠妥。在 atproto 中,数据集分布在各用户持有的个人数据服务器(PDS)上,AppView 是一个查询数据集的统一 API,避免了每个下游客户端与每个 PDS 分别建立连接。AppView 是减轻 PDS 负担的众多程序之一,让 PDS 能以相对低廉的成本高效运行。
让我列举一些 Tangled 的 AppView 存在的问题,这样你就能理解我是如何一步步发现并最终打造出 Bobbin 的:
1. Tangled 的 AppView 没有 API;它同时兼任 Web 客户端,通过 SSR 提供服务。
2. 它很脆弱,无法轻松回填 [atproto](https://atproto.com/) 数据。
3. 其底层架构天然假设 *自己* 是数据的事实来源,这是典型 Web 程序的思维方式。
4. 由于上述原因,加上存在诸如邮箱到用户 [DID](https://atproto.com/specs/did) 映射这类私密数据,Tangled 应该只保留一个 AppView。这意味着访问 Tangled 的延迟大致取决于距离斯德哥尔摩服务器的远近。
5. 在内存危机期间,它对内存的消耗极大。
将这些问题的反面翻转过来,就得到了我理想中的 AppView:一个仅提供 API、易于回填、易于分发、内存占用低的程序。
为什么是“仅提供 API”?一旦能通过通用 XRPC API 轻松获取 Tangled 数据,就可以在其上构建更多程序——比如替代前端、第三方客户端、个人定制仪表盘;就我而言,是为了实现一个好用的 CLI(虽然这也算替代前端)。现有的 Tangled AppView 把自己当作 Web 客户端,导致没有可扩展的基础,除非你是爬虫高手,否则只能自己想方设法从 PDS 世界中抓取 Tangled 数据集。
在我思考如何有趣地创建这样一个程序时,我也一直在为状态存储的问题而焦虑——我时常冷汗涔涔地醒来,担心 Tranquil PDS 自制的嵌入式数据库会不会以某种神奇的方式崩溃。这个新程序如何才能避免写入任何持久化、易出错的状态呢?况且,AppView 是 PDS 的下游,这意味着我们在 AppView 中保存的状态越多,它就越可能与现实脱节。哪怕只漏掉一条记录,就足以让 AppView 的数据严重失准。因此,让 AppView 能够协调自身认知与实际情况,至关重要。
身为一个头脑简单的人,我认为让一个只负责缓存和提供记录的程序永不接触磁盘,是最好的方法。我希望它每次重启时都从零开始回填所有数据。这样一来,与现实同步只需要一次重启,而且能自动完成!这可行吗?如果每次回填都要花一整天呢?那部署会变得很慢,这样做到底值不值得?
我最初的概念测试结果令人振奋。我快速搭建了一个小服务器,它能在约 15 分钟内回填 Tangled 的全部 atproto 数据集——利用 [Hydrant](https://tangled.org/did:plc:6v3ul2ptnqctyxwkz5ti4amn) 作为聚合器。我设定的可接受回填时间是 5 分钟以内或更短,这样在蓝绿部署时,就能在合理的预热时间内切换运行实例。
> Hydrant 是一个针对任意 atproto 数据集的聚合器,它通过追踪实时 [事件流](https://atproto.com/specs/event-stream) 工作,拉取网络中每个 [仓库](https://atproto.com/guides/glossary#data-repo) 的 [CAR 文件](https://atproto.com/specs/repository#car-file-serialization) 进行回填,并通过 WebSocket 重新提供数据。Bobbin 只需要建立连接,请求游标 0,然后根据返回的数据流(重新)构建它的索引。非常感谢 [Dawn](https://tangled.org/did:plc:dfl62fgb7wtjj3fcbb72naae) 创建了 Hydrant!也感谢她听取我的困难后,对它进行优化——还是出于乐趣!
当回填时间降到 5 分钟时,我为了好玩把目标调整到 90 秒以内。这正是我们目前接近的数字——取决于上游 Hydrant 实例的预热程度:最佳情况下 30 秒就能完成,最糟时(Hydrant 也要从头从 PDS 拉取记录)可能需要 20 分钟。
> 是的,你也可以自己启动这些程序,并在 20 分钟或更短的时间内获取 Tangled 的全部数据,否则全额退款。(退款金额为 0 欧元。)
我遇到一个小难题:回填还在预热时,程序该提供什么服务?在聚合/关联数据可能不准确的情况下,我是否应该让程序处理请求?我认为不应该。那么,在最坏情况下可能长达 20 分钟的回填时间内,程序是否应该什么也不提供?我本来可以这么做,但相反,我们至少在点对点查询时连接到 [Slingshot](https://tangled.org/did:plc:c7mc2fn47ihdihul4vjwsuy3/tree/main/slingshot),这样即使程序本身尚未“见过”这些数据,也能立即获得准确信息。
> Slingshot 是一个出色的 atproto 记录和身份边缘缓存。Bobbin 向它请求单条记录和身份查询,这样新启动的 Bobbin 在其索引预热之前就能准确回答单个查询。Slingshot 是 [microcosm](https://www.microcosm.blue/) 项目的一部分,该项目提供了一系列社区运行的 atproto 基础设施。感谢 [Fig](https://tangled.org/did:plc:hdhoaan3xa3jiuq4fg4mefid) 创建了它!
正在演变为 Bobbin 的这个程序,能够将 Tangled 的全部 atproto 数据集存储在约 200MB 的内存中——这已经不错了,但经过一些压缩后降到了 100MB。可能还有更多压缩空间尚未利用。“哇,整个平台社交层的数据竟然这么少!”你会感叹,但别忘了,这是目前以来数据集 *最小* 的时候,未来只会越来越大。节省空间至关重要。
对于未存储在 RAM 中或尚未从 Hydrant 接收到的数据,Bobbin 实时使用 Slingshot 作为记录/身份解析器。我很喜欢 Fig 的工作,但我不自己实现这些查询而依赖 Slingshot,其实是为了节省时间而走捷径;如果 Bobbin 未来还有什么重大架构变化,那将是内联处理这些查询,让 Bobbin 永远不必去请求 Slingshot,并找到更快的获取相同数据的方式。
有人可能会想把 Hydrant 实例嵌入到 AppView 程序中,让提供 atproto 数据集的功能只是 Hydrant 数据的薄包装;但我希望自己能轻松启动数百个程序实例。让数百个 Hydrant 不断请求 PDS 是相当浪费的。一个 Hydrant 实例就能轻松为数百个 Bobbin 提供服务。
由于我们没有与 Knot 相关数据的 Hydrant/中继等效物,Bobbin 将 Knot 相关请求直接代理到指定的 knot。我觉得需要一个 knotstream 聚合器,或许只是一个能同时处理两者的 Hydrant 插件。我认为 Bobbin 和 Knot Mirror 应该合并成一个服务,但这只是我的个人看法。
有了“无状态”的 AppView,我们就不必担心数据库及其相关烦恼:迁移、数据略微偏离“真实”数据集时的生产环境修正,等等。任何时候想要更改或添加功能,只需要重启 Bobbin 即可完成。另一个好处是,Bobbin 很适合“云原生部署”——用企业的话说。Tangled 自己的旗舰实例就运行在 Cloudflare Containers 上。
说了这么多,我并非死守 Bobbin 永不接触磁盘的想法。一个不错的改进可能是每小时将内存中的数据集快照保存到磁盘,启动时只回填差异部分。但目前我认为这条路还有空间继续走下去——写这篇文章时,Bobbin 可以在 200MB 内存下健康运行。此外,我最初“内存占用低”的要求目前仍然满足,但一旦变得太重,Bobbin 会在运行时动态地将部分数据卸载到磁盘。你问“太重”如何定义?我凭直觉判断:1GB RAM 算浪费,10GB 则不可接受。此外,Bobbin 还要服务用户,每个 API 请求都会增加内存压力,不仅仅是存储 Tangled 数据集本身。
就是这样!现在有了这个有趣的 AppView——无磁盘存储、低于 90 秒回填、像一个能跑起来的小引擎一样处理 API 请求。有了它,任何人都能更容易地使用 Tangled 数据集构建程序。
Bobbin 的一个实例运行在 [api.tangled.org](https://api.tangled.org/),希望你觉得托管自己的实例也很容易。Bobbin 的源代码位于 [我们的单体仓库](https://tangled.org/tangled.org/core/tree/master/bobbin)。
相似文章
Bob的CLI
Bob的CLI是一款本地优先、适应用户的AI编码命令行工具。
@beyang:说到这个,这是我今天为演示做的一个东西:在 Amp 内的一次性 3D Flappy Bird。(用户要求外太空是粉色的。)
Beyang 展示了在 Amp 中构建的一次性 3D Flappy Bird 演示,重点介绍了 Orbs 功能,可在一个窗口内实现应用更改的创建、审核、使用和合并。
@berryxia: 兄弟们! 不要重复造轮子,直接拿这个31.4K Star的开源来干吧! 字节跳动把 UI-TARS-desktop 开源了,看了一眼,这个项目已经上线快一年了! 目前 Star 数已经来到 31.4k,而且增长速度还挺稳。 24 小时增长…
ByteDance open-sourced UI-TARS-desktop, a native desktop GUI agent with 31.4k GitHub stars that uses vision models to control local or remote applications via natural language. The tool runs locally for privacy, supports Windows and macOS, and includes a CLI with streaming output for developers.
BobCA
BobCA 是一个自主的 AI 代理,能够根据用户偏好学习编码,现已在 Product Hunt 上架。
Movwin:我的(未发布)TUI 框架
一篇博文,详细介绍了作者个人未发布的、基于 ncurses 的 Python TUI 框架 'movwin',重点突出了其对 Unicode 的支持和性能表现,并因担心 AI 公司爬取代码而决定将其保密。