Czy przechowywanie plików w Cassandra to dobry pomysł?

Czy przechowywanie plików w Cassandra to dobry pomysł?
SI
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 70
0

Jest taka architektura:

  • aplikacja Spring Boot (wiele instancji, wiele serwerów)
  • baza danych ScyllaDB (Cassandra przepisana na C++)
  • load balancer (kieruje ruch na losowy serwer z aplikacją)

Potrzebuję przechowywać duże pliki, w tym upload z możliwością wznawiania i download.

Jednym ze sposobów jest podzielenie plików na małe fragmenty:

Kopiuj
CREATE TABLE IF NOT EXISTS files
(
  id          UUID,
  file_name   TEXT STATIC,
  size_bytes  INT STATIC,
  chunk_no    UUID,
  data        BLOB,
  PRIMARY KEY (id, chunk_no)
);

Jednak nasuwają się pewne wątpliwości:

  1. Kompaktowanie w Cassandrze/Scylli może doprowadzić do gwałtownego wzrostu zajmowanego miejsca.

  2. Czy nie dojdzie do zablokowania wątków, jeśli aplikacja Spring Boot będzie pośredniczyć zarówno przy uploadzie jak i przy downloadzie plików? Algorytm pobierania działałby tak:

  • policz liczbę kawałków na podstawie rozmiaru pliku
  • dla każdego pozostałego kawałka (to może trwać bardzo długo):
    • pobierz kawałek z bazy
    • wrzuć go do odpowiedzi (w Springu prawdopodobnie przez StreamingResponseBody)

Jeśli mamy 10 wątków i zablokujemy w ten sposób wszystkie wątki, to możemy doprowadzić, że aplikacja przestanie być dostępna dla innych użytkowników?

Jakie są jeszcze inne wady takiego rozwiązania?

Jak powinno się prawidłowo zrealizować rozproszony system plików? Czy istnieją gotowe rozwiązania open source? Istotne jest też, aby dało się ograniczyć dostęp do plików na podstawie informacji zapisanych w bazie danych (czyli sprawdzamy, czy użytkownik, który chce pobrać plik, ma dostęp do elementów powiązanych tym plikiem).

S4
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 1268
0

A nie lepiej jakiś CDN?

Charles_Ray
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 1914
2

Dlaczego chcesz pliki trzymać w bazie, a nie w blob storage np. S3/GCS albo jakis wlasny serwer plików?

Odnosnie watkow - operacje dlugotrwajace powinny byc procesowane na dedykowanych pulach watkow. Moze w ogole powinny byc do tego dedykowane serwisy albo instancje?

SI
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 70
0

To jakie serwery polecacie (do postawienia lokalnie, a nie w chmurze jak S3/GCS)? I jak wygląda kontrola dostępu? Zalogowany użytkownik, który ma dostęp do obiektu powiązanego z tym plikiem, powinien móc go pobrać, ale inna osoba już nie. Najlepiej by to zobaczyć na przykładzie.

A co do wątków, być może przejście na Spring Reactor rozwiązałoby ten problem,

NoSQL-owe przechowalnie plików to żadna nowość, istnieje trochę takich rozwiązań, lecz nie zawsze to jest najlepszy pomysł, zwłaszcza w przypadku Cassandry lub ScyllaDB - ktoś pisał o tym pracę dyplomową i jedną z pułapek było właśnie kompaktowanie, a druga jak mi się zdaje, to będzie z wątkami w Javie (trzeba by w jakiś sposób dać większy priorytet normalnemu ruchowi, a nie obsłudze plików), ale to musiałbym sprawdzić.

Może macie jeszcze inne pomysły?

TR
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: 700m n.p.m.
  • Postów: 681
1

Strumieniowaniem i wysyłaniem plików to powinien się zajmować serwer www, bo do tego jest wyspecjalizowany, a nie wątek w Javie. Tego nie zrobisz w ten sposób dobrze, za to jest to dobry sposób na zabicie serwera.

Polecam do tego serwer nginx i szukanie w internecie: "nginx streaming", "nginx sendfile", a same pliki radzę przechowywać po prostu na dysku, w bazie danych jedynie ścieżki do nich.

Jedyny problem to zbyt duża ilość plików w jednym katalogu - zaczyna spadać czas dostępu i robią się problemy z indeksami systemu plików.

Dlatego trzeba opracować algorytm tworzenia podkatalogów o głębokim zagłębieniu - np. w oparciu o numeryczne id pliku w bazie lub crc64 nazwy pliku.

Ja mam taki algorytm opracowany, nie tworzy więcej niż x plików w jednym katalogu i y podkatalogów w danym katalogu, natomiast zagłębienie jest teoretycznie nieskończone w oparciu o numeryczny id. Działa doskonale przy kilkuset tysiącach plików jakie muszę obsługiwać, które zajmują łącznie kilkadziesiąt GB.

SI
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 70
0

Na wejściu jest Envoy Proxy. Sprawdzę, czy da się go wykorzystać do pobierania plików, a jak nie, to za nim mogę postawić serwer HTTP (np. Nginx / Apache). Ale Tomcat też umie w streaming. Kwestia odpowiedniej konfiguracji Spring Boot? Czy to nie ma dużego znaczenia, czy żądania będą przechodzić przez aplikację (np. FilesController), czy z pominięciem aplikacji i będzie podobnie szybko / wolno? Pliki byłyby przechowywane w wolumenie Dockera, który byłby podpięty do poszczególnych instancji aplikacji.

