news 2026/9/12 9:47:59

使用 @tursodatabase/sync 在 JavaScript 中实现 Turso 本地数据库与云端双向同步

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
使用 @tursodatabase/sync 在 JavaScript 中实现 Turso 本地数据库与云端双向同步

使用 @tursodatabase/sync 在 JavaScript 中实现 Turso 本地数据库与云端双向同步

【免费下载链接】tursoA SQL database in Rust: SQLite-compatible, now also speaking Postgres (experimental). The LLVM of databases.项目地址: https://gitcode.com/GitHub_Trending/tu/turso

Turso(turso)是一个用 Rust 编写的 SQLite 兼容数据库,而@tursodatabase/sync是面向 JavaScript/TypeScript 生态的同步引擎包,用于将本地 Turso 数据库与 Turso Cloud 托管数据库进行双向同步。本文以 bindings/javascript/sync/README.md 为主线,结合该仓库中 sync 包的 TypeScript 源码(sync/packages/commonsync/packages/nativesync/packages/wasm)与 NAPI 类型定义,完整讲解安装、connect配置、pull/push/sync同步流程、高级选项(加密、transform、远程写、部分同步等)以及底层同步引擎的运作原理,帮助你快速上手并深入理解同步机制。

包定位与适用场景

@tursodatabase/sync的核心职责是将本地 Turso 数据库与 Turso Cloud 之间进行双向同步(原文:This package is for syncing local Turso databases to the Turso Cloud and back)。

在 Turso 的 JavaScript 生态中,它与其他包的分工如下(见 sync/README.md 与 native 包 README):

  • @tursodatabase/database:提供内存版 Turso 数据库,兼容 SQLite 查询语言与文件格式,直接运行在 Node.js 进程内,无网络开销;
  • @tursodatabase/database-wasm:浏览器(WASM)环境的嵌入式数据库库;
  • @tursodatabase/serverless:提供相同 API 的 serverless 驱动;
  • @tursodatabase/sync:在上述本地数据库能力之上,增加与云端数据库的双向同步能力。

需要注意的是,@tursodatabase/sync连接的数据库由connect返回的对象,@tursodatabase/database包的Database类拥有相同的函数,只是额外增加了几个用于同步的方法(pullpushsync),这与源码中class Database extends DatabasePromise(见 promise.ts)的实现一致——同步数据库实例继承自通用的DatabasePromise基类。

从仓库状态看,Turso 数据库已在多家组织的生产环境中运行,但尚未达到 1.0 版本,官方在文档中明确建议保持备份

安装

在 Node.js 项目中安装同步包:

npm install @tursodatabase/sync

从 native 包 package.json 可以看到,该包是 NAPI-RS 原生模块(二进制名sync),并依赖两个配套包:

  • @tursodatabase/database-common:提供DatabasePromiseTransaction等公共基础类;
  • @tursodatabase/sync-common:提供同步引擎协议、runnerretryFetch等公共逻辑。

支持的原生目标平台包括x86_64-unknown-linux-gnux86_64-pc-windows-msvcaarch64-apple-darwinaarch64-unknown-linux-gnu,即 Linux(x86/arm64)、macOS 与 Windows 均可用。

快速开始:同步一个 Turso Cloud 数据库

以下示例完整来自 sync/README.md 的 Getting Started 章节,用于将托管在 Turso Cloud 的数据库同步到本地:

