把git当作一个小型最终一致性的 json 数据库

这几天写了一些有趣的代码:

规格设计

把git当作 json 数据库。做法是这样的:

  1. 创建一个git仓库
  2. 为每个最小粒度的数据创建一个独立的json文件({table}.json)
  3. 客户端通过 Python写git操作代码,实现几个数据库操作接口。

数据库操作接口最小集:

  • 初始化:把git仓库拉下来(这个后面可以优化为只拉取指定json文件) git init, git fetch, git checkout branch
  • 连接:要更新的时候,同步下最新,git checkout . + git clean -df . + git pull
  • 查询:直接读取仓库内的json文件, json.load(f)
  • 更新:比对并写入仓库内json文件, json.dump(table,f,indent=4)
  • 提交:推送, git add {table}.json, git commit -m f"update {table}", git push

对于 table 的设计:

  1. 拆分足够小的粒度,使得不同table之间相对独立,避免冲突,这种隔离使得,git push可以自动合并本地和远程仓库的不同(不同文件之间无冲突可自动合并)。
  2. 同table在更新机制上只要满足最终一致性即可,因此即使同文件发生并发导致某次push失败,也会被后续的更新解决。
  3. 对于性能要求不高,只要最终一致性满足即可

使用这样的方式好处是:

  1. 使用一个 git 仓库就可以做分布式有状态功能
  2. 获得 json table 的版本化支持,通过git的日志可以非常清晰地看到不同table的版本更新,方便比对
  3. 无需引入独立的数据库

GPT编写的代码

我们把需求提给GPT,得到初版代码:

import os
import json
from git import Repo, exc

class GitJsonDatabase:
    def __init__(self, repo_path, remote_url=None, branch='master'):
        self.repo_path = repo_path
        self.remote_url = remote_url
        self.branch = branch
        self.repo = None
        
    def init_repo(self):
        """
        初始化 Git 仓库,如果远程 URL 存在,则从该远程地址克隆仓库。
        否则,在本地创建一个新的仓库。
        """
        if self.remote_url:
            self.repo = Repo.clone_from(self.remote_url, self.repo_path)
        else:
            self.repo = Repo.init(self.repo_path)
        
        # Checkout the desired branch
        try:
            self.repo.git.checkout(self.branch)
        except exc.GitCommandError:
            # If branch doesn't exist, create it.
            self.repo.git.checkout('-b', self.branch)

    def connect(self):
        """
        连接到仓库,并更新最新版本。
        """
        if self.repo is None:
            raise Exception("Repository not initialized.")
        
        # Reset local changes and clean up untracked files
        self.repo.git.checkout('--', '.')
        self.repo.git.clean('-df')
        
        # Pull latest changes from the remote
        origin = self.repo.remotes.origin
        origin.pull(self.branch)
        
    def query(self, table):
        """
        查询指定的 JSON 文件。
        """
        file_path = os.path.join(self.repo_path, f"{table}.json")
        if not os.path.exists(file_path):
            return None
        with open(file_path, 'r') as f:
            return json.load(f)
    
    def update(self, table, data):
        """
        更新指定的 JSON 文件。
        """
        file_path = os.path.join(self.repo_path, f"{table}.json")
        with open(file_path, 'w') as f:
            json.dump(data, f, indent=4)
    
    def commit(self, table, message=None):
        """
        提交更改并推送到远端。
        """
        if self.repo is None:
            raise Exception("Repository not initialized.")
        
        file_path = f"{table}.json"
        message = message or f"Update {table}"
        
        # Stage the file and commit
        self.repo.git.add(file_path)
        self.repo.index.commit(message)
        
        # Push to the remote
        origin = self.repo.remotes.origin
        origin.push(self.branch)

# 使用示例
# 创建 GitJsonDatabase 实例时需指定本地仓库路径和远程仓库 URL
db = GitJsonDatabase('/path/to/repo', 'https://github.com/user/repo.git')

# 初始化仓库(克隆或创建)
db.init_repo()

# 连接到仓库,同步最新内容
db.connect()

# 查询数据
data = db.query('my_table')

# 更新数据
data['new_key'] = 'new_value'
db.update('my_table', data)

# 提交并推送更改
db.commit('my_table', 'Added new_key to my_table')

--end--

posted @ 2024-01-30 12:39  ffl  阅读(77)  评论(0编辑  收藏  举报