news 2026/10/1 2:00:27

GUN 版本演进与迁移实战指南:从 0.3 到 0.2020 的 API 变迁、破坏性变更与升级清单

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
GUN 版本演进与迁移实战指南:从 0.3 到 0.2020 的 API 变迁、破坏性变更与升级清单
  • 数据库
  • 图数据库
  • 后端

【免费下载链接】gun

An open source cybersecurity protocol for syncing decentralized graph data.

项目地址:https://gitcode.com/gh_mirrors/gu/gun
点击查看免费下载

本文以 GUN 官方 CHANGELOG 为骨架,梳理 GUN(一个实时、去中心化、离线优先的图数据同步引擎)从 0.3 到 0.2020 的完整演进脉络,逐条解读每一版本引入的破坏性变更、迁移步骤与底层源码实现。读者读完本文后,将能够:识别老版本 API 并完成 0.3 → 0.5 → 0.2020 的平滑升级;理解 soul 格式、RAD 存储引擎、DHT、.val(cb)语义、.set()弃用等核心变更背后的设计动机;并借助仓库源码(src/state.js、src/root.js、src/back.js、src/put.js、src/on.js)掌握状态时钟与 HAM 冲突解决机制,从而在实际项目中正确配置和升级 GUN。

一、版本时间线总览

CHANGELOG 记录了 GUN 从 0.3 起步到 0.2020 的演进轨迹。当前仓库 package.json 中的版本号为0.2020.1239,且 src/root.js 中Gun.version = 0.2020,说明仓库主线正处于 0.2020.x 时代。整体时间线与关键词如下:

版本关键词变更性质
0.2020.xRAD 正式化、soul 格式重定义、DHT(AXE)结构性演进,核心 API 无破坏
0.2019.xRAD 与 SEA 数据格式调整尽量向后兼容
0.9.xRadix Storage Engine(RSE)集成、S3 备份无破坏性变更
0.8.x适配器接口改为实例级gun.on、.path()/.not()外移破坏性(适配器层)
0.7.x.val(cb)触发语义变化小型破坏性变更
0.6.x链式.val()、.map()map/reduce、socket 升级实验性新特性
0.5.90.3 → 0.4 → 0.5 迁移指南大规模 API 更名
0.3.x.attach→.wsp、.set()弃用、内部改名大规模破坏性变更

整体可以概括为三条主线:数据格式与存储层(RAD/RSE 取代旧存储适配器)、API 与链式接口(.val()/.map()/.set()语义收敛)、网络与拓扑(socket 升级、DHT 引入)。下面按版本逐一展开。

二、0.2020.x:当前主线的架构性变更

0.2020.x 是距离当前仓库最近的主线版本,CHANGELOG 明确指出核心 API 无破坏性变更,但以下结构性变化会显著影响依赖内部机制的应用。

2.1 soul 格式:从随机 UUID 到可预测图路径

GUN soul format changed from being a random UUID to being a more predictable graph path (of where initially created) to support even better offline behavior. This meansnulling & replacing an object will not create a new but re-merge.

soul 是 GUN 中每个图节点(node)的唯一标识('#'字段)。旧版本中 soul 是随机 UUID;新版本中 soul 变为「节点最初创建位置的可预测图路径」,以支持更好的离线行为。这一变更带来的直接后果是:对对象执行null后再替换,不再创建新节点,而是重新合并到原节点——离线环境下的冲突合并行为因此更可预期。

在源码层面,soul 的写入集中在状态工具中:src/state.js 的State.ify(n, k, s, v, soul)会在传入soul参数时设置n._['#'] = soul;而数据读取与校验则依赖 src/valid.js 对{'#': soul}形式的链接进行识别(Gun.valid返回 soul 字符串时即为节点引用,参见 test/common.js 中 Gun Safety 的is link测试组)。

2.2 RAD 正式化与存储适配器

Storage adapterputevent breaking change (temporary?), RAD is official now and storage adapters should be RAD plugins instead of GUN adapters.

0.2020.x 宣布RAD 成为官方存储方案,存储适配器应作为 RAD 插件而非 GUN 适配器存在。这意味着原先直接挂接put事件的第三方存储适配器需要迁移到 RAD 插件体系。仓库中 RAD 相关实现分布在 lib/radix.js、lib/radisk.js、lib/radisk2.js、lib/rindexed.js 等文件中,另有 lib/rfs.js(文件系统存储)、lib/rfsmix.js 等配套模块;测试侧可参考 test/rad/ 下的 bench、book、recover 等用例。

