Menggunakan git repository sebagai backend database

119

Saya melakukan proyek yang berhubungan dengan database dokumen terstruktur. Saya memiliki pohon kategori (~ 1000 kategori, hingga ~ 50 kategori di setiap level), setiap kategori berisi beberapa ribu (hingga, katakanlah, ~ 10.000) dokumen terstruktur. Setiap dokumen adalah beberapa kilobyte data dalam beberapa bentuk terstruktur (saya lebih suka YAML, tapi mungkin juga JSON atau XML).

Pengguna sistem ini melakukan beberapa jenis operasi:

  • mengambil dokumen-dokumen ini dengan ID
  • mencari dokumen dengan beberapa atribut terstruktur di dalamnya
  • mengedit dokumen (yaitu menambah / menghapus / mengganti nama / menggabungkan); setiap operasi pengeditan harus dicatat sebagai transaksi dengan beberapa komentar
  • melihat riwayat perubahan yang direkam untuk dokumen tertentu (termasuk melihat siapa, kapan dan mengapa mengubah dokumen, mendapatkan versi sebelumnya - dan mungkin kembali ke versi ini jika diminta)

Tentu saja, solusi tradisional akan menggunakan semacam database dokumen (seperti CouchDB atau Mongo) untuk masalah ini - namun, kontrol versi (riwayat) ini menggoda saya untuk ide liar - mengapa saya tidak boleh menggunakan gitrepositori sebagai database backend untuk aplikasi ini?

Sekilas bisa diatasi seperti ini:

  • kategori = direktori, dokumen = file
  • mendapatkan dokumen dengan ID => mengubah direktori + membaca file dalam copy pekerjaan
  • mengedit dokumen dengan edit komentar => membuat komit oleh berbagai pengguna + menyimpan pesan komit
  • riwayat => log git normal dan pengambilan transaksi lama
  • search => itu bagian yang sedikit rumit, saya kira itu akan membutuhkan ekspor berkala dari kategori ke database relasional dengan pengindeksan kolom yang akan kami izinkan untuk mencari

Apakah ada kendala umum lainnya dalam solusi ini? Adakah yang pernah mencoba untuk mengimplementasikan backend seperti itu (yaitu untuk kerangka kerja populer - RoR, node.js, Django, CakePHP)? Apakah solusi ini memiliki implikasi yang mungkin terjadi pada kinerja atau keandalan - misalnya, apakah terbukti bahwa git akan jauh lebih lambat daripada solusi database tradisional atau akan ada masalah skalabilitas / keandalan? Saya berasumsi bahwa sekelompok server seperti itu yang mendorong / menarik repositori satu sama lain harus cukup kuat & dapat diandalkan.

Pada dasarnya, beri tahu saya apakah solusi ini akan berhasil dan mengapa akan berhasil atau tidak?

GreyCat
sumber
silakan lihat youtube.com/watch?v=nPPlyjMlQ34
Assaf S.

Jawaban:

58

Menjawab pertanyaan saya sendiri bukanlah hal terbaik untuk dilakukan, tetapi, karena saya akhirnya membatalkan idenya, saya ingin berbagi alasan yang berhasil dalam kasus saya. Saya ingin menekankan bahwa alasan ini mungkin tidak berlaku untuk semua kasus, jadi terserah arsitek untuk memutuskan.