import { connect } from '@tursodatabase/sync'; const db = await connect({ path: 'local.db', // path used as a prefix for local files created by sync-engine url: 'https://<db>.turso.io', // URL of the remote database: turso db show <db> authToken: '...', // auth token issued from the Turso Cloud: turso db tokens create <db> clientName: 'turso-sync-example' // arbitrary client name }); // db has same functions as Database class from @tursodatabase/database package but adds few more methods for sync: await db.pull(); // pull changes from the remote await db.push(); // push changes to the remote await db.sync(); // pull & push changes

几点关键解读:

  • path本地文件的路径前缀。正如 types.ts 中DatabaseOpts.path的注释所说明:同步数据库会以该前缀写出多个文件(例如local.db-infolocal.db-wal等),不仅仅是单个数据库文件;
  • url是远程数据库地址,可以通过turso db show <db>获取;注意它同时支持libsql://https://两种协议前缀——在 run.ts 的normalizeUrl中会把libsql://turso://统一归一化为https://后再发起请求;
  • authToken通过turso db tokens create <db>签发;
  • clientName是任意客户端名称,用于在服务端区分客户端;源码注释说明库会通过追加唯一后缀来保证clientId的唯一性。

创建连接后,db同时具备普通数据库能力(execpreparetransaction等)与三个同步方法。sync()等价于先pull()push()

核心同步 API 详解

pull():从远程拉取变更

await db.pull(); // 返回 true 表示拉取到了新变更

pull()的语义与返回值的细节,可从 promise.ts 的实现确认:

  • 若数据库是以无同步方式打开(未提供url),调用会抛出sync is disabled as database was opened without sync support
  • 内部先调用engine.wait()等待远程产生新变更,再调用engine.apply(changes)将变更应用到本地;
  • 如果拉取到的变更集为空,返回false;否则应用变更后返回true
  • 如果设置了longPollTimeoutMs,服务端会保持连接打开,直到数据库出现新变更或超时,可用于实时监听场景。

push():推送本地变更到远程

await db.push();

push()将本地累积的未同步变更发送到远程(见 promise.ts)。如果设置了transform回调,则每条变更在发送前都会经过该回调处理(详见下文 transform 章节)。

sync():先拉后推

await db.sync(); // pull & push changes

sync()pull()push()的组合操作,适合在程序的关键节点(如启动时、写入一批数据后)做一次完整的双向同步。

checkpoint() 与 stats()

除了文档提到的三个同步方法,同步数据库还提供了(见 promise.ts):

  • await db.checkpoint():对本地数据库执行 WAL checkpoint;
  • await db.stats():返回同步引擎统计信息,其结构定义在 types.ts 的DatabaseStats
字段含义
cdcOperations尚未发送到远程的本地变更(CDC 操作)数量
mainWalSize主 WAL 文件大小(字节)
revertWalSize回滚 WAL 文件大小(字节)
lastPullUnixTime最近一次成功 pull 的 Unix 时间戳
lastPushUnixTime最近一次成功 push 的 Unix 时间戳(可为 null)
revision从远程拉取到的变更的不透明版本号(可作为 etag 使用,但不应解释其内容)
networkSentBytes/networkReceivedBytes网络发送/接收的总字节数

connect 完整配置项(DatabaseOpts)

connect接受的配置对象类型为DatabaseOpts,完整定义在 types.ts。下表汇总了所有字段及其语义:

字段类型说明
pathstring(必填)本地文件路径前缀,同步引擎会写入多个带此前缀的文件
urlstring \| (() => string \| null)远程数据库地址。省略时为纯本地数据库;也支持传入函数,返回非空值时启用同步(延迟同步),此时其他参数(如加密)必须预先设置
authTokenstring \| (() => Promise<string>)远程鉴权令牌;支持函数形式以按请求提供短期凭证
clientNamestring任意客户端名,库会附加唯一后缀保证 clientId 唯一
remoteEncryptionEncryptionOpts云端数据库加密参数(若云端默认加密)
transformTransform每条变更发送到远程前的回调,可用于实现复杂冲突解决策略
longPollTimeoutMsnumberpull 操作的长轮询超时时间,不设置则无超时
tracing'error' \| 'warn' \| 'info' \| 'debug' \| 'trace'开启内部日志
experimentalExperimentalFeature[]在本地数据库上启用的实验特性(如'views''index_method''vacuum'),与普通Databaseexperimental选项对应
remoteWritesExperimentalboolean实验性:写语句在远程服务器执行而非本地;每次写(或事务提交)后自动 pull 以保持读写一致性。需要url,且所有显式事务都走远程
pushOperationsThresholdnumber单个 push HTTP 批次中打包的 CDC 操作数量上限;达到后按事务边界拆分,单个用户事务不会被拆分。默认(不设置)一次发送全部变更集
pullBytesThresholdnumber引导(bootstrap)下载拆分为多个/pull-updatesHTTP 请求的字节数提示(使用server_pages_selector位图)。默认单次往返完成引导;仅影响引导阶段,增量 pull 不受影响;部分同步使用query策略时无效
logicalMvccPullboolean增量 pull 的同步协议覆盖:默认自动探测远程协议(WAL 页流 vs MVCC 逻辑日志流)并持久化;true强制 MVCC 逻辑日志流,false强制页流。通常仅测试或作为逃生舱使用
fetchtypeof fetch同步引擎所有 HTTP 请求(push、pull、wait-for-changes)的 fetch 实现替换,可用于重试/退避(见retryFetch)、AbortSignal 超时、请求日志、测试 mock
partialSyncExperimentalobject实验性:部分同步配置(见下文)

url 的延迟同步与动态鉴权

urlauthToken都支持函数形式(见 types.ts),这在 promise.ts 中有完整实现:

  • url是函数时,本地数据库先创建,同步在 url 返回非空值时"开启";同步引擎每次 HTTP 请求时都会调用该函数获取最新地址(见 run.ts),若返回 null 则引擎被暂停(url is empty - sync is paused);
  • authToken是函数时,每次请求都会调用它动态生成Authorization: Bearer <token>头,适合接入短期凭证刷新机制。

加密(EncryptionOpts)

remoteEncryptionEncryptionOpts定义在 types.ts:

interface EncryptionOpts { // base64 编码的加密密钥(根据算法必须是 16 或 32 字节) key: string, // 加密算法: // - aes256gcm, aes128gcm, chacha20poly1305: 预留 28 字节 // - aegis128l, aegis128x2, aegis128x4: 预留 32 字节 // - aegis256, aegis256x2, aegis256x4: 预留 48 字节 cipher: 'aes256gcm' | 'aes128gcm' | 'chacha20poly1305' | 'aegis128l' | 'aegis128x2' | 'aegis128x4' | 'aegis256' | 'aegis256x2' | 'aegis256x4' }

当配置了remoteEncryption时,promise.ts 会在每次请求中额外附加两个 HTTP 头:x-turso-encryption-keyx-turso-encryption-cipher,同步引擎的 NAPI 构造参数也相应接受remoteEncryptionCipherremoteEncryptionKey(见 index.d.ts)。

transform:发送前的变更改写

transform回调类型为Transform = (arg: DatabaseRowMutation) => DatabaseRowTransformResult(types.ts)。DatabaseRowMutation描述一条变更:

interface DatabaseRowMutation { changeTime: number; // 变更的 Unix 秒级时间戳 tableName: string; // 变更所属表名 id: number; // 变更行的 rowid changeType: 'insert' | 'update' | 'delete'; // 变更类型 before?: Record<string, any>; // 变更前的行数据 after?: Record<string, any>; // 变更后的行数据 updates?: Record<string, any>; // 变更中仅被更新的列 }

返回值DatabaseRowTransformResult有三种可能(源码注释,见 types.ts):

  • { operation: 'skip' }:完全忽略该变更,不发送也不应用;
  • { operation: 'rewrite', stmt: { sql, values } }:用提供的 SQL 语句替换该变更;
  • null:保持变更原样。

底层处理在 run.ts 的Transform请求类型中:每条 mutation 的结果被映射为Keep/Skip/Rewrite三种内部指令传给同步引擎。

部分同步(partialSyncExperimental,实验性)

partialSyncExperimental: { // bootstrap 策略: // - prefix: 启动时先在本地加载前 N 字节 // - query: 加载指定 SQL 语句触及的页面 bootstrapStrategy: { kind: 'prefix', length: number } | { kind: 'query', query: string }, // 分段大小:让同步引擎按 segment_size 字节分批加载页面 // (例如以 128kb 加载第 1 页时,会加载 [1..32] 共 32 页) segmentSize?: number, // 预取可能很快会被访问的页面 prefetch?: boolean, }

该配置在 promise.ts 中被转换为 NAPI 层的JsPartialSyncOptsPrefix/Query两种 bootstrap 策略,见 index.d.ts)后传给同步引擎。

底层原理:同步引擎与协议循环

@tursodatabase/sync并非简单地"上传/下载整个文件",而是由一个 Rust 编写的同步引擎(SyncEngine,通过 NAPI-RS 暴露,见 index.d.ts 的SyncEngine类)以生成器(generator)协议驱动。理解这一点有助于把握connectpullpush的真实开销与行为。

协议循环(runner / run)

同步引擎通过GeneratorHolderconnect()wait()push()apply()checkpoint()stats()都返回该对象)以resumeAsync方式推进,每次推进可能产生四类请求(见 index.d.ts 的JsProtocolRequest):

  1. Http:向远程服务器发起 HTTP 请求(方法、路径、请求头、请求体由引擎给出);
  2. FullRead:读取本地元数据文件;
  3. FullWrite:原子写入本地元数据文件;
  4. Transform:把一批 mutation 交给 JS 侧的transform回调处理。

这些请求由 run.ts 的process()逐类执行,runner()则维护请求队列并推进引擎的 I/O 循环(ioLoopAsync)。同步引擎的每个 HTTP 请求都会附加上文构造的鉴权/加密头。

并发控制:SyncEngineGuards

由于同步操作会与本地语句执行交错进行,run.ts 定义了SyncEngineGuards,用四把异步锁(waitLockpushLockpullLockcheckpointLock)串行化关键操作:

  • pull()wait守卫(只持 waitLock),拉取到变更后再走apply守卫(四把锁全持有)来应用变更;
  • push()push守卫(持 push/pull/checkpoint 三把锁);
  • checkpoint()checkpoint守卫(四把锁全持有)。

这样保证 wait(长轮询)、push、apply、checkpoint 之间互不重叠,避免本地数据库文件状态在同步过程中被并发破坏。

原生 I/O 与内存 I/O

  • Node.js 环境下使用NodeIO(见 promise.ts):通过node:fs/promises读写文件,且写入采用"先写临时文件再 rename"的原子方式(${path}.tmp.${unix}.${nonce}),避免半写状态;
  • 若底层数据库是内存模式,则改用memoryIO(),把元数据保存在Map中(run.ts)。

远程写(remoteWritesExperimental)

开启remoteWritesExperimental后,promise.ts 的exec/prepare会先通过classifySql(NAPI 层的Database.classifySql,见 index.d.ts)判断 SQL 类别(read/write/begin/commit/rollback):

  • 读语句仍在本地执行;
  • 写语句通过RemoteWriter(remote-writer.ts)发送到远程,RemoteWriter内部复用@tursodatabase/serverlessSession,每次写(或事务提交)后自动pull()一次以保证读写一致性(read-your-writes);
  • 事务整体在远程执行:BEGIN ...COMMITROLLBACKexecRemote自动识别并管理会话生命周期(见 remote-writer.ts)。

需要注意:transactionAsync目前不支持remoteWritesExperimental同时使用(会抛出明确错误,见 promise.ts),此时应使用已标记 deprecated 的transaction()

retryFetch:弹性网络传输

同步引擎的fetch可替换为 run.ts 提供的retryFetch(),为所有同步 HTTP 请求增加重试/退避能力:

  • 重试条件:网络错误(DNS、连接重置、AbortError 等)、5xx 服务端响应、429 限流响应;
  • 不重试:2xx、3xx 及其余 4xx(鉴权/请求错误重试无意义);
  • 默认值:3 次尝试(初始 + 2 次重试)、初始延迟 500ms、退避倍率 2(延迟序列 500ms → 1000ms);可用{ attempts, delayMs, backoff, fetch }定制。

示例:

import { connect } from '@tursodatabase/sync'; import { retryFetch } from '@tursodatabase/sync-common'; const db = await connect({ path: 'local.db', url: 'libsql://...', fetch: retryFetch(), // 默认参数 // fetch: retryFetch({ attempts: 5, delayMs: 1000 }), });

运行与测试

同步包在仓库内的测试方式(见 native package.json 的 scripts)可以复现:

  • 常规测试:npm test,需要先构建本地同步服务器二进制(LOCAL_SYNC_SERVER=../../../../../target/debug/tursodb),并用 vitest 运行(排除 remote-write 测试);
  • 远程写测试:npm run test:remote-write
  • 测试用例可参考 promise.test.ts 与 remote-write.test.ts;
  • 浏览器/WASM 场景对应 wasm 包(@tursodatabase/database-wasm生态),其入口文件支持 Vite/Turbopack 的构建适配(见index-vite-dev-hack.tsindex-turbopack-hack.ts等)。

与周边包的搭配

  • 纯本地内存数据库(无网络):@tursodatabase/database,示例见 native README,支持内存库、文件库与transactionAsync事务;
  • 浏览器环境:@tursodatabase/database-wasm,API 与 Node 版本一致;
  • 无状态 serverless 场景:@tursodatabase/serverless
  • 需要本地↔云端双向同步:@tursodatabase/sync,即本文主题。

仓库中的可运行示例还可在 examples/javascript 下找到(如database-nodesync-nodesync-wasm-viteconcurrent-writesencryption等),其中sync-nodesync-wasm-vite是与本文主题直接对应的端到端参考实现,建议结合阅读。

小结

@tursodatabase/sync提供了一条在 Node.js/浏览器中把本地 Turso 数据库与 Turso Cloud 保持双向同步的路径:通过connect一次配置pathurlauthToken,即可获得与@tursodatabase/database完全一致的数据库 API,并叠加pull/push/sync三个同步方法。在此基础上,其配置体系还覆盖了长轮询监听、动态鉴权、延迟同步、云端加密、冲突改写(transform)、远程写、部分同步等进阶能力;底层则由 Rust 同步引擎以生成器协议驱动 HTTP/本地 I/O 请求循环,配合多把异步锁保证同步与本地执行的安全交错。使用时请留意remoteWritesExperimentalpartialSyncExperimental等仍处于实验阶段的特性,并始终为生产数据保留备份。

【免费下载链接】tursoA SQL database in Rust: SQLite-compatible, now also speaking Postgres (experimental). The LLVM of databases.项目地址: https://gitcode.com/GitHub_Trending/tu/turso

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

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

YOLOv5害虫检测数据集:结构、标注与可视化全解析

简介&#xff1a;YOLO格式的农作物害虫检测数据集&#xff0c;面向计算机视觉入门者与农业智能应用开发者&#xff0c;解决害虫检测任务中数据标注与格式转换的痛点。数据集涵盖蝗虫、苍蝇、水果蛾等4类常见害虫&#xff0c;图像为640640高分辨率RGB图&#xff0c;每张含多个完…

作者头像 李华
网站建设 2026/9/12 9:46:07

Bun 运行时原理与实战:JavaScript/TypeScript 新基建

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

作者头像 李华
网站建设 2026/9/12 9:45:22

RevokeMsgPatcher:让 PC 版微信/QQ/TIM 撤回的消息依然可见

RevokeMsgPatcher&#xff1a;让 PC 版微信/QQ/TIM 撤回的消息依然可见 【免费下载链接】RevokeMsgPatcher :trollface: A hex editor for WeChat/QQ/TIM - PC版微信/QQ/TIM防撤回补丁&#xff08;我已经看到了&#xff0c;撤回也没用了&#xff09; 项目地址: https://gitco…

作者头像 李华
网站建设 2026/9/12 9:41:28

PID控制与Simulink实现:从基础到BP-PID优化

1. PID控制基础与Simulink实现在控制工程领域&#xff0c;PID控制器因其结构简单、鲁棒性强等特点&#xff0c;成为工业控制中最常用的控制器类型。Simulink作为MATLAB的重要组件&#xff0c;为PID控制算法的仿真验证提供了强大支持。我们先从最基本的PID控制器开始&#xff0c…

作者头像 李华
网站建设 2026/9/12 9:39:25

三机九节点系统与风电调频的Simulink建模实践

1. 三机九节点系统与风电调频的背景解析 电力系统仿真领域中&#xff0c;三机九节点系统是一个经典的测试案例&#xff0c;它模拟了包含三个发电机和九个节点的简化电力网络。这个系统虽然结构简单&#xff0c;但足以展现电力系统动态特性的核心要素&#xff0c;包括电压稳定性…

作者头像 李华