2.3 DHT 引入与Gun({axe: false})

As the DHT gets implemented, your relay peers may automatically connect to it, so do not assume your peer is standalone.Gun({axe: false})should help prevent this but loses you most scaling properties.

DHT(分布式哈希表)落地后,relay 节点会自动接入 DHT 网络,不能再假设某个 peer 是独立孤立的。若希望阻止自动接入,可在初始化时传Gun({axe: false})——代价是失去大部分扩展性属性。AXE 是 GUN 的 DHT/索引层实现,仓库中 axe.js、lib/axe.js 以及 lib/aws.js(与 S3/AWS 相关的存储扩展)与之相关,测试用例位于 test/panic/axe/(含 load_balance、get_turns、get_subs 等)与 test/panic/e2e/。

2.4 多实例消息传递与内部工具清理

CHANGELOG 特别警告:>0.2020.520版本可能破坏进程内gun1/gun2的消息传递,并提示查看 test/common.js 中 "Check multi instance message passing" 的测试以获取线索。同时:

Pretty much all internal GUN utility will be deleted, these are mostly undocumented but will affect some people - they will still be available as a separate file but deprecated.

大部分内部工具函数将被删除(它们本就未文档化),受影响者仍可从独立文件中获取,但这些工具已标记为 deprecated。升级建议:不要依赖任何未在官方文档出现的Gun._/Gun.chain内部方法,优先使用公开链式 API。

三、0.9.x:Radix Storage Engine 与 S3 备份

No breaking changes, but the new Radix Storage Engine (RSE) has been finally integrated and works with S3 as a backup.

0.9.x 是无破坏性变更的里程碑:Radix Storage Engine(RSE)终于完成集成,并支持以 S3 作为备份存储。RSE 即前述 RAD 存储引擎体系(lib/radix.js、lib/radisk.js 等),配合 lib/aws.js 可实现 S3 备份场景。由于 0.9.x 不破坏公开 API,运行在 0.6–0.8 的应用可以放心升级以获得持久化与备份能力的增强。

四、0.8.x:适配器接口与核心方法外移

0.8.x 有两个直接影响开发者的变更。

4.1 适配器事件从全局改为实例级

Adapter interfaces have changed fromGun.on('event', cb)togun.on('event', cb), this will force adapters to be instance specific.

存储/网络适配器的事件注册从全局Gun.on('event', cb)改为实例级gun.on('event', cb),强制适配器与具体 GUN 实例绑定,避免多个实例共享同一适配器事件造成状态污染。这也与 src/index.js 中Gun.on = require('./onto')(事件调度原语)的设计相呼应——事件系统同时支撑全局Gun.on与实例at.on两种形态(参见 src/root.js 的Gun.create中对at.on = at.on || Gun.on的赋值)。

4.2.path()与.not()移出核心包

.path()and.not()have been officially removed from the core bundle, you can bundle them yourself atlib/path.jsandlib/not.jsif you still need them.

.path()与.not()从核心 bundle 中移除,需要者自行引入:

  • lib/path.js 实现了Gun.chain.path(field, opt):支持以字符串按分隔符(默认.)拆分多级路径并逐个get,也支持直接传入数组逐级下行;
  • lib/not.js 实现了Gun.chain.not = function(cb, opt, t),其底层通过this.get(ought, {not: cb})实现——当目标路径当前无数据时触发not回调,本质上是「不存在时执行」的守卫逻辑。

这也解释了 0.7.x 中.val(cb)与.not(cb)的联动语义(见下节):not的触发时机与val的空数据行为紧密耦合。

五、0.7.x:.val(cb)触发语义变更

0.7.x 是 CHANGELOG 中记录的小型破坏性变更,针对.val(cb):

Previously.val(cb)would ONLY be called when data exists, like.on(cb). However, due to popular demand, people wanted.val(cb)to also get called for.not(cb)rather than (before) it would "wait" until data arrived.

变更前:.val(cb)仅在数据存在时被调用(与.on(cb)类似),因此配合.not(cb)时会一直「等待」数据到达。变更后:.val(cb)也会在.not(cb)场景下被调用,即数据为空时回调同样触发。

注意例外:对于动态路径,.val(cb)依然会等待。例如:

gun.get('users').map().val(cb)

