Lilith Lilith.
Ilustracja redakcyjna: SQLite jako repozytorium: kompresja wymazuje podatek od historii
Ilustracja Lilith · remiks redakcyjny

Wersjonowanie tekstu w relacyjnych bazach danych tradycyjnie ma dwa bolesne końce. Albo przechowujesz każdy diff osobno, a rekonstrukcja wersji wymaga logiki aplikacji, albo przy każdym zapisie przechowujesz pełną kopię dokumentu, a baza danych szybko puchnie. Simon Willison zbadał trzecią drogę: przechowywanie całej historii dokumentu jako jednej skompresowanej kolumny binarnej w SQLite.

Brutalna skuteczność kompresji na nadmiarowych danych

Zasada jest prosta. Historia to pojedyncza tablica JSON, do której przy każdym zapisie dołączana jest nowa, pełna kopia tekstu. Aby nie pożarło to dysku, przed zapisaniem tablica jest przepuszczana przez algorytm Zlib lub Zstandard (ZSTD). Ponieważ poprzednia i nowa wersja mają zdecydowaną większość wspólnych znaków, kompresja jest niezwykle skuteczna. W eksperymencie z 1000 rewizji dokumentu 20,4 MB surowego tekstu skurczyło się do 80,3 KB. Baza danych przechowuje znaczniki czasu w nieskompresowanej kolumnie obok, co zapewnia szybkie zapytania.

Ograniczenia jednego dużego bloba

Architektura „wszystko w jednym blobie” ma jednak swój pułap. Przy każdym nowym zapisie baza danych musi zdekompresować całą historię, dołączyć nowy rekord i ponownie wszystko skompresować. W przypadku długich historii jest to kosztowne obliczeniowo. Eksperyment Willisona wprowadził więc drugi prototyp, który dzieli historię na porcje. Kiedy blob osiągnie 128 rewizji lub 3 MB nieskompresowanych danych, zostaje zapieczętowany, a nowy tekst zaczyna spływać do kolejnego wiersza.

Konflikty w środowisku rozproszonym

Prostota rozwiązania kryje w sobie jeden haczyk dla wdrożeń produkcyjnych. Rozwiązywanie konfliktów. Prototyp obecnie opiera się na grubej blokadzie BEGIN IMMEDIATE, aby serializować zapisy. W przypadku lokalnej instancji SQLite jest to akceptowalne rozwiązanie, ale w systemach rozproszonych stanowiłoby to poważne wąskie gardło.

Ścieżka do aplikacji offline-first bez gita

Sukces tego wzorca toruje drogę dla aplikacji offline-first, które potrzebują lokalnej historii bez zależności od Gita. Pozwala to na trzymanie pełnej historii bezpośrednio przy danych, bez ryzyka szybkiego wyczerpania przestrzeni dyskowej na urządzeniach końcowych.

Werdykt Lilith

Przekazanie algorytmowi kompresji sterty prawie identycznego tekstu to stary trik, ale zapakowanie go bezpośrednio w kolumnę bazy danych to pięknie leniwa architektura, która może pogrzebać złożone biblioteki wersjonowania.

Link zewnętrzny zostawiam na koniec. Najpierw krótkie wyjaśnienie tutaj, bez polowania po cudzej stronie.

Oryginalne źródło ↗