Warning: strpos() [function.strpos]: Empty needle in /home/dealer/kasat/pkg/cahir/web/html/lib/plugins/translation2/action.php on line 53

Warning: Cannot modify header information - headers already sent by (output started at /home/dealer/kasat/pkg/cahir/web/html/lib/plugins/translation2/action.php:53) in /home/dealer/kasat/pkg/cahir/web/html/inc/actions.php on line 163
erlang:database:ets_dets [Krystian Bacławski Wiki]
 

ETS - Erlang Term Storage

Wprowadzenie

Dynamiczne tabele składujące krotki.

Tabela:

  • tworzona przez proces,
  • posiada prawa dostępu,
  • nie jest odśmiecana (jako całość, natomiast rekordy po modyfikacji są),
  • automatycznie niszczona kiedy proces (właściciel) umiera.

Można transferować prawa własności.

Typy tabeli:

  • set – nie mogą być dwie krotki z tym samym kluczem,
  • ordered_set – j.w. przy czym zapewnia się leksykograficzny porządek trawersowania,
  • bag – dopuszcza się dwie różne krotki z takim samym kluczem,
  • duplicate_bag – dopuszcza się duplikaty krotek.

Operacje na poszczególnych typach tabel działają w stałym czasie (tablice mieszające), za wyjątkiem ordered_set gdzie działają w czasie logarytmicznym (drzewa AVL).

Wsparcie dla współbieżności:

  • operacje aktualizacji są:
    • atomowe – operacja wykonuje się w całości lub w ogóle,
    • izolowane – pośrednie wyniki nie są widoczne dla innych.

FIXME Różnice między == i =:=!

Tabele

Opcje

Uprawnienia:

  • public – do odczytu i zapisu dla wszystkich,
  • protected – do odczytu dla wszystkich, do zapisu tylko dla właściciela,
  • private – do odczytu i zapisu tylko dla właściciela.

Reszta:

  • named_table – w trakcie tworzenia tabela zostanie zarejestrowana pod nazwą (przekazaną jako pierwszy argument ets:new/2),
  • {keypos,Pos} – które z pól rekordu zostanie użyte jako klucz,
  • {heir,Pid,HeirData} | {heir,none} – ustawia proces, który odziedziczy prawo własności do tabeli, jeśli oryginalny proces zakończy działanie,
  • {write_concurrency,bool()} – domyślnie ustawione na false, oznacza to że tabela jest zoptymalizowana pod kątem współbieżnego odczytu; gdy true tabela jest zoptymalizowana pod kątem operacji modyfikujących.

Po utworzeniu tabeli można zmieniać tylko jedną opcję hier przy pomocy poniższego wywołania:

ets:setopts(Tab, Options)

Tworzenie, usuwanie, zmiana nazwy

ets:new(Name, Options) -> tid() | atom()

Domyślnie opcje dla tworzenia tabeli to: protected, set, {keypos, 1}.

Tabele nazwane i nienazwane

> TabId = ets:new(tabela, [named_table, bag]).
tabela
> ets:info(TabId).
[{memory,316},
 {owner,<0.37.0>},
 {heir,none},
 {name,tabela},
 {size,0},
 {node,nonode@nohost},
 {named_table,true},
 {type,bag},
 {keypos,1},
 {protection,protected}]
> TabId2 = ets:new(tabela2, []).
16401
> ets:info(TabId2).
[{memory,316},
 {owner,<0.37.0>},
 {heir,none},
 {name,tabela2},
 {size,0},
 {node,nonode@nohost},
 {named_table,false},
 {type,set},
 {keypos,1},
 {protection,protected}]

Pozostałe operacje administratorskie na tabelach:

ets:delete(Tab) -> true
ets:rename(Tab, Name) -> Name

Spis tabel

Mamy kilka użytecznych funkcji, którymi można sobie poprzeglądać tabele:

ets:all() -> [Tab]
ets:i() -> ok
ets:i(Tab) -> ok
ets:info(Tab) -> [{Item, Value}] | undefined
ets:info(Tab, Item) -> Value | undefined

Przykłady

