npm发布-csync-浏览器数据库同步-三方对比
在之前,我想做一个用 github 作为远程仓库,在浏览器中保存内容,可以实现类似 APP 的效果。于是,写一个同步引擎,让数据进行同步,现在已经 npm 和 github 发布了 @huxzhi/csync 可以直接下载
核心功能
Csync 本质是两个适配器,本地和远程,获取文件列表。远程的适配器(提前写了 github,webdav,S3),只需要填写权限配置就行了
所以只需要写本地适配器的 4 个函数就行了,用闭包实现,告诉适配器如何 获取文件信息列表,获取文条目数据,插入数据库条目,删除数据库条目
getLocalManifest 需要提供 路径(作为远程仓库的文件路径,唯一),更新时间和 hash(进行冲突判定),有可以指定标签,只同步部分数据
interface LocalDatabaseAdapter {
getLocalManifest: () => Promise<SyncMetadata[]>;
getRecordContent: (path: string) => Promise<ArrayBuffer | null>;
upsertRecord: (content: ArrayBuffer, meta: SyncMetadata) => Promise<void>;
deleteRecordPermanently: (path: string) => Promise<void>;
}
// github,webdav,S3 ,可以不用写了,如果是其他远程仓库,可以自己实现
interface RemoteRepositoryAdapter {
getRemoteManifest: () => Promise<SyncMetadata[]>;
uploadFile: (meta: SyncMetadata, content: ArrayBuffer) => Promise<SyncMetadata>;
downloadFile: (path: string) => Promise<{
content: ArrayBuffer;
meta: SyncMetadata;
}>;
deleteFile: (path: string) => Promise<void>;
}
三方对比,冲突检测
- 远程仓库,每次获取远程仓库的全部文件列表
- 本地数据库,需要自己写导出文件的函数。
- 上次同步成功的记录,作为 baseline
把上一次成功同步的记录作为 baseline,可以检测删除的条目是哪些,不用在条目后面添加一个待删除的标记,不用污染原始数据,但是需要在本地额外存一份快照(上一次读取本地 SyncMetadata 的列表)。
interface SyncMetadata {
path: string;
hash: string;
updatedAt: number;
tags?: string[]; // 可以指定标签的数据进行上传和下载
}
Example
如果是多个本地数据库需要同步怎么办,用 tags 作区分
// 本地数据库的导入和导出
const local: LocalDatabaseAdapter = {
async getLocalManifest(): Promise<SyncMetadata[]> {
const [notebooks, entries] = await Promise.all([
getLocalManifestNotebooks(),
getLocalManifestEntries(),
])
return [...notebooks, ...entries]
},
async getRecordContent(path: string): Promise<ArrayBuffer | null> {
if (isNotebookPath(path)) {
const nb = await db.notebooks.get(getNotebookDir(path))
return enc.encode(serializeNotebook(nb)).buffer
}
if (isEntryPath(path)) {
const entry = await db.entries.get(parsed.id)
return enc.encode(serializeEntry(entry)).buffer
}
},
async upsertRecord(content: ArrayBuffer, meta: SyncMetadata): Promise<void> {
const { path, hash, updatedAt } = meta
if (isNotebookPath(path)) {
// 自己实现插入数据库的函数
setNotebookSyncHash(dir, hash)
} else if (isEntryPath(path)) {
await upsertRemoteEntry({
remotePath: path,
notebookDir: getNotebookDir(path),
content: dec.decode(content),
hash,
updatedAt,
})
}
},
async deleteRecordPermanently(path: string): Promise<void> {
if (isNotebookPath(path)) {
await deleteNotebook(dir)
setNotebookSyncHash(dir, '')
} else if (isEntryPath(path)) {
const parsed = parseEntryFilename(path)
if (parsed) await db.entries.delete(parsed.id)
}
}
}
// 配置远程权限配置
const remote = createGitHubAdapter({ owner, repo, branch, token, basePath})
const syncer = createSyncer({ local, remote})
// Pull all entries for this notebook on next sync
// diff 会返回3个队列,上传,下载,还有冲突队列
const diff = await syncer.prepare({ tags: [dirName] })
await syncer.commit(diff)
npm发布-csync-浏览器数据库同步-三方对比
转载或引用本文时请遵守许可协议,注明出处、不得用于商业用途!