2017-04-20 15 views
1

Ich gehe derzeit durch Elixier in Aktion und ich mache einige Refactoring meiner Todo Anwendung Code, um einen besseren Einblick in die wichtigsten Teile von OTP.Ist es in Ordnung, Logik in einem Supervisor zu haben?

Die Anwendung verwendet eine Datenbank, die einfach Daten in Dateien auf der Festplatte speichert. Um sicherzustellen, dass der Zielordner für die Datenbank existiert, wird File.mkdir_p!(db_folder) innerhalb des Datenbankprozesses aufgerufen. Der Datenbankprozess selbst verwendet eine Reihe von Worker-Prozessen, um das tatsächliche Speichern/Abrufen von Daten von der Festplatte durchzuführen.

Das Kapitel Ich bin derzeit bei der Einführung einer DIY-Prozess-Registrierung, um einen robusteren Überwachungsbaum zu implementieren, indem die Mitarbeiter sich bei der Registrierung registrieren lassen und der Datenbankprozess den Mitarbeiter mit der Registrierung durchsucht, so dass beide Parteien überwacht werden können und wird immer noch nach einem Fehler arbeiten.

Als Elixir 1.4 herauskam, las ich über das Modul Registry in den Patchnotes, also dachte ich, ich könnte die App umgestalten und das verwenden. Jetzt stellt sich heraus, dass der Datenbankprozess nicht wirklich wissen muss, aus welchem ​​Ordner die Datenbank Daten speichert. Also nahm ich den mkdir_p! Anruf von diesem Modul und dachte darüber nach, wo man es setzt. Zwei Optionen in den Sinn kommen:

  1. Die DatabaseWorker
  2. Die DatabaseWorkerSupervisor

Ich persönlich bevorzuge den zweiten Ansatz, da die ganze app gebunden ist sowieso zum Absturz zu bringen, wenn der Benutzer keine Zugriffsrechte auf die hat Persistenzordner. Aber ich bin mir nicht ganz sicher, ob es in Ordnung ist, Logik in einen Supervisor zu bringen.

Ist Logik in einen Supervisor schlechten Stil oder akzeptabel je nach Situation? Wenn es ein schlechter Stil ist, wo setze ich eine Startlogik, die ich nicht wiederholen will, wenn ein Prozess abstürzt?


Mein Supervisor-Code:

defmodule Todo.DatabaseWorkerSupervisor do 
    use Supervisor 

    def start_link(db_folder) do 
    Supervisor.start_link(__MODULE__, db_folder) 
    end 

    def init(db_folder) do 
    File.mkdir_p!(db_folder) 

    processes = 
     for worker_id <- 1..3 do 
     worker(Todo.DatabaseWorker, [db_folder, worker_id], id: {:dbworker, worker_id}) 
     end 

    supervise(processes, strategy: :one_for_one) 
    end 
end 

Antwort

2

Wo setze ich die Startup-Logik, die ich nicht wiederholen will, wenn ein Prozess abstürzt?

Dies vom Supervisor init aufrufen scheint wie der logische Ort. Vor allem, wenn Sie nicht möchten, dass es wiederholt wird.

Vielleicht könnte der Code in einem anderen Modul definiert werden, aber es ist sinnvoll, ihn von der Supervisor-Init aufzurufen und den Supervisor zum Absturz zu bringen, wenn die Initialisierung fehlschlägt.

+0

Ich wählte diese Antwort, da sie direkt die ursprüngliche Frage beantwortet. Beachten Sie jedoch, dass [diese Antwort] (http://stackoverflow.com/a/43519122/4050456) sehr sinnvoll ist, wenn die Datenbank auch ein GenServer sein soll. –

2

würde ich es vorziehen, nicht Logik in der Aufsicht zu stellen. Wenn Sie anfangen, Logik in Supervisors zu setzen, können Sie nicht über Abstürze/Neustarts von Ihrem Überwachungsbaum schauen. Stattdessen werde ich die folgende Aufsicht Baum vorschlagen:

suggested supervision tree

Wenn Sie den Ordner Schöpfung im init des DB-GEN-Server setzen, wird die DBSupervisor warten, die init zurückzukehren, bevor seine anderen Kinder weitergehen . Wenn die Erstellung des Ordners fehlschlägt, wird der Rest des Überwachungsbaums nicht einmal erscheinen. Wenn die DBSupervisor-Strategie :rest_for_all ist, startet ein Fehler im DB-Gen-Server den Rest der Überwachungsstruktur neu.

Ich weiß, dass diese Antwort wie ein Overkill scheint, aber wenn der Schwerpunkt auf Korrektur und Lernen liegt, denke ich, dass dies die richtige Richtung ist.

Ein wichtiger Hinweis. Wie Sie gesagt haben, brauchen Sie mit einem registery nicht wirklich den DB Gen Server, um Aufgaben von Clients an die Arbeiter zu übergeben, und Sie haben Recht!Obwohl der vorgeschlagene Überwachungsbaum dem vor dem Register ähnlich sieht, sollten Sie jetzt nur Funktionen aufrufen (die im DB-Modul implementiert werden können), um die registery und übergeben Aufgaben direkt von Client-Prozesse an die Arbeiter.

+0

Sasa entfernt das GenServer-Verhalten ein paar Seiten später aus dem 'Database'-Modul, da es den Zustand nicht mehr hält und einfach die Registry-Suche und Delegation durchführt. Wo würdest du die Ordnererstellung in diesem Fall platzieren? –

+1

Ich denke, dass mein Vorschlag immer noch gilt, auch wenn das einzige, was der DB-Gen-Server verantwortlich ist für die Ordnererstellung bei der Initialisierung ist. Wiederum ist es ein Overkill, aber die Idee ist, eine richtige OTP-Entwicklung zu lernen. Aber vielleicht liege ich falsch. Mal sehen was andere vorschlagen ... – Nagasaki45

Verwandte Themen