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)
CC

npm发布-csync-浏览器数据库同步-三方对比

本文作者
Huxzhi
发布于
更新于
许可协议
CC BY-NC-SA 4.0

转载或引用本文时请遵守许可协议,注明出处、不得用于商业用途!