Secara umum, poin utama pertama yang terlewatkan oleh pertanyaan saya adalah bahwa saya berurusan dengan sistem multi-pengguna yang bekerja secara paralel, secara bersamaan, menggunakan server saya dengan klien tipis (yaitu hanya browser web). Dengan cara ini, saya harus menjaga status mereka semua. Ada beberapa pendekatan untuk yang satu ini, tetapi semuanya terlalu sulit pada sumber daya atau terlalu rumit untuk diterapkan (dan dengan demikian mematikan tujuan asli dari melepas semua hal implementasi yang sulit ke git di tempat pertama):

  • Pendekatan "Blunt": 1 pengguna = 1 status = 1 copy pekerjaan lengkap dari repositori yang dipelihara server untuk pengguna. Bahkan jika kita berbicara tentang database dokumen yang cukup kecil (misalnya, 100s MiBs) dengan ~ 100K pengguna, mempertahankan klon repositori penuh untuk semuanya membuat penggunaan disk berjalan melalui atap (yaitu 100K pengguna dikalikan 100MiB ~ 10 TiB) . Yang lebih buruk, mengkloning repositori 100 MiB setiap kali membutuhkan waktu beberapa detik, bahkan jika dilakukan dengan cara yang cukup efektif (yaitu tidak menggunakan oleh git dan membongkar-mengemas ulang barang), yang tidak dapat diterima, IMO. Dan lebih buruk lagi - setiap pengeditan yang kita terapkan ke pohon utama harus ditarik ke setiap repositori pengguna, yang merupakan (1) sumber daya, (2) dapat menyebabkan konflik pengeditan yang tidak terselesaikan dalam kasus umum.

    Pada dasarnya, ini mungkin seburuk O (jumlah pengeditan × data × jumlah pengguna) dalam hal penggunaan disk, dan penggunaan disk seperti itu secara otomatis berarti penggunaan CPU yang cukup tinggi.

  • Pendekatan "Hanya pengguna aktif": pertahankan copy pekerjaan hanya untuk pengguna aktif. Dengan cara ini, Anda biasanya tidak menyimpan repo-klon-per-pengguna penuh, tetapi:

    • Saat pengguna masuk, Anda menggandakan repositori. Dibutuhkan beberapa detik dan ~ 100 MiB ruang disk per pengguna aktif.
    • Saat pengguna terus bekerja di situs, dia bekerja dengan copy pekerjaan yang diberikan.
    • Saat pengguna keluar, klon repositori akan disalin kembali ke repositori utama sebagai cabang, sehingga hanya menyimpan "perubahan yang belum diterapkan", jika ada, yang cukup hemat ruang.

    Dengan demikian, penggunaan disk dalam hal ini mencapai puncaknya pada O (jumlah pengeditan × data × jumlah pengguna aktif), yang biasanya ~ 100..1000 kali lebih sedikit dari jumlah total pengguna, tetapi ini membuat proses masuk / keluar menjadi lebih rumit dan lebih lambat. , karena ini melibatkan kloning cabang per pengguna pada setiap login dan menarik perubahan ini kembali saat logout atau berakhirnya sesi (yang harus dilakukan secara transaksional => menambahkan lapisan kompleksitas lainnya). Dalam jumlah absolut, itu menurunkan 10 TiB penggunaan disk menjadi 10..100 GiB dalam kasus saya, itu mungkin dapat diterima, tetapi, sekali lagi, kita sekarang berbicara tentang database yang cukup kecil dari 100 MiB.

  • Pendekatan "pembayaran jarang": membuat "pembayaran jarang" daripada klon repo lengkap per pengguna aktif tidak banyak membantu. Ini mungkin menghemat ~ 10x penggunaan ruang disk, tetapi dengan mengorbankan beban CPU / disk yang jauh lebih tinggi pada operasi yang melibatkan riwayat, yang jenisnya membunuh tujuan.

  • Pendekatan "kumpulan pekerja": alih-alih melakukan klon lengkap setiap kali untuk orang yang aktif, kami mungkin menyimpan kumpulan klon "pekerja", siap digunakan. Dengan cara ini, setiap kali pengguna log in, dia menempati satu "pekerja", menarik cabangnya dari repo utama, dan, saat dia logout, dia membebaskan "pekerja", yang melakukan hard reset git pintar untuk menjadi lagi hanya klon repo utama, siap digunakan oleh pengguna lain yang masuk. Tidak banyak membantu dengan penggunaan disk (ini masih cukup tinggi - hanya klon penuh per pengguna aktif), tetapi setidaknya itu membuat log in / out lebih cepat, sebagai biaya bahkan lebih kompleks.

Karena itu, perhatikan bahwa saya dengan sengaja menghitung jumlah basis data dan basis pengguna yang cukup kecil: 100 ribu pengguna, 1 ribu pengguna aktif, total basis data 100 MiB + riwayat pengeditan, 10 MiB salinan pekerjaan. Jika Anda melihat proyek crowd-sourcing yang lebih menonjol, ada angka yang jauh lebih tinggi di sana:

│              │ Users │ Active users │ DB+edits │ DB only │
├──────────────┼───────┼──────────────┼──────────┼─────────┤
│ MusicBrainz  │  1.2M │     1K/week  │   30 GiB │  20 GiB │
│ en.wikipedia │ 21.5M │   133K/month │    3 TiB │  44 GiB │
│ OSM          │  1.7M │    21K/month │  726 GiB │ 480 GiB │

Jelas, untuk sejumlah data / aktivitas itu, pendekatan ini sama sekali tidak dapat diterima.