> ets:i().
 id              name              type  size   mem      owner
 ----------------------------------------------------------------------------
 ...
 16401           tabela2           set   0      316      <0.37.0>
 tabela          tabela            bag   0      316      <0.37.0>
 ...
ok
> ets:all().
[16401,tabela,...]

Jeśli eksperymentujecie z tabelami ets w konsoli to należy pamiętać, że w przypadku błędu użytkownika, proces do którego należała tabela kończy swoje działanie! Należy usunąć symbole związane z tabelami funkcją f() i utworzyć jeszcze raz wszystkie tabele.

Przekazywanie prawa własności

Możemy przekazać prawo własności do tabeli Tab do procesu Pid. Jeśli dodatkowe dane są potrzebne, to można je przesłać jako GiftData. Nowy właściciel musi być procesem lokalnym, a proces wołający musi być oczywiście właścicielem tabeli.

ets:give_away(Tab, Pid, GiftData) -> true

Jeśli przekazanie praw własności się powiodło to nowy właściciel dostanie do skrzynki pocztowej następujący komunikat:

{'ETS-TRANSFER',Tab,FromPid,GiftData}

Uwaga: Przekazanie praw własności nie zmienia parametru hier.

Operacje na rekordach

Wprowadzenie

Moduł do testów

-module(ets_test).
-export([db/0]).
-record(emp, {empno,     % Employee number as a string, the key
              surname,   % Surname of the employee
              givenname, % Given name of employee
              dept,      % Department one of {dev,sales,prod,adm}
              empyear}). % Year the employee was employed
 
data() -> [
        {emp,"011103","Black","Alfred",sales,2000},
        {emp,"041231","Doe","John",prod,2001},
        {emp,"052341","Smith","John",dev,1997},
        {emp,"076324","Smith","Ella",sales,1995},
        {emp,"122334","Weston","Anna",prod,2002},
        {emp,"535216","Chalker","Samuel",adm,1998},
        {emp,"789789","Harrysson","Joe",adm,1996},
        {emp,"963721","Scott","Juliana",dev,2003},
        {emp,"989891","Brown","Gabriel",prod,1999}].
 
