Um geänderte Module neu zu laden, kann man entweder die VM neu starten oder aber nur die Module
neu laden, die sich geändert haben.
Die erste Methode kennt bestimmt jeder, aber die zweite vielleicht nicht. Ich benutze diese zweite Methode in meinem Continuous Integration Prozess, indem ein Prozess die geänderten Sourcen aus SCM lädt, baut und dann die Nachricht an den sogenannten code_reloader schickt, damit dieser die geänderten Module neu laden soll.
Wie funktioniert das im Detail?
Zunächst einmal ermitteln wir alle Module, die z.Z. geladen sind.
code:all_loaded()
Diese Funktion gibt uns eine voll qualifizeirte Liste aller geladenen Module zurück. Wir könnten nun über die Liste iterieren und einfach alle Module neu laden, aber das wäre langweilig.
Deswegen überprüfen wir zunächst, ob es sich bei der Datei um ein beam File handelt. Das ist für unsere späteren Schritte wichtig.
Um das zu überprüfen schaun wir nach, ob die zu überprüfende Datei Metadaten besitzt.
file:read_file_info(File)
Wir werten diese Daten nicht aus, es reicht uns das diese vorhanden sind.
Metadaten können sein, siehe :
file:read_file_info
Da das Modul
code nur Dateien liest, die die Endung beam haben (
code:load_file) müssen wir noch prüfen, ob die Datei diese Endung auch besitzt.
filename:extension(File)
Diese Überprüfungen reichen aber moch nicht aus um eine Liste zu erzeugen, die nur die geänderten Module enthält. Deshalb überrpüfen wir nun, ob es Module gibt, die ein jüngeres Compile Datum haben.
Man kann zur Laufzeit bestimmte Metadaten eines Modules mittels
module_info() abfragen. So z.B. die funktionen die export werden, eigene Attribute wie die vsn, author und viele mehr.
Wir nutzen das Compile Datum, das ebenfalls in den Metadaten hinterlegt wurde, um zu prüfen ob ein geladenes Module älter ist als das was in dem ebin Verzeichnis liegt.
1. Auslesen des Compile Datums des geladenen Modules
proplists:get_value(time, Module:module_info(compile))
2. Auslesen des Compile Datums des Modules, das im ebin Verzeichnis liegt
{ok,{_,[{_,I}]}} = beam_lib:chunks(File,[compile_info]),
proplists:get_value(time,I).
Dazu nutzen wir das Modul
beam_lib, das uns den Zugriff auf die Beam Files ermöglicht. Mit der Funktion
chunks können wir die Compile Informationen ermitteln und dann die Compile Zeit auslesen.
Nun haben wir die Liste der Module, die sich geändert haben und können diese nun neu laden. Bevor wir den neuen Code laden, entladen wir den alten Code mittels
code:purge und laden dann den neuen Code mittels
code:load_file
Alles zusammen ergibt dann :
[load_module(Module) || {Module, File} <- code:all_loaded(), is_beamfile(File), is_old(Module,File)]
Lade das Module aus der Liste aller geladenen Module, die den Filtern is_beamfile und is_old genügen.
Vieleicht wird der eine oder andere fragen wollen, was denn mit neuen Modulen passiert. Werden diese auch neu geladen?
Diese werden nicht durch diesen Mechanismus geladen, sondern das wird durch die Laufzeit erledigt. Wenn ein Modul namentlich angesprochen wird, so wird geprüft, ob es dieses Modul schon im Speicher gibt, ansonsten wird versucht das Modul zu laden.
Kommentare