2017-12-07 10 views
2

Kürzlich habe ich ein Problem gelöst, bei dem eine große Anzahl von Schlüsselwerten aktualisiert wurde.Speicherstatus in Elixir

Natürlich dachte ich über eine Map, mit Operationen wie Map.put/3.

dies schien jedoch unzureichend, die unveränderliche Natur der Datenstrukturen in Elixir gegeben:

iex> m = Map.put(%{}, :a, 1) 
%{a: 1} 
iex> Map.put(m, :b, 2) 
%{a: 1, b: 2} 
iex> m 
%{a: 1} 

Ich löste dann das Problem durch den Zustand der Map in einem GenServer halten und es mit handle_cast/3 Anrufen zu aktualisieren.

Allgemein, ist dies der richtige Ansatz, oder war das hier zu viel?

Antwort

6

Ich löste dann das Problem durch den Zustand der Map in einem GenServer halten [...] Im Allgemeinen ist dies der richtige Ansatz, oder war das hier zu viel?

Es hängt stark von Ihrem Ziel ab. Es gibt viele verschiedene Möglichkeiten, den Zustand zu speichern. Rebinding Variablen wie:

m = Map.put(%{}, :a, 1) 
#⇒ %{a: 1} 
m = Map.put(m, :b, 2) 
#⇒ %{a: 1, b: 2} 

Does not store nichts. Es bindet die lokale Variable m an RHO, und sobald der Kontrollfluss den Bereich verlässt, wird diese Variable zu Garbage Collection. Ob Sie die oben genannte Karte in einem einzigen Bereich benötigen, GenServer (und andere Statusinhaber) ist ein Overkill.


OTOH, wenn Sie benötigen, um den Zustand für eine lange Zeit zu speichern und zwischen den verschiedenen Bereichen teilen (z. B.. Zwischen verschiedenen Prozessen) GenServer ist der einfachste Weg, das zu erreichen. In Elixir haben wir Agent Modul, um die Boilerplate für GenServer, die als eine einfache In-Memory-Speicher verwendet wird, zu verringern, aber mein Rat wäre, immer GenServer zu verwenden: früher oder später Agent wird zu eng für Ihre Zwecke werden.

Auch könnte man ets Modul verwenden, um speicherinternen Schlüssel-Wert-Speicher zu halten, der zwischen Prozessen geteilt wird.

dets ist eine Möglichkeit, den Zustand zwischen Prozess Neustarts zu speichern.

Und schließlich mnesia ist ein OTP nativen Ansatz den Zustand zwischen beiden Neustarts und verschiedenen Knoten zu teilen (in verteilten Umgebungen.)

2

Ihre erste Annäherung war richtig, Sie haben nur eine Sache falsch.

Sie sollten die Variable erneut binden, wenn Sie die Karte aktualisieren, wie hier:

iex> m = Map.put(%{}, :a, 1) 
%{a: 1} 
iex> m = Map.put(m, :b, 2) 
%{a: 1, b: 2} 
iex> m 
%{a: 1, b: 2} 

Aber du musst hier undestand, dass es nicht die Variable nicht mutieren, erstellt es eine neue Karte und erneut bindet es an den gleichen Variable.

Jetzt ist dieser Ansatz der einfachste und Sie müssten diese Karte an jede Funktion übergeben, die sie verwendet. Alternativ können Sie das Modul Agent verwenden. Alle Informationen, was es ist und wofür es verwendet wird, finden Sie in seiner Dokumentation.