Pierwsza funkcjonalność - załączniki dodawane do postów - jest już zaimplementowana w ten sposób, że w bazie trzymamy metadane, natomiast plik jest trzymany w systemie plików. Pliki są przesyłanie w częściach, bo jak się okazało, proxy na domenie limituje żądania do 2 MB. Pewnie w celu ochrony przed DDoS. Trzeba jeszcze dorobić obsługę HTTP Range Stream na potrzeby audio / wideo. Klient wcześniej upierał się, by trzymać pliki w ScyllaDB, jest to gdzieś stosowane, ale większość odradza, bo baza może spuchnąć i będzie problem ze skalowaniem. Tak naprawdę to nie chciało mi się pisać mechanizmu strumieniowania z bazy i do bazy. Za bardzo nie wiedziałem, jak do tego podejść. Teraz wiem i bym napisał, ale chyba nie powinniśmy w to iść.

Jeśli chodzi o pobieranie załączników, to działa to mniej więcej tak:

  • uderz do /posts/id_postu/zalacznik/id_zalacznika
  • sprawdź, czy użytkownik może wyświetlić post
  • pobierz metadane pliku z bazy
  • zwróć uchwyt do pliku

Ale oprócz załączników będą też inne pliki przesyłane przez użytkowników (np. awatary, obraz tytułowy postu), które najprościej trzymać jako base64 w bazie, ale to też nie jest najlepszy pomysł, choć dla programisty wygodny.

Tak więc dla pozostałych plików opcje są 3:

  • zostawić jak jest i trzymać base64 w bazie
  • stworzyć prosty serwer plików w Springu z kontrolą uprawnień
  • serwer CDN

Oczywiście można zlać sprawdzanie uprawnień, ale jeśli to wyjdzie, to można iść w pasiaki, bo to mogą być dane wrażliwe.

Czy ktoś ma doświadczenie z serwerem CDN? Od razu odpadają wszelkie płatne rozwiązania. Klient nie zapłaci Bezosowi za Amazon S3. Można co najwyżej postawić lokalnie jakiś serwer plików, jeśli to odciąży całą aplikację i przyspieszy pracę z plikami. Ale jak tam dodać kontrolę uprawnień?

Minęły 3 lata i projekt dalej w powijakach. Trzeba to popchnąć do przodu.

Edit: z wideo dopiero będzie jazda, bo konwersja do różnych formatów, generowanie miniaturki, ech, może są już gotowe rozwiązania, bo po co wynajdować koło na nowo? Myślę, jak przechowywać różne wersje wideo / obrazów / audio. Pewnie w bazie metadane w kolekcji lub wide table (bo używamy ScyllaDB) zapisywać poszczególne wersje, a w systemie plików (lub w serwerze plików) osobne pliki. I teraz w URL-u podawać, jaki format chcemy. Zobaczcie, jak to działa np. w CDN na Wykopie (nie wiem, jakiego CDN-a wykorzystują).

Mbappe_koksik
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 74
0

Raczej unika się trzymania plików w bazie danych ze względu na wiele ograniczeń. Pliki najlepiej trzymać po prostu w S3 albo EFS. Nie będę się zagłębiać nawet głębiej bo trzymanie plików w bazie to po prostu facepalm, który mógł wymyślić chyba Junior. Nawet AI to odradza.

loza_prowizoryczna
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 1628
0
Mbappe_koksik napisał(a):

Raczej unika się trzymania plików w bazie danych ze względu na wiele ograniczeń. Pliki najlepiej trzymać po prostu w S3 albo EFS. Nie będę się zagłębiać nawet głębiej bo trzymanie plików w bazie to po prostu facepalm, który mógł wymyślić chyba Junior. Nawet AI to odradza.

ReFS w fundamentach to w zasadzie jedna wielka baza danych.

Mbappe_koksik
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 74
0
Shiba Inu napisał(a):

Czy ktoś ma doświadczenie z serwerem CDN? Od razu odpadają wszelkie płatne rozwiązania. Klient nie zapłaci Bezosowi za Amazon S3. Można co najwyżej postawić lokalnie jakiś serwer plików, jeśli to odciąży całą aplikację i przyspieszy pracę z plikami. Ale jak tam dodać kontrolę uprawnień?

Ogólnie popularne CDN'y działają 'nad' Kubernetesem. Jeżeli chodzi o statyczne treści to powinieneś użyć CDN'a i jak chcesz to zrobić z Kubernetesem to tutaj np. masz przykładowe rozwiązanie.

https://thelinuxnotes.com/index.php/how-to-deploy-and-configure-simple-cdn-server-in-kubernetes/

SL
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 1029
SI
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 70
0

Na razie i tak nie mamy Kubernetesa, tylko Dockera. Na weekendzie obczaję powyższe CDNy.

A może ktoś wytłumaczyć, jak inni realizują kontrolę dostępu, jeśli używają CDN-a? Podobno używa się podpisanych URL-i lub ciasteczek, ale to chyba nie do końca zamyka temat. Na przykład jest plik wideo, który może pobrać właściciel oraz osoby / grupy, które mają dostęp do jego folderu. Trzeba by generować osobny URL dla każdego użytkownika (i pilnować, by po odebraniu dostępu URL-e straciły ważność) i weryfikować użytkownika na podstawie tokenu JWT. Zdaje się, że powszechną praktyką jest generowanie tymczasowych URL-i (bodajże tak robi FB) bez kontroli dostępu (czyli jeśli wyślesz link koledze, to zadziała, nawet jeśli kolega nie powinien mieć dostępu do zasobu). A na Naszej Klasie kontrola dostępu była ścisła, ale nie pamiętam, jak były zbudowane URL-e i czy używali CDN.

Bez CDN-a przy każdym dostępie sprawdzałbym uprawnienia. Ciekawe, czy przy streamingu wideo przy każdym chunku będzie sprawdzanie uprawnień, ale chyba nie, bo serwer powinien zapamiętać sobie Resource, a jeśli tak, to zrobi się jakiś cache.

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.