因为map()的行为就是在找不到条目时不向下游链传递任何事件。这一语义延续到了当前源码:核心链的val基于once机制实现,而 src/on.js 中对once的规则注释明确指出——「如果被缓存应快速返回,但不应在写的同时读;不应重复触发其他监听器;即使什么都没找到也应被触发」,正是 0.7.x 语义的源码级印证。

六、0.6.x:链式.val()与.map()map/reduce

Introduced experimental features, chaining.val()(no callback) and.map(cb)behaving as a map/reduce function. It also upgraded the socket adapters and did end-to-end load testing and correctness testing.

0.6.x 引入两个实验特性:

  1. 链式.val()(无回调):不带回调的.val()返回一条新的链,可继续.val(...)串联(none逻辑见 src/on.js,源码注释提醒「链式 val 是实验性的,API 可能继续变化」);
  2. .map(cb)表现为 map/reduce 函数:对集合(Set)逐项映射聚合。

同时该版本升级了 socket 适配器,并完成了端到端的负载测试与正确性测试。当前仓库中map的链实现位于 src/map.js,socket 相关见 src/websocket.js 与 lib/wsproto.js。

七、0.5.9 迁移指南:0.3 → 0.4 → 0.5

0.5.9 给出了一份明确的迁移对照表,是历史上最重要的一次 API 更名,升级老代码时请逐条对照:

旧写法新写法
gun.backgun.back()
gun.get(key, cb)回调签名cb(err, data)回调签名cb(at),读取at.err、at.put
gun.map(cb)gun.map().on(cb)
gun.init已弃用(deprecated)
gun.put(data, cb)回调签名cb(err, ok)回调签名cb(ack),读取ack.err、ack.ok
gun.get(key)(全局/绝对寻址)gun.back(-1).get(key)
gun.key(key)暂时不可用(temporarily broken)

其中gun.back(-1)的语义在 src/back.js 有明确实现:当n === -1或n === Infinity时返回this._.root.$,即根节点链;n === 1返回直接父链;数字n递归回退 n 级;还支持传入字符串数组(按字段逐级回溯)或回调函数(沿链向上查找满足条件的祖先)。gun.put(data, cb)的回调则演化出 src/put.js 中as.ack的 ack 对象(含err与ok字段,参见ran.err与ran中对as.ack(ack)的调用)。

八、0.3.x:从.attach到.wsp的大迁移

0.3 是 CHANGELOG 中记录的最早大规模迁移版本,也奠定了此后所有版本的设计方向。原文迁移指南要点如下。

8.1 服务端:.attach()→.wsp()

Server side default.wsp()renamed from.attach().