db() ->
    ets:new( emp_tab, [{keypos, #emp.empno}, named_table, ordered_set]),
    ets:insert( emp_tab, data() ).

Praca z powłoki

> c(ets_test).
{ok,ets_test}
> rr(ets_test).
[emp]
> ets_test:db().
ok
> ets:i(emp_tab).
<1   > {emp,"011103","Black","Alfred",sales,2000}
<2   > {emp,"041231","Doe","John",prod,2001}
<3   > {emp,"052341","Smith","John",dev,1997}
<4   > {emp,"076324","Smith","Ella",sales,1995}
<5   > {emp,"122334","Weston","Anna",prod,2002}
<6   > {emp,"535216","Chalker","Samuel",adm,1998}
<7   > {emp,"789789","Harrysson","Joe",adm,1996}
<8   > {emp,"963721","Scott","Juliana",dev,2003}
<9   > {emp,"989891","Brown","Gabriel",prod,1999}
EOT  (q)uit(p)Digits(k)ill /Regexp -->p4
{emp,"076324","Smith","Ella",sales,1995}
EOT  (q)uit(p)Digits(k)ill /Regexp -->/adm
<1   > {emp,"535216","Chalker","Samuel",adm,1998}
<2   > {emp,"789789","Harrysson","Joe",adm,1996}
EOT  (q)uit(p)Digits(k)ill /Regexp -->k
% Tabela zostaje zniszczona

Wstawianie

ets:insert(Tab, ObjectOrObjects) -> true
ets:insert_new(Tab, ObjectOrObjects) -> true | false

Funkcja ets:insert/2 nadpisuje obiekty z takim samym kluczem, ets:insert_new/2 wstawia do tabeli element o ile się on tam nie znajduje.

Przykłady wstawiania elementów

> ets:insert(emp_tab, #emp{empno="000666",surname="Insane",givenname="Developer",dept=dev,empyear=2012}).
true
> ets:i(emp_tab).
...
EOT  (q)uit(p)Digits(k)ill /Regexp -->/666
<1   > {emp,"000666","Insane","Developer",dev,2012}
...
> ets:insert(emp_tab, #emp{empno="000666",surname="Insane",givenname="Developer",dept=dev,empyear=2038}).
true % insert umożliwia nadpisywanie
> ets:i(emp_tab).
...
EOT  (q)uit(p)Digits(k)ill /Regexp -->/666
<1   > {emp,"000666","Insane","Developer",dev,2038}
EOT  (q)uit(p)Digits(k)ill /Regexp -->q
ok
> ets:insert_new(emp_tab, #emp{empno="000666",surname="Insane",givenname="Developer",dept=dev,empyear=2002}).
false

Wyszukiwanie

... na podstawie klucza

ets:lookup(Tab, Key) -> [Object]
ets:lookup_element(Tab, Key, Pos) -> Object | [Object]
ets:member(Tab, Key) -> true | false

Przykłady wyszukiwania

> ets:lookup(emp_tab,"052341").
[#emp{empno = "052341",surname = "Smith",givenname = "John", dept = dev, empyear = 1997}]
> ets:lookup_element(emp_tab,"052341",#emp.givenname).
"John"
> ets:member(emp_tab,"42").
false
> ets:member(emp_tab,"052341").
true

... przy pomocy wzorca

FIXME ets:repair_continuation/2 – potrzebne w implementacji Mnesii!

ets:match(Tab, Pattern) -> [Match]
ets:match(Tab, Pattern, Limit) -> {[Match],Continuation} | '$end_of_table'
ets:match(Continuation) -> {[Match],Continuation} | '$end_of_table'
ets:match_object(Tab, Pattern) -> [Object]
ets:match_object(Tab, Pattern, Limit) -> {[Match],Continuation} | '$end_of_table'
ets:match_object(Continuation) -> {[Match],Continuation} | '$end_of_table'

Funkcja:

  • ets:match zwraca tylko te pola rekordu, które zostały związane ze specjalnymi atomami postaci '$<int>'. Odpowiada to $$ z akcji zapytań dopasowujących.
  • ets:match_object zwraca zawsze całe rekordy co odpowiada '$_' z akcji zapytań dopasowujących.

Parametr Limit oznacza ile maksymalnie rekordów chcemy pobrać w jednym wywołaniu. Jeśli rekordów jest więcej dostaniemy obiekt kontynuacji, którym możemy wznowić wyszukiwanie od momentu, w którym zostało przerwane.

Przykład

> ets:match(emp_tab, {'_', '_', '$2', '$1', sales, '_'}).
[["Alfred","Black"],["Ella","Smith"]]
 
> ets:match_object(emp_tab, {emp, '_', '$2', '$1', sales, '_'}).
[#emp{empno = "011103",surname = "Black",givenname = "Alfred",dept = sales,empyear = 2000},
 #emp{empno = "076324",surname = "Smith",givenname = "Ella",dept = sales,empyear = 1995}]
 
> {Match1,Cont1}=ets:match(emp_tab, {emp, '_', '$2', '$1', '_', '_'}, 5).
{[["Alfred","Black"],
  ["John","Doe"],
  ["John","Smith"],
  ["Ella","Smith"],
  ["Anna","Weston"]],
 {emp_tab,"122334",[],5,<<>>,[],0,0}}
 
> {Match2,Cont2}=ets:match(Cont1).
{[["Samuel","Chalker"],
  ["Joe","Harrysson"],
  ["Juliana","Scott"],
  ["Gabriel","Brown"]],
 '$end_of_table'}

Trawersowanie

ets:first(Tab) -> Key | '$end_of_table'
ets:last(Tab) -> Key | '$end_of_table'
ets:next(Tab, Key1) -> Key2 | '$end_of_table'
ets:prev(Tab, Key1) -> Key2 | '$end_of_table'
ets:foldl(Function, Acc0, Tab) -> Acc1
ets:foldr(Function, Acc0, Tab) -> Acc1

Trawersowanie nie jest domyślnie operacją bezpieczną dla nieuporządkowanych tabel (nie dotyczy ordered_set). Pamiętajcie, że w trakcie trwania przeglądania tabeli, może się ona zmieniać!

  1. Uchwyt do bieżącego klucza może się zdeaktualizować!
  2. Pierwszy / ostatni element mogą się zmienić!

Aby się uchronić przed zmianami w trakcie treawersowania należy użyć ets:safe_fixtable/2!

Funkcja ets:safe_fixtable/2 obniża wydajność operacji na tabeli! Należy jej używać na krótko i dbać, żeby proces odmroził tabelę. Proces, który się zakończy, automatycznie odmraża tabelę.

ets:safe_fixtable(Tab, true|false) -> true

Zamrażanie jest operacją rekursywną – założenie blokady N razy wymaga zwolnienia blokady N razy.

Kiedy tabela jest zamrożona, sekwencja operacji ets:first/1 i ets:next/2 zwróci każdy obiekt w tabeli dokładnie raz, nawet jeśli w trakcie przeglądania dodawano lub usuwano elementy. Klucz nowo wstawionego obiektu może zostać zwrócony przez ets:next/2, ale zależy to od wewnętrznego porządku tabeli. Natomiast elementy usunięte nie są w rzeczywistości usuwane z tabeli, dopóki proces jej nie odmrozi.

Przykłady trawersowania

> First = ets:first(emp_tab).
"000666"
> Last = ets:last(emp_tab).
"989891"
> ets:next(emp_tab, First).
"011103"
> ets:prev(emp_tab, Last).
"963721"
> ets:prev(emp_tab, First).
'$end_of_table'
> ets:next(emp_tab, Last).
'$end_of_table'

Aktualizacja pól

... dowolnych

Można aktualizować jedno lub więcej pól rekordu:

ets:update_element(Tab, Key, {Pos,Value}) -> true | false
ets:update_element(Tab, Key, [{Pos,Value}]) -> true | false

Atom false jest zwracany jeśli nie udało się odnaleźć klucza. Funkcja rzuci wyjątkiem badarg jeśli:

  • tabela nie jest typu set lub ordered_set,
  • Pos nie wskazuje na pozycję w rekordzie,
  • próbujemy zaktualizować klucz.

Przykład ets:update_element/3

> ets:update_element(emp_tab, "052341", [{#emp.surname, "Brown"}, {#emp.givenname, "David"}]).
true
> ets:lookup(emp_tab, "052341").
[#emp{empno = "052341",surname = "Brown",
      givenname = "David",dept = dev,empyear = 1997}]

... całkowitoliczbowych

Można bardzo szybko aktualizować pola, które są liczbami całkowitymi:

ets:update_counter(Tab, Key, UpdateOp) -> Result
ets:update_counter(Tab, Key, [UpdateOp]) -> [Result]
ets:update_counter(Tab, Key, Incr) -> Result          % aktualizuje wartość pola znajdującego się zaraz za kluczem!

Jeśli wiele operacji dotyczy tego samego pola, będą wykonywane po kolei. Aktualizacja rekordu jest operacją atomową.

Specyfikacja aktualizacji rekordu:

UpdateOp = {Pos,Incr} | {Pos,Incr,Threshold,SetValue}

Jeśli podano drugi wariant i wartość pola po operacji wychodzi poza zasięg tj.:

  • Incr >= 0 i wynik większy od Threshold
  • Incr < 0 i wynik mniejszy od Threshold

polu zostaje przypisana wartość SetValue.

Funkcja rzuci wyjątkiem badarg jeśli – patrz ets:update_element/3 i dodatkowo:

  • nie istnieje obiekt z podanym kluczem,
  • aktualizowany element nie jest całkowitoliczbowy,
  • którekolwiek z Pos, Incr, Threshold lub SetValue nie są całkowitoliczbowe.

Przykład ets:update_counter/3

> ets:update_counter(emp_tab,"052341",{#emp.empyear,-1}).
1996
> ets:update_counter(emp_tab,"052341",{#emp.empyear,30,2012,2011}).
2011

Usuwanie

...ogólne

ets:delete(Tab, Key) -> true
ets:delete_all_objects(Tab) -> true
ets:delete_object(Tab, Object) -> true

Funkcja ets:delete_object/2 przydaje się w przypadku tabel typu bag i duplicate_bag.

Przykłady usuwania

> ets:member(emp_tab, "052341").
true
> ets:delete(emp_tab, "052341").
true
> ets:member(emp_tab, "052341").
false
> ets:member(emp_tab, "000042").
false
> ets:delete(emp_tab, "000042").
true

...na podstawie wzorca

ets:match_delete(Tab, Pattern) -> true

Przykład usuwania wielu rekordów na podstawie wzorca

> ets:info(emp_tab,size).
9
> ets:match_delete(emp_tab, {'_', '_', '_', '_', dev, '_'}).
true
> ets:info(emp_tab,size).
7

Operacje z użyciem zapytań dopasowujących

Zapytania dopasowujące

Pseudo-funkcja, konwertująca uproszczoną funkcję do zapytania dopasowującego:

ets:fun2ms(LiteralFun) -> MatchSpec

Czemu pseudo-funkcja? Musi działać w trakcie kompilacji i wymaga dołączenia następującej linii do pliku w którym jest wywoływana:

-include_lib("stdlib/include/ms_transform.hrl").

Przy pomocy funkcji ms_transform:parse_transform/2 tłumaczy ciąg znakowy LiteralFun będący zapisem uproszczonej funkcji do zapytania dopasowującego.

Pseudo-unkcja ets:fun2ms/1 w sposób bezpośredni może być wywoływana tylko z powłoki erlangowej. W programie nie dołączającym pliku ms_transform.hrl po nakarmieniu zmienną z funkcją ets:fun2ms/1 rzuci wyjątkiem badarg!

FIXME A co ze skompilowanymi zapytaniami dopasowującymi?

Do testowania naszego zapytania dopasowującego możemy użyć następującej funkcji.

ets:test_ms(Tuple, MatchSpec) -> {ok, Result} | {error, Errors}

Przykład wykorzystania ets:test_ms/2

> A = [{{'$1','$2'},[{'>','$1','$2'}],['$1']}]
 
> ets:test_ms({1,2}, A).
{ok,false}
 
> ets:test_ms({b,a}, A).
{ok,b}

Selekcja na podstawie zapytania

ets:select(Tab, MatchSpec) -> [Match]
ets:select(Tab, MatchSpec, Limit) -> {[Match],Continuation} | '$end_of_table'
ets:select(Continuation) -> {[Match],Continuation} | '$end_of_table'

Parametry Limit i Continuation mają podobne znaczenie jak w przypadku funkcji ets:match.

Przykłady wykorzystania ets:select/2

> ets:select(emp_tab,[{#emp{empno = '$1', dept = sales, surname='$2', _='_'},[],['$$']}]).
[["011103","Black"],["076324","Smith"]]
 
> ets:select(emp_tab, ets:fun2ms( fun(#emp{empno = E, dept = sales, surname = S}) -> [E,S] end)).
[["011103","Black"],["076324","Smith"]]
 
> ets:select(emp_tab, [{#emp{empno = '$1', empyear = '$2', _='_'},
                       [{'<', '$2', 2000}],
                       ['$_']}]).
[#emp{empno = "052341",surname = "Smith",givenname = "John",dept = dev,empyear = 1997},
 #emp{empno = "076324",surname = "Smith",givenname = "Ella",dept = sales,empyear = 1995},
 #emp{empno = "535216",surname = "Chalker",givenname = "Samuel",dept = adm,empyear = 1998},
 #emp{empno = "789789",surname = "Harrysson",givenname = "Joe",dept = adm,empyear = 1996},
 #emp{empno = "989891",surname = "Brown",givenname = "Gabriel",dept = prod,empyear = 1999}]
 
> ets:select(emp_tab, ets:fun2ms(
                      fun(#emp{empno = E, surname = "Smith" }) ->
                              {guru,E};
                         (#emp{empno = E, empyear = Y}) when Y < 1997  ->
                              {inventory, E};
                         (#emp{empno = E, empyear = Y}) when Y > 2001  ->
                              {newbie, E};
                         (#emp{empno = E, empyear = Y}) ->
                              {rookie, E}
                      end)).
[{rookie,"011103"},{rookie,"041231"},{guru,"052341"},{guru,"076324"},{newbie,"122334"},
 {rookie,"535216"},{inventory,"789789"},{newbie,"963721"},{rookie,"989891"}]

Usuwanie dopasowanych rekordów

ets:select_delete(Tab, MatchSpec) -> NumDeleted

Uwaga: Żeby obiekt został usunięty zapytanie dopasowujące musi zwrócić atom true!

Przykład użycia ets:select_delete

> ets:info(emp_tab, size).
9
 
> MS = ets:fun2ms( fun(#emp{dept = dev}) -> true end).
[{#emp{empno = '_',surname = '_',givenname = '_',dept = dev, empyear = '_'},
  [],
  [true]}]
 
> ets:select_delete(emp_tab, MS).
2
 
> ets:info(emp_tab, size).
7
 
> ets:tab2list(emp_tab).
[#emp{empno = "011103",surname = "Black",givenname = "Alfred",dept = sales,empyear = 2000},
 #emp{empno = "041231",surname = "Doe",givenname = "John",dept = prod,empyear = 2001},
 #emp{empno = "076324",surname = "Smith",givenname = "Ella",dept = sales,empyear = 1995},
 #emp{empno = "122334",surname = "Weston",givenname = "Anna",dept = prod,empyear = 2002},
 #emp{empno = "535216",surname = "Chalker",givenname = "Samuel",dept = adm,empyear = 1998},
 #emp{empno = "789789",surname = "Harrysson",givenname = "Joe",dept = adm,empyear = 1996},
 #emp{empno = "989891",surname = "Brown",givenname = "Gabriel",dept = prod,empyear = 1999}]

Zliczanie dopasowanych rekordów

ets:select_count(Tab, MatchSpec) -> NumMatched

Uwaga jak w przypadku ets:select_delete/2 – zapytanie dopasowujące musi zwrócić true, żeby obiekt został zliczony.

Przykłady wykorzystanie ets:select_count/2

> ets:fun2ms( fun(#emp{dept = dev}) -> true end).
[{#emp{empno = '_',surname = '_',givenname = '_',dept = dev, empyear = '_'},
  [],
  [true]}]
 
> ets:select_count(emp_tab, ets:fun2ms( fun(#emp{dept = dev}) -> true end)).
2
 
> ets:select_count(emp_tab, ets:fun2ms( fun(#emp{dept = dev} = Obj) -> Obj end)).
0

Konwersje

Kopia dyskowa

Żeby zrzucić całą tabelę do pliku należy się posłużyć następującymi funkcjami:

ets:tab2file(Tab, Filename) -> ok | {error,Reason}
ets:tab2file(Tab, Filename, [Option]) -> ok | {error,Reason}

Jedyną dostępną opcją jest {extended_info,[md5sum]}, która zapisuje sumę kontrolną do pliku celem jej weryfikacji przy wczytywaniu tabeli.

Następnie możemy sprawdzić informacje o tabeli znajdującej się w postaci binarnej na dysku:

ets:tabfile_info(Filename) -> {ok, TableInfo} | {error, Reason}

Możemy również spróbować wczytać tabelę z dysku do pamięci:

ets:file2tab(Filename) -> {ok,Tab} | {error,Reason}
ets:file2tab(Filename, [Option]) -> {ok,Tab} | {error,Reason}

Jedyną obsługiwaną opcją jest {verify,true}, która powoduje sprawdzenie sumy kontrolnej pliku, jeśli takowa jest dostępna.

Uwaga: Jeśli zrzucaliśmy nazwaną tabelę i takowa istnieje w pamięci dostaniemy błąd {error,cannot_create_table}.

Przykłady zapisywania i odczytywania tabeli z dysku

> ets:tab2file(emp_tab, "tabela.ets", [{extended_info,[md5sum]}]).
ok
 
> ets:tabfile_info("tabela.ets").
{ok,[{name,emp_tab},
     {type,ordered_set},
     {protection,protected},
     {named_table,true},
     {keypos,2},
     {size,9},
     {extended_info,[md5sum]},
     {version,{1,0}}]}
 
> ets:delete(emp_tab).
true
 
> ets:file2tab("tabela.ets", [{verify,true}]).
{ok,emp_tab}
 
> ets:info(emp_tab).
[{memory,539},
 {owner,<0.106.0>},
 {heir,none},
 {name,emp_tab},
 {size,9},
 {node,nonode@nohost},
 {named_table,true},
 {type,ordered_set},
 {keypos,2},
 {protection,protected}]

Tabele DETS

Poniższych funkcji można używać by kopiować dane między bazami ETS i DETS:

ets:from_dets(Tab, DetsTab) -> true
ets:to_dets(Tab, DetsTab) -> Tab

Przy kopiowaniu

  • DETSETS – docelowa tabela nie jest czyszczona. Ewentualnie rekordy zostają nadpisane.
  • ETSDETS – docelowa tabela zostaje najpierw wyczyszczona!

Listy

ets:tab2list(Tab) -> [Object]

Przykład

> ets:tab2list(emp_tab).
[#emp{empno = "011103",surname = "Black",givenname = "Alfred",dept = sales,empyear = 2000},
 #emp{empno = "041231",surname = "Doe",givenname = "John",dept = prod,empyear = 2001},
 #emp{empno = "052341",surname = "Smith",givenname = "John",dept = dev,empyear = 1997},
 #emp{empno = "076324",surname = "Smith",givenname = "Ella",dept = sales,empyear = 1995},
 #emp{empno = "122334",surname = "Weston",givenname = "Anna",dept = prod,empyear = 2002},
 #emp{empno = "535216",surname = "Chalker",givenname = "Samuel",dept = adm,empyear = 1998},
 #emp{empno = "789789",surname = "Harrysson",givenname = "Joe",dept = adm,empyear = 1996},
 #emp{empno = "963721",surname = "Scott",givenname = "Juliana",dept = dev,empyear = 2003},
 #emp{empno = "989891",surname = "Brown",givenname = "Gabriel",dept = prod,empyear = 1999}]

Źródła

Moduły:

  • ets - składowanie wyrażeń erlangowych w pamięci RAM

DETS - Disk Erlang Term Storage

Wprowadzenie

DETS w odróżnieniu do ETS przechowuje termy Erlangowe na dysku zamiast w pamięci. Niestety implementacja nie jest pełna!

Tabele DETS:

  1. Nie mogą mieć więcej niż 2GB. Jeśli chcecie więcej należy użyć modułu mnesia_fragmentation!
  2. Nie implementują typu ordered_set!
  3. Wywołanie dets:safe_fixtable/2 nie działa. Trawersowanie w trakcie zapytań modyfikujących może nie być bezpieczne!

Szczegóły implementacji:

  • w pamięci operacyjnej Buddy Systems,
  • na dysku tablica z liniową funkcją mieszającą.

Sloty – ilość różnych kluczy, które jest w stanie pomieścić tabela.

Tabele

Otwieranie

dets:open_file(Name, Args) -> {ok, Name} | {error, Reason}

Opcje:

  • {access, read | read_write} – prawa dostępu do bazy (domyślnie read_write),
  • {auto_save, 180000} – okres (w milisekundach) synchronizacji struktur w pamięci operacyjnej z dyskiem,
  • {file, Path} – ścieżka do pliku,
  • {max_no_slots, Num} – maksymalna ilość dostępnych kluczy (domyślnie $$2^20$$, maksymalnie $$2^25$$); im większe tym potencjalnie większa fragmentacja i wolniejszy dostęp;
  • {min_no_slots, Num} – minimalna ilość dostępnych kluczy,
  • {keypos, Num} – pozycja klucza w przechowywanych krotkach,
  • {ram_file, true | false} – czy tabela ma być dodatkowo trzymana w pamięci operacyjnej dla przyśpieszenia operacji (domyślnie false),
  • {repair, true | false | force} – reakcja na uszkodzoną tabelę w trakcie otwierania; false w przypadku uszkodzonej tabeli powoduje zwrócenie błędu {error, {needs_repair, FileName}}; true – domyślne; force – uruchamia procedurę naprawiania nawet, jeśli nie wykryto uszkodzenia tabeli;
  • {type, set | bag | duplicate_bag} – typ tabeli.

W przypadku, gdy chcemy zdefragmentować tabelę należy ją otworzyć z opcją {repair,force}!

Przykład

> dets:open_file(emp_tab_disk, [{file, "emp_tab.dets"},{keypos,2}]).
{ok,emp_tab_disk}
> dets:from_ets(emp_tab_disk,emp_tab).
ok
> dets:info(emp_tab_disk).
[{type,set},
 {keypos,2},
 {size,9},
 {file_size,6008},
 {filename,"emp_tab.dets"}]
> dets:is_dets_file("emp_tab.dets").
true

Informacje

Informacje nt. tabeli możemy wyciągnąć jeśli jest otwarta. Pierwsza wersja podaje nam skróconą wersję (patrz wyżej).

ets:info(Name) -> InfoList | undefined
ets:info(Name, Item) -> Value | undefined

Gdzie Name jest atomem: access, auto_save, bchunk_format, hash, memory, no_keys, no_objects, no_slots, owner, ram_file, safe_fixed, version.

> lists:map(fun(X) -> io:format("~p: ~p~n", [ X, dets:info(emp_tab_disk,X) ]) end,
            [access, auto_save, bchunk_format, hash, memory, no_keys, no_objects, no_slots, owner, ram_file, safe_fixed, version]).
 
access: read_write
auto_save: 180000
bchunk_format: <<131,104,...>>
hash: phash2
memory: 6392
no_keys: 9
no_objects: 9
no_slots: {256,256,2097152}
owner: <0.72.0>
ram_file: false
safe_fixed: false
version: 9

Trawersowanie

Przy pomocy poniższej funkcji możemy łatwo trawersować (w nieustalonym porządku) tabelę DETS:

dets:traverse(Name, TraverseFun) -> Return | {error, Reason}

Powyższa funkcja przyjmuje funkcjonał TraverseFun następującej postaci:

TraverseFun = fun(Object) -> continue | {continue, Val} | {done, Value}

TraverseFun musi zwracać jeden z poniższych wyników, inaczej działanie dets:traverse/2 się zakończy.

  • continue – idź do następnego elementu,
  • {continue, Value} – idź do następnego elementu, Value dodaj do akumulatora,
  • {done, Value} – zakończ, dodaj Value do akumulatora.

Funkcja dets:traverse/2 zwraca akumulator (listę obiektów).

Przykład

> dets:traverse( emp_tab_disk, fun(X) -> io:format("~s ~s~n", [X#emp.surname, X#emp.givenname]), continue end).
Smith John
Weston Anna
Chalker Samuel
Brown Gabriel
Black Alfred
Scott Juliana
Smith Ella
Harrysson Joe
Doe John
[] % pusty akumulator

Synchronizacja i zamykanie

Synchronizacja powoduje zapisanie wszystkich danych przechowywanych w pamięci na dysk.

dets:sync(Name) -> ok | {error, Reason}

Jeśli tabela jest zsynchronizowana i nastąpi awaria systemu, to jej otwarcie nie będzie wymagało naprawy.

Wszystkie otwarte tabele muszą zostać explicite zamknięte przed zakończeniem działania systemu erlangowego.

dets:close(Name) -> ok | {error, Reason}

W przeciwnym wypadku w trakcie otwierania tabeli, moduł dets automatycznie zacznie ją naprawiać. Jeśli tabela jest duża, to wiemy czego się spodziewać ;-)

Źródła

Moduły:

  • dets - składowanie wyrażeń erlangowych na dysku
 
erlang/database/ets_dets.txt · Last modified: 2010/06/01 11:37 by Krystian Bacławski
 
Except where otherwise noted, content on this wiki is licensed under the following license:CC Attribution-Noncommercial-No Derivative Works 3.0 Unported
Recent changes RSS feed Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki