
Roboczy opis zalozen dla poliqarp_storage.

+++++++++ definicje +++++++
kolekcja/tabela - pojedynczy zbior danych, rekordow. Cos jak tabelka w bazach SQL.
literal - wartosc wielotypowa, moze przyjmowac wartosci boolowskie, liczbowe (calkowite i zmiennoprzecinkowe), napisowe

============ dbms ===============
watek sluchajacy selectem, jak odbierze polaczenie (zaczynajace sie od tajnegokodu), to tworzy watek roboczy

watek roboczy utrzymuje polaczenie az do zakonczenia polaczenia ze strony klienta, chyba, ze ubije go timeout
++++++++++++ komendy dla watku roboczego +++++++++++
1) stworz tabele xyz i otworz w trybie zapisu
  -> pod muteksem oznaczamy te tabele jako zapis (= zwiekszamy refcount zapisujacych), jesli jest refcount odczytu > 0 to wywalamy;
  -> pliki tej tabeli otwieramy przez mmap
  -> otwarcie tabeli powoduje zaladowanie rekordu opisujacego jej strukture z pliku podstawowego i otwarcie/stworzenie pozostalych plikow kolumnowych
2) otworz juz istniejaca, przygotowana do odczytu tabele xyz w trybie odczytu
  -> pod muteksem zwiekszamy refcount odczytujacych, jesil refcount zapisu > 0 to wywalamy
  -> pliki tabeli otwieramy przez mmap
  -> otwieramy wszystkie pliki tabelkowe wynikajace ze struktury (po zaladowaniu rekordu z pliku podstawowego)
3) zamknij tabele xyz - odwoluje zapis/odczyt dla tego watku (pod muteksem zmniejszamy stosowny refcount)
  -> przy koncowym zamykaniu tabeli zapisywanej (czyli refcount spada do 0) trzeba przytrimowac rozmiar pliku do faktycznie zajmowanego
4) pobierz dane z magazynowej (payloadowej) czesci rekordow
  -> w pakiecie wyspecyfikowane warunki, ktore maja byc spelnione na kluczach.
  -> jesli pojedyncza wartosc (albo konfigurowalnie malo, np. < 1/1000 rekordow), to 
    binsearchem wybieramy odpowiednie rekordy i pakujemy razem z danymi z plikow kolumnowych i magazynowego
  -> jesli duzy zbior wartosci, regex albo <, >, to pelen skan podstawowej tabeli (i ewentualnie pelen skan plikow literalowych), pakowanie jak powyzej
  -> w pakiecie wyspecyfikowane ktore pola danych zwracamy
5) zapisz rekordy dane do tabeli (wspolbiezne wielowatkowe zapisy!)
  -> pod muteksami poszczegolnych kolumn literalowych tlumaczymy literaly na identy (dodajac w razie potrzeby nowe pozycje)
    +> zapisy w plikach kolumn literalowych sa male, wiec raczej nie zablokuja
  -> pod muteksami poszczegolnych kolumien magazynowych rezerwujemy stosowne obszary danych (bez zapisywania jeszcze; na wszystkie rekordy razem). 
    Notujemy sobie przydzielone obszary (offset w pliku i dlugosc pola) w rekordach
  -> pod muteksem tabelki (pliku podstawowego) rezerwujemy stosowny obszar danych - na wszystkie rekordy
  -> bez muteksu zapisujemy rekordy w podstawowym i dane w magazynowych
  ===> rezerowawanie obszarow w plikach musi w razie czego powiekszac plik. mmapujemy konfigurowalna ilosc MB (np. 1GB) i w razie czego munmap i mmap ponowny, o ten gigabajt wiekszy
    +> sledzimy rozmiary plikow
    +> powiekszanie pliku pod muteksem
    +> po powiekszeniu przeliczyc odpowiednie pointery, np. poczatek rekordow w pliku podstawowym 
6) przygotuj zapisane dane do odczytywania (tabela nie moze byc aktualnie odczytywana ani zapisywana, blokujemy ja calkiem)
  -> przesortowanie plikow kolumien literalowych i poprawienie identyfikatorow w rekordach w podstawowym
    +> stringi tez ulozyc w kolejnosci
  -> przesortowanie podstawowego leksykograficznie
  -> te operacje robic bezpiecznie, tzn kopiuja dane do nowego pliku zamiast nadpisywac
7) info o tym co aktywne w watku (nazwa tabeli, tryb otwarcia tabeli)
8) info o obciazeniu dbms (zarezerwowac, na razie niekonieczne)
9) info o tabeli (czy gotowa do odczytu, czy uzywana, liczba rekordow, rozmiar, schemat)
10) usun tabele
11) wylistuj wszystkie tabele


+++++++++++++ struktura plikow ++++++++++++++++++

nazwy: nazwatabeli.$ext
$ext to identyfikator pliku:
1) podstawowy: b
2) kolumna literalowa numer $n: ll$n = lista rekordow, ls$n = stringi
3) kolumna magazynowa numer $n: p$n

kolumny $n liczone od zera

plik podstawowy:
1) stalej dlugosci rekord opisujacy strukture danych (tzn kolejnosc i typ kolumn)
  -> 1B na boola, czy kolekcja jest przygotowana do odczytu (tzn posortowana)
  -> 1B na liczbe kolumn
  -> N razy po 1B na typy kolejnych kolumn
    1: bool
    2: int
    3: literal
    4: magazynowa (przechowywanie danych binarnych)
2) rekordy o sztywnej dlugosci, sztywna kolejnosc pol

pola boolowskie po 1B na pole (0x00 = false, 0xff = true)
pola integerowe po 8B na pole
pola literalowe po 4B na pole (identyfikator do rozwiniecia przez plik kolumnowy, limit wartosci implicite 4mld)
pola magazynowe: po 8B na offset + 4B na dlugosc danych - wszystko w bajtach

wszystkie integery bez znaku!

pliki kolumny literalowej:

lista rekordow: 
  rekordy stalej dlugosci, format:
    typ literalu: 1B
    dane literalu: 8B, do wyboru:
    -> string:
      offsety w pliku stringow w znakach: 4B
      dlugosc stringu w znakach: 4B
    -> integer: 8B w miejscu
    -> bool: 1B 0x00 dla false lub 0xff dla true + 7B pustego miejsca
    -> double: 8B w miejscu
  !!identyfikatorem literalu jest numer rekordu!! - przy sortowaniu stosownie jest to uaktualniane

  wszystkie integery bez znaku!

stringi:
  jednym ciagiem dane zapisane w utf32, wszystkie offsety i dlugosci zapisane w liscie rekordow

w trakcie zapisu cale dane powyzsze sa w pliku utrzymywane nieposortowane, 
ale dodatkowo mamy je w std::map w pamieci dla szybkiego dostepu
  -> std::map<literal, numer>


plik kolumny magazynowej:
dane binarne jednym ciagiem - wszystkie offsety i dlugosci zapisane w podstawowym [w bajtach]



++++++++++++ pakiety z komendami i danymi +++++++++++
1 bajt odpowiedzi serwera:
0 = OK
>0 = zle, w tym:
  1 = nie ok   <= po tym _zawsze_ string z informacja czemu
  2 = zajete, prosze czekac
  3 = zly tryb
  4 = zla wersja
  5 = nieznana tabela
  6 = tabela nieprzygotowana do odczytu

inne definicje: 
  string = 2B dlugosci + string w utf8/ascii (dla nazw plikow, opisow bledow itp)
  widestring = 4B dlugosci (w znakach) + widestring w utf32
  bool = 1B dlugosci, wartosci 0 i 0xff/-1/
  int = 8B dlugosci
  dane = 4B dlugosci (w bajtach) + dane binarne
  literal = 1B typ literalu + odpowiednio 8B int/double, 1B bool lub 4B dlugosci + (4xdlugosc)B widestring

  wszystkie integery bez znaku!