Secara umum, ini akan berhasil, jika seseorang dapat menggunakan browser web sebagai klien "tebal", yaitu mengeluarkan operasi git dan menyimpan cukup banyak pembayaran penuh di sisi klien, bukan di sisi server.

Ada juga poin lain yang saya lewatkan, tetapi itu tidak terlalu buruk dibandingkan dengan yang pertama:

  • Pola dari status edit pengguna yang "tebal" kontroversial dalam hal ORM normal, seperti ActiveRecord, Hibernate, DataMapper, Tower, dll.
  • Sejauh yang saya cari, tidak ada basis kode gratis yang ada untuk melakukan pendekatan itu ke git dari kerangka kerja populer.
  • Setidaknya ada satu layanan yang entah bagaimana berhasil melakukannya secara efisien - itu jelas github - tetapi, sayangnya, basis kode mereka adalah sumber tertutup dan saya sangat curiga bahwa mereka tidak menggunakan server git biasa / teknik penyimpanan repo di dalamnya, yaitu mereka pada dasarnya diimplementasikan alternatif "data besar" git.

Jadi, intinya : itu adalah mungkin, tetapi bagi kebanyakan usecases saat itu tidak akan berada di dekat solusi optimal. Menggulung implementasi dokumen-edit-sejarah-ke-SQL Anda sendiri atau mencoba menggunakan database dokumen yang ada mungkin akan menjadi alternatif yang lebih baik.

GreyCat
sumber
16
Mungkin agak terlambat ke pesta, tetapi saya memiliki persyaratan yang mirip dengan ini dan benar-benar mengikuti jalan pintas. Setelah menggali beberapa bagian dalam git, saya menemukan cara untuk membuatnya bekerja. Idenya adalah bekerja dengan repositori kosong. Ada beberapa kekurangannya, tapi menurut saya ini bisa diterapkan. Saya telah menulis semuanya di pos yang mungkin ingin Anda periksa (jika ada, demi kepentingan): kenneth-truyers.net/2016/10/13/git-nosql-database
Kenneth
Alasan lain saya tidak melakukan ini adalah kemampuan kueri. Penyimpanan dokumen sering kali mengindeks dokumen, sehingga memudahkan pencarian di dalamnya. Ini tidak akan langsung terjadi dengan git.
FrankyHollywood
12

Memang pendekatan yang menarik. Saya akan mengatakan bahwa jika Anda perlu menyimpan data, gunakan database, bukan repositori kode sumber, yang dirancang untuk tugas yang sangat spesifik. Jika Anda bisa menggunakan Git out-of-the-box, maka tidak masalah, tapi Anda mungkin perlu membangun layer repositori dokumen di atasnya. Jadi, Anda juga bisa membangunnya di atas database tradisional, bukan? Dan jika yang Anda minati adalah kontrol versi bawaan, mengapa tidak menggunakan salah satu alat repositori dokumen sumber terbuka saja ? Ada banyak pilihan.

Nah, jika Anda memutuskan untuk tetap menggunakan Git backend, maka pada dasarnya itu akan berfungsi untuk kebutuhan Anda jika Anda menerapkannya seperti yang dijelaskan. Tapi:

1) Anda menyebutkan "sekelompok server yang mendorong / menarik satu sama lain" - Saya sudah memikirkannya beberapa lama dan masih belum yakin. Anda tidak dapat mendorong / menarik beberapa repo sebagai operasi atom. Saya ingin tahu apakah ada kemungkinan beberapa kekacauan penggabungan selama pekerjaan bersamaan.

2) Mungkin Anda tidak membutuhkannya, tetapi fungsionalitas yang jelas dari repositori dokumen yang tidak Anda daftarkan adalah kontrol akses. Anda mungkin dapat membatasi akses ke beberapa jalur (= kategori) melalui submodul, tetapi mungkin Anda tidak dapat memberikan akses pada tingkat dokumen dengan mudah.

Kombajn zbożowy
sumber
11

senilai 2 pence saya. Agak rindu tapi ...... Saya memiliki persyaratan serupa di salah satu proyek inkubasi saya. Mirip dengan milik Anda, persyaratan utama saya di mana database dokumen (xml dalam kasus saya), dengan versi dokumen. Itu untuk sistem multi-pengguna dengan banyak kasus penggunaan kolaborasi. Preferensi saya adalah menggunakan solusi sumber terbuka yang tersedia yang mendukung sebagian besar persyaratan utama.