服务端默认方法由.attach(更名为.wsp(。所有在服务端使用gun.attach(...)的代码,需改为gun.wsp(...)。

8.2.set()弃用与显式替代

.set()deprecated because it did a bunch of random inconsistent things. Its useful behavior has now become implicit or can be done explicitly. Migrate by removing.set()and changing.set($DATA)to.path('I' + Date.now() + 'R' + Gun.text.random(5)).put($DATA).

.set()因「做了太多随机而不一致的事」被弃用,其有用行为变为隐式(详见 8.4)或需显式写出。迁移公式:把.set($DATA)替换为

.path('I' + Date.now() + 'R' + Gun.text.random(5)).put($DATA)

即用「时间戳 + 随机串」生成唯一 key 并写入。而在后续版本(0.3.3)中,集合场景有了更优雅的原生写法:

gun.get('users').set(gun.get('person/mark')); // 集合/表/列表 gun.get('mark').path('owner').put(gun.get('cat')); // 节点原生链接

其中set的链式返回值语义在 0.3.4 又一次调整:list.set(item)返回的是item 的链,而非 list 链——升级后不要对set的返回值继续做 list 操作。

8.3.not()内部禁止return/this

Inside of.not()no longer usereturnorthis, instead (probably) usegunand noreturn.

在.not(cb)回调内部:不要再return链,也不要使用this,而应使用外层gun变量且不返回任何值。原因:若在.not中return this.put({}).key(key)之类的链,会导致.val()被触发两次——因为return会把两条独立链汇合到一起(这是设计使然)。例如 lib/not.js 的实现中,ought(at, ev)回调里this.not.call(at.gun, ...)以at.gun作为执行上下文,进一步说明回调this语义的脆弱性——升级代码应显式捕获外层 gun 引用。

8.4.put()/.path()隐式.init()与Gun({init: true})

.put()and.path()do implicit.init()by default, turn on explicit behavior withGun({init: true}).

.put()与.path()默认执行隐式.init();如需显式初始化行为,可传Gun({init: true})。这正是 8.2 中「.set()的有用行为已变为隐式」所指——创建即初始化。

8.5 回调与内部 API 改名清单

0.3 还包含一批回调签名与内部 API 的更名,逐条整理如下:

  • .get(soul, cb)的回调参数由(err, graph)改为(err, node)(文档此前就不建议回调风格,现在回调风格是合法的);
  • .val()空调用时自动输出日志(便于调试);
  • 新增.init();
  • 选项opt.wire由opt.hooks更名(模块开发者需同步,且 wire 协议本身已变更);
  • 类型判断更名:Gun.is.val←Gun.is.value;Gun.is.rel←Gun.is.soul;Gun.is.node.soul←Gun.is.soul.on;
  • 合并工具更名:Gun.union.ify←Gun.union.pseudo;Gun.union.HAM←Gun.HAM;
  • Gun.HAM现在就是实际的冲突解决函数;Gun._.state←Gun._.HAM;
  • 错误检测增强:写入 regex、Date、NaN 现在会显式报错(此前是静默);
  • .on()在 key 后来被新建时也会触发(此前不会);.val()不应只收到一个关系引用(内部会解析),已修复。

另有两处稳定性提升:Maximum Callstack Exceeded 问题在非人为阻塞线程时基本消失(#95);.on()/.val()的触发时序修复(#116、#132)。

0.3.4 同时新增Gun.is.lex(词法查询校验),0.3.5 修复服务端推送,0.3.6 修复 S3 拼写问题,0.3.7 捕获 localStorage 错误——这些细节说明该阶段主要在打磨协议正确性与存储边界。

九、源码级印证:状态时钟与 HAM 冲突解决

GUN 的冲突解决(HAM)与状态时钟是理解「为什么这些迁移会发生」的关键。CHANGELOG 中.set()弃用、soul 重定义、.val()语义调整等,最终都落到数据写入时的状态比较上。

9.1Gun.state:单调递增的逻辑时钟

src/state.js 定义了状态时钟:

function State(){ var t = +new Date; if(last < t){ return N = 0, last = t + State.drift; } return last = t + ((N += 1) / D) + State.drift; } State.drift = 0; var NI = -Infinity, N = 0, D = 999, last = NI;

同一毫秒内最多生成 999 个递增计数(N / D),确保同一进程内的写操作状态严格单调递增;State.drift可用于微调(默认 0)。State.is(src/state.js)读取某个 key 的状态,缺失时返回-Infinity;State.ify(src/state.js)在写入时记录n._['>'] = { key: state }。每个 key 的每次写入都携带一个时间戳状态,这是 HAM 比较的基础。

9.2 HAM:基于状态的冲突解决

src/root.js 中的ham(val, key, soul, state, msg)是核心冲突解决函数,其逻辑可归纳为:

  1. 未来状态延迟:若state > now,则setTimeout延迟到状态时间到达后再处理(上限MD,32 位最大整数毫秒),避免时钟漂移导致乱序写入被误判;
  2. 旧状态丢弃:若state < was(本地已有更新状态),直接返回,拒绝旧写入覆盖新数据;
  3. 相同状态合并:若state === was且值相同或长度更短(L(val) <= L(known)),视为相同/重复消息不重复触发;
  4. 新写入落盘:通过root.on('put', ...)触发下游(存储适配器、订阅链)更新。

入站消息的校验在 src/root.js 的put函数中完成:每个 soul 必须带_['#']且与 soul 一致、必须带_['>']状态表、每个值必须通过valid校验,否则整条消息以"Error: Invalid graph!"拒绝。这正是 CHANGELOG 0.3 中「regex/Date/NaN 显式报错」的落地实现。对应测试可参见 test/common.js 中 Union 测试组(disjoint、past、future 等场景)以及 Gun Safety 组对Gun.valid的边界断言。

9.3back/put/on的链式实现

  • back:src/back.js 实现链式回溯,-1/Infinity返回根(对应 0.5.9 迁移中的gun.back(-1).get(key));
  • put:src/put.js 实现图数据写入:递归遍历嵌套对象生成 graph、解析关系引用(cat.link['#'] = soul)、stun机制在写期间暂停读、ran汇总 ack(as.ack(ack)中的err/ok即 0.5.9 新回调签名);
  • on/once:src/on.js 实现订阅与一次性读取,off支持按链解绑;once的注释规则(缓存快速返回、不重复触发、空数据也要触发)正是 0.7.x.val(cb)语义的延续。

十、升级检查清单与测试验证

综合全文,升级到当前主线(0.2020.x,仓库版本 0.2020.1239)时可依据以下清单自查:

  1. 回调签名:所有cb(err, data)/cb(err, ok)改为cb(at)/cb(ack),读取err/put/ok字段(0.5.9);
  2. 服务端方法:attach→wsp;模块开发者将opt.hooks改为opt.wire(0.3);
  3. 集合写入:删除.set($DATA),改用.path('I' + Date.now() + 'R' + Gun.text.random(5)).put($DATA);对list.set(item)的返回值按 item 链处理(0.3 / 0.3.4);
  4. .not()回调:不return链、不使用this,改用外层 gun 引用(0.3);
  5. .path()/.not():若仍需要,从 lib/path.js、lib/not.js 手动引入(0.8.x);
  6. 适配器:事件注册迁移到实例级gun.on(...);存储适配器评估迁移为 RAD 插件(0.8.x / 0.2020.x);
  7. 拓扑假设:确认 relay 是否会被 DHT 自动接管,按需使用Gun({axe: false})(0.2020.x);
  8. .val(cb)空数据行为:确认回调在.not场景下的触发是否符合预期(0.7.x);
  9. 内部 API 依赖:清点是否依赖未文档化的Gun._内部工具,尽快替换(0.2020.x)。

仓库提供了较完整的测试基线可供验证:核心行为测试见 test/common.js(含 Gun Safety、Union/HAM、链式事件等大量用例),浏览器端可在 test/index.html / test/panic.html 中运行,服务端 HTTP 相关见 test/server/ 与 test/panic/e2e/。运行npm test(mocha)可快速验证基础行为;e2e 用例则通过npm run e2e执行。

提示:以上迁移结论均以当前仓库的 CHANGELOG 与源码为据。若在升级中遇到兼容性问题,建议优先在社区渠道反馈,并保留旧版本依赖以便对照验证。

  • 数据库
  • 图数据库
  • 后端

【免费下载链接】gun

An open source cybersecurity protocol for syncing decentralized graph data.

项目地址:https://gitcode.com/gh_mirrors/gu/gun
点击查看免费下载

相关推荐

上一篇:Godot Engine终极指南:VR控制器震动与触觉反馈实现技巧
下一篇:从零到上手:用 ComfyUI-MimicMotionWrapper 三步完成 AI 动作迁移实战

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/1 1:59:32

基于YOLOv8的非机动车闯红灯识别系统实战解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/1 1:58:43

马德拉岛旅行全攻略:徒步、美食、自驾与避坑指南

第一次降落在这座被旅行者称作“大西洋珍珠”的小岛上时&#xff0c;我心里只有一个念头&#xff1a;这地方怎么一开始没被人发现&#xff1f;马德拉&#xff0c;距离里斯本大约一个半小时航班&#xff0c;藏在北大西洋深处&#xff0c;却同时拥有悬崖、云海、月桂林、火山岩池…

作者头像 李华
网站建设 2026/10/1 1:58:42

交通违规目标检测数据集实战指南:时空耦合与法规嵌入

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/1 1:58:24

基于深度强化学习的部分计算任务卸载延迟优化:从原理到实战

简介&#xff1a;面向计算机相关专业学生、教师及企业研发人员&#xff0c;这份压缩包提供基于深度强化学习的部分计算任务卸载延迟优化Python源码&#xff0c;并配有详细代码注释。项目聚焦移动边缘计算下的任务卸载决策&#xff0c;通过深度强化学习模型在本地执行与边缘卸载…

作者头像 李华
网站建设 2026/10/1 1:58:00

配电网N-1扩展规划Matlab建模与求解实践

接到一个配电网N-1扩展规划的需求&#xff0c;第一反应往往是&#xff1a;这不就是把每条线依次断开算个潮流吗&#xff1f;真动手做才发现&#xff0c;问题远不止校核那么轻巧。中压配电网规划里&#xff0c;N-1准则指的是任一馈线、主变压器或开关设备退出运行后&#xff0c;…

作者头像 李华