przywitanie:
klient do serwera: 16B tajny kod + 2B numer wersji
serwer odpowiada 1 bajtem odpowiedzi (OK/nie ok/zajete), jesli nie OK to zrywa polaczenie

komendy od strony klienta:
1B numer komendy
reszta stosownie:

zamknij/info-o-watku/info-o-obciazeniu/listuj-kolekcje: brak parametrow

usun/otworz-ro/przygotuj-sortuj/info o tabelce: nazwatabeli[string]

stworz-otworz-wo: nazwatabeli[string] + info o strukturze
  -> info wyglada tak samo jak rekord opisujacy strukture w pliku podstawowym

zapis-rekordow: 1B liczba rekordow + N * rekord
  -> rekord = kolejne pola (wszystkie obowiazkowe, kolejnosc sztywna jak przy tworzeniu):
    pole = bool, int, dane albo literal

wyszukiwanie: opis warunkow dla wszystkich pol kluczowych, 
  kolejnosc sztywna jak przy tworzeniu:
  -> warunek na pole:
    +> 1B typ warunku:
      0 = brak warunku = dowolna wartosc (na tym koniec pola; dla magazynowych oznacza to brak zwracania tej kolumny)
      1 = $in
      2 = $nin
      3 = <
      4 = >
      5 = <=
      6 = >=
      7 = regex
      8 = zwroc te kolumne (tylko dla magazynowych, na tym koniec pola)
    +> tresc warunku:
      $in/$nin = 4B dlugosc listy wartosci + kolejne wartosci typu zgodnego ze struktura (bool, int lub literal)
      <, >, <=, >= = pojedyncza wartosc typu zgodnego ze struktura (int lub literal)
      regex = widestring (flagi zakodowane w tresci regexa)
    +> uwagi: 
      dla typu bool legalne jest tylko $in/brak warunku
      dla typu int regex jest nielegalny




serwer zwraca:
zamknij: jednobajtowa odpowiedz (OK/nie ok)

listuj-kolkecje: jednobajtowa odpowiedz (OK/nie ok) + 4B liczba kolekcji + nazwy kolekcji [N*string]

usun/stworz/otworz-ro/przygotuj-sortuj: jednobajtowa odpowiedz (OK/nie ok/zajete/zly tryb/nieznana tabela/nieprzygotowana tabela)
  dla 'nie ok' dodatkowo string z opisem czemu

zapis-rekordow: jednobajtowa odpowiedz (OK/nie ok/zly tryb)
  dla 'nie ok' dodatkowo string z opisem czemu

wyszukiwanie: jednobajtowa odpowiedz (OK/nie ok/zly tryb) + ((jesli OK) 4B liczba rekordow + N * rekord)
  -> rekord = kolejne pola (kolejnosc jak przy tworzeniu; format jak w zapisie, 
                     pola z danymi: te, o ktore klient pytal normalnie, reszcza tak, jakby byly zerowej dlugosci)

info o watku: jednobajtowa odpowiedz (OK) + 1B tryb otwarcia + (opcjonalnie: nazwatabeli[string])
  -> tryb:
    1 = brak tabeli
    2 = zapisywanie
    3 = odczyt

info o obciazeniu: jednobajtowa odpowiedz (OK) + liczba aktywnych watkow[int] + procent wykorzystanego limitu otwartych plikow[1B] + procent zajetej pamieci[1B] + 100*sysload 1m,5m,15m[3*int]


info o tabeli: jednobajtowa odpowiedz (OK/nie ok/nieznana tabela) + ((jesli OK)czy gotowa do odczytu[bool] + czy uzywana[bool] + liczba rekordow[int] + calkowity rozmiar danych[int] + schemat[format jak przy stworz/otworz]) / ((jesli nie ok)string z opisem czemu)



serwer zamyka polaczenie workerowe po:
-> bledzie (zly tryb, nie ok)
-> timeoucie, np 1h

worker ubity przy zerwaniu albo zamknieciu polaczenia