Singkatnya, saya tidak dapat menemukan satu produk pun yang menyediakan keduanya, dengan cara yang cukup skalabel (jumlah pengguna, volume penggunaan, penyimpanan, dan sumber daya komputasi). Saya cenderung pada git untuk semua kemampuan yang menjanjikan, dan (kemungkinan) solusi yang bisa dibuat dari itu. Saat saya lebih banyak bermain-main dengan opsi git, berpindah dari satu perspektif pengguna ke perspektif multi (mili) pengguna menjadi tantangan yang jelas. Sayangnya, saya tidak dapat melakukan analisis kinerja substansial seperti yang Anda lakukan. (.. malas / berhenti lebih awal .... untuk versi 2, mantra) Kekuatan untuk Anda !. Bagaimanapun, ide bias saya sejak itu berubah menjadi alternatif berikutnya (masih bias): mesh-up alat yang terbaik di bidang terpisah, database dan kontrol versi.

Sementara masih bekerja dalam proses (... dan sedikit diabaikan) versi yang diubah hanyalah ini.

  • di frontend: (userfacing) menggunakan database untuk penyimpanan tingkat 1 (berinteraksi dengan aplikasi pengguna)
  • di backend, gunakan sistem kontrol versi (VCS) (seperti git) untuk melakukan pembuatan versi objek data dalam database

Intinya, ini sama saja dengan menambahkan plugin kontrol versi ke database, dengan beberapa lem integrasi, yang mungkin harus Anda kembangkan, tetapi mungkin jauh lebih mudah.

Cara kerjanya (seharusnya) adalah pertukaran data antarmuka multi-pengguna utama dilakukan melalui database. DBMS akan menangani semua masalah menyenangkan dan kompleks seperti multi-pengguna, konkurensi e, operasi atom, dll. Pada backend, VCS akan melakukan kontrol versi pada satu set objek data (tidak ada masalah konkurensi, atau masalah multi-pengguna). Untuk setiap transaksi yang efektif pada database, kontrol versi hanya dilakukan pada catatan data yang secara efektif akan berubah.

Adapun lem interfacing, akan berupa fungsi interworking sederhana antara database dan VCS. Dalam hal desain, sebagai pendekatan sederhana akan menjadi antarmuka yang digerakkan oleh peristiwa, dengan pembaruan data dari database yang memicu prosedur kontrol versi (petunjuk: dengan asumsi Mysql, penggunaan pemicu dan sys_exec () bla bla ...). Dalam hal kompleksitas implementasi, ini akan berkisar dari yang sederhana dan efektif (misalnya pembuatan skrip) hingga yang kompleks dan indah (beberapa antarmuka konektor terprogram). Semua tergantung pada seberapa gila Anda ingin melakukannya, dan berapa banyak modal keringat yang bersedia Anda keluarkan. Saya rasa skrip sederhana harus melakukan keajaiban. Dan untuk mengakses hasil akhir, berbagai versi data, alternatif sederhana adalah mengisi klon database (lebih merupakan tiruan dari struktur database) dengan data yang direferensikan oleh tag versi / id / hash di VCS. lagi-lagi bit ini akan menjadi tugas kueri / terjemahkan / peta sederhana dari sebuah antarmuka.

Masih ada beberapa tantangan dan hal yang tidak diketahui yang harus dihadapi, tetapi saya kira dampaknya, dan relevansi dari sebagian besar masalah ini akan sangat bergantung pada persyaratan aplikasi dan kasus penggunaan Anda. Beberapa mungkin hanya menjadi bukan masalah. Beberapa masalah termasuk kecocokan kinerja antara 2 modul kunci, database dan VCS, untuk aplikasi dengan aktivitas pembaruan data frekuensi tinggi, Penskalaan sumber daya (penyimpanan dan daya pemrosesan) dari waktu ke waktu di sisi git sebagai data, dan pengguna tumbuh: mantap, eksponensial atau akhirnya dataran tinggi

Dari koktail di atas, inilah yang sedang saya buat

  • menggunakan Git untuk VCS (awalnya dianggap CVS lama yang baik karena hanya menggunakan perubahan atau delta antara 2 versi)
  • menggunakan mysql (karena sifat data saya yang sangat terstruktur, xml dengan skema xml yang ketat)
  • bermain-main dengan MongoDB (untuk mencoba database NoSQl, yang sangat cocok dengan struktur database asli yang digunakan di git)

