Gdy tworzymy pierwszą aplikację webową, która umożliwia upload plików przeważnie lądują one lokalnie w pewniej lokalizacji. Gdy druga aplikacja potrzebuje dostępu do tych plików wystarczy podać ścieżkę. Problemy zaczynają się gdy aplikacji jest kilka i rozmieszczonych na kilku serwerach. Można korzystać z sieciowych systemów plików ale to często nie jest zbyt wygodne - ciężko odpowiednio ustawić uprawnienia by pewne aplikacje miały dostęp do zapisu plików a inne nie, trzeba skonfigurować dany katalog w kilku miejscach w konfiguracji serwera WWW aby serwować pliki itp…

Jeżeli w utrzymaniu swoich aplikacji dociera się do takiego momentu to kolejne pomysły zakładają wykorzystanie bazy danych do przechowywania plików. Ale chwila googlania i już wiemy że bazy typu SQL średnio radzą sobie z przetwarzaniem tak dużych rekordów jak pliki. Kolejny etap to sprawdzenie co mogą nam zaoferować bazy NoSQL.

W tym miejscu przeczytałem wiele różnych artykułów i ostatecznie zastanawiałem się czy wybrać MongoDB czy CouchDB. Oba projekty są bardzo podobne a główną ich zaletą jest łatwość wykorzystania w istniejących aplikacjach webowych. Tutaj szczególnie CouchDB wypada bardzo dobrze bo zarządzanie i dostęp do bazy odbywa się standardowym protokołem HTTP - polecenia wydaje się POST’ami, a dane pobiera GET’ami. Dzięki takiej budowie łatwo można schować CouchDB za reverse proxy i serwować np. pliki uploadowane przez użytkowników wprost z bazy - bez dorabiania dodatkowych interfejsów. Bardzo łatwo też obsługuje się bazę z poziomu aplikacji AJAX. Więc w moim przypadku wypadło na CouchDB.

Instalacja

Instalacja na Debianie jest banalna i sprowadza się do:

apt-get install -y couchdb

TADAM! Mamy działające CouchDB. Domyślnie baza nasłuchuje na adresie 127.0.0.1 i porcie 5984.

Dodatkowo Couch posiada webowy interfejs (zwany Futon’em) zarządzający dostępny na tym samym porcie pod adresem, np. http://127.0.0.1:5984/_utils/

Optymalizacja

NODELAY

W moim przypadku CouchDB służy do przechowywania zarówno bardzo wielu małych plików, jak i kilku całkiem sporych. W przypadku bardzo małych plików CouchDB w wersji 0.11 czeka z wysłaniem odpowiedzi od razu bo być może uda się “dorzucić coś jeszcze”. Takie zachowanie może powodować opóźnienia przy pobieraniu małych plików, warto więc zmienić w pliku /etc/couchdb/local.ini taką opcję:

[httpd]
socket_options = [{nodelay, true}]

Ustawienie opcji TCP NODELAY wyłączy opóźnienie przy wysyłaniu małych plików.

Identyfikatory

Warto zastanowić się nad długością i schematem identyfikatorów dla rekordów. Domyślnie mają one 32 bajty co przy małej liczbie elementów w bazie jest mocną przesadą. Rozmiar identyfikatorów znacznie wpływa na rozmiar bazy i jej wydajność - dlatego czasem warto opracować własny schemat generowanych identyfikatorów. Przykładowo jeśli identyfikator będzie generowany z cyfr oraz dużych i małych liter to dla 3 znaków możemy wygenerować identyfikatory dla ponad 260 tys. elementów, dla 4 znaków już ponad 14 milionów co powinno wystarczyć dla średniej wielkości bazy.

Ustawienia bezpieczeństwa

Trochę mnie zaskoczyło podejście do bezpieczeństwa w bazie CouchDB - domyślnie po instalacji baza działa w trybie Admin Party, czyli każda osoba która wejdzie do zarządzania bez logowania ma uprawnienia administratora 😄

Admin Party

Mnie ten stan rzeczy nie bardzo odpowiadał więc:

  • odpalamy interfejs zarządzający - Futon i w prawym dolnym rogu szukamy tekstu: “Welcome to Admin Party! Everyone is admin. Fix this” - klikamy na “Fix this”,
  • w okienku które się pojawi podajemy login i hasło administratora.
    Dodawanie administratora

Po ustawieniu konta administratora dalsze musimy zadbać o ustawienie odpowiednich uprawnień dla każdej nowo tworzonej bazy, czyli bazy tworzą się dostępem dla wszystkich ale możemy ograniczyć np. zapis/odczyt dla pewnych grup użytkowników.

Zabezpieczenie bazy użytkowników

Po ustawieniu pierwszego użytkownika kolejna rzecz, o którą powinniśmy zadbać do dostępna dla każdego do odczytu baza użytkowników. Najlepiej gdy tylko administratorzy będą mieli do niej dostęp. By to osiągnąć:

  • w Futonie wchodzimy do bazy _users i klikamy przycisk “Security” na górze strony,
  • w okienku które się pojawi w polu Readers -> Roles (pole na samym dole) wpisujemy ["admin"]

Blokowanie dostępu do bazy _users

Od teraz tylko administratorzy mają dostęp do bazy _users.

Ustawienie bazy jako tylko do odczytu

To co chciałem osiągnąć korzystając z CouchDB to jakieś repozytorium, do którego wrzucać mogą wybrańcy a czytać wszyscy (np. taki CDN dla stronki itp) ale otrzymanie takiego rezultatu jest nieco… nieintuicyjne. By to osiągnąć:

  • w bazie którą chcemy ustawić jako tylko do odczytu wchodzimy w Security i w polu Admin Roles (drugie z góry) wpisujemy ["admin"] - to zablokuje dostęp do panelu Security i możliwości modyfikowania design dokumentów, nadal możliwe jest jednak dodawanie, modyfikowani i kasowanie dokumentów,
    Blokowanie dostępu do design dokumentów
  • by zablokować dostęp do zapisu w CouchDB trzeba wykorzystać funkcję validate_doc_update która będzie wywoływana przy każdej próbie dostępu do pojedynczego dokumentu, by z niej skorzystać tworzymy nowy pusty dokument,
  • zmieniamy pole _id dokumentu na _design/auth
  • dodajemy pole nazwane language z wartością javascript
  • dodajemy kolejne pole nazwane validate_doc_update z wartością:
function(newDoc, oldDoc, userCtx) {
    if (userCtx.roles.indexOf('_admin') !== -1) {
        return;
    } else {
        throw({forbidden: 'Nie jesteś administratorem!'});
    }
}
  • zapisujemy dokument klikając na “Save Document”
    Blokowanie dostępu do zapisu