把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--

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章