Beberapa fakta menyenangkan - git sebenarnya melakukan hal-hal yang jelas untuk mengoptimalkan penyimpanan, seperti kompresi, dan penyimpanan hanya delta di antara revisi objek - YA, git hanya menyimpan perubahan atau delta antara revisi objek data, di mana itu berlaku (tahu kapan dan bagaimana) . Referensi: file paket, jauh di dalam inti internal Git - Review penyimpanan objek git (sistem file beralamat konten), menunjukkan kesamaan yang mencolok (dari perspektif konsep) dengan database noSQL seperti mongoDB. Sekali lagi, dengan mengorbankan modal kerja, ini mungkin memberikan kemungkinan yang lebih menarik untuk mengintegrasikan 2, dan penyesuaian kinerja

Jika Anda sampai sejauh ini, izinkan saya jika hal di atas mungkin berlaku untuk kasus Anda, dan dengan asumsi demikian, bagaimana hal itu akan disesuaikan dengan beberapa aspek dalam analisis kinerja komprehensif terakhir Anda.

chisango muda
sumber
4

Saya menerapkan pustaka Ruby di atasnya libgit2yang membuatnya cukup mudah untuk diterapkan dan dijelajahi. Ada beberapa batasan yang jelas, tetapi ini juga merupakan sistem yang cukup membebaskan karena Anda mendapatkan toolchain git lengkap.

Dokumentasi mencakup beberapa ide tentang kinerja, pengorbanan, dll.

ioquatix.dll
sumber
2

Seperti yang Anda sebutkan, kasus multi-pengguna agak lebih rumit untuk ditangani. Salah satu solusi yang mungkin adalah menggunakan file indeks Git khusus pengguna yang menghasilkan

  • tidak perlu copy pekerjaan terpisah (penggunaan disk dibatasi untuk file yang diubah)
  • tidak perlu pekerjaan persiapan yang memakan waktu (per sesi pengguna)

Triknya adalah dengan menggabungkan GIT_INDEX_FILEvariabel lingkungan Git dengan alat untuk membuat Git komit secara manual:

Garis solusi mengikuti (hash SHA1 aktual dihilangkan dari perintah):

# Initialize the index
# N.B. Use the commit hash since refs might changed during the session.
$ GIT_INDEX_FILE=user_index_file git reset --hard <starting_commit_hash>

#
# Change data and save it to `changed_file`
#

# Save changed data to the Git object database. Returns a SHA1 hash to the blob.
$ cat changed_file | git hash-object -t blob -w --stdin
da39a3ee5e6b4b0d3255bfef95601890afd80709

# Add the changed file (using the object hash) to the user-specific index
# N.B. When adding new files, --add is required
$ GIT_INDEX_FILE=user_index_file git update-index --cacheinfo 100644 <changed_data_hash> path/to/the/changed_file

# Write the index to the object db. Returns a SHA1 hash to the tree object
$ GIT_INDEX_FILE=user_index_file git write-tree
8ea32f8432d9d4fa9f9b2b602ec7ee6c90aa2d53

# Create a commit from the tree. Returns a SHA1 hash to the commit object
# N.B. Parent commit should the same commit as in the first phase.
$ echo "User X updated their data" | git commit-tree <new_tree_hash> -p <starting_commit_hash>
3f8c225835e64314f5da40e6a568ff894886b952

# Create a ref to the new commit
git update-ref refs/heads/users/user_x_change_y <new_commit_hash>

Bergantung pada data Anda, Anda dapat menggunakan tugas cron untuk menggabungkan ref baru mastertetapi resolusi konflik bisa dibilang bagian tersulit di sini.

Ide untuk membuatnya lebih mudah dipersilakan.

7mp
sumber
Itu umumnya merupakan pendekatan yang tidak mengarah ke mana pun, kecuali jika Anda ingin memiliki konsep transaksi dan UI yang lengkap untuk resolusi konflik manual. Ide umum untuk konflik adalah membuat pengguna menyelesaikannya langsung saat melakukan (yaitu "maaf, ada orang lain yang mengedit dokumen yang sedang Anda edit -> harap lihat hasil editnya dan hasil edit Anda lalu gabungkan"). Ketika Anda mengizinkan dua pengguna untuk berhasil berkomitmen dan kemudian mengetahui dalam cronjob async bahwa semuanya berjalan ke selatan, umumnya tidak ada yang tersedia untuk menyelesaikan masalah.
GreyCat