DistributedSync allows for distributed filesystem synchronization. Each directory is synchronized using an instance of a DistSync.Client
, which communicates any changes that occur to the main server DistSync.Server
. The DistSync.Server
is responsible for relaying those changes on to all the clients that have "subscribed" to the synchronization. In this way, each individual client can avoid having to know about any other client that exists.
A "client" actually consists of several threads:
- a "serve" thread, which tracks which files have been added, changed, or deleted;
- a "fetch" thread, which watches for messages from the server and modifies files accordingly;
- a directory monitor thread, which kills the client if the directory is deleted or not present
- a server monitor thread, which kills the client if the server goes offline
-
Avoiding infinite loops
- For example: a file in
directoryA
is updated; that update is sent to the server; the server sends a message todirectoryA
telling it to update that same file; the client sees that the file was updated AGAIN, and sends another update to the server, etc.
- For example: a file in
-
Resolving filename clashes
- For example:
directoryA
anddirectoryB
both contain fileexample.txt
.directoryA
is synced first, and the server stores the contents ofdirectoryA/example.txt
. ThendirectoryB
is synced, and new contents under the nameexample.txt
are sent to the server.
- For example:
-
Avoiding sending the entire contents of a file through Elixir's message passing system
- Infinite loops were avoided by having the client's serve threads be aware of their corresponding fetch threads. They include that fetch thread's PID in messages to the server, so that the server can relay the changes to all the listening fetch threads EXCEPT that one.
- Filename clashes are resolved by always taking the most recent changes as the point of truth. This implies that the following would occur:
directoryA
containsexample.txt
, last modified at 3:00pm.directoryB
containsexample.txt
, last modified at 3:30pm.directoryA
is synced at 4:00pm, and the server considers the contents ofdirectoryA/example.txt
to be the point of truth forexample.txt
.directoryB
is synced at 4:05pm, and the server now considers the contents ofdirectoryB/example.txt
to be the point of truth forexample.txt
, and the proper update messages are sent out to the listening fetch threads.directoryA/example.txt
now mirrorsdirectoryB/example.txt
- Because Elixir's actor message passing system works best when the messages are small, the contents of an updated file are first compressed using
:zlib.zip/1
and:zlib.unzip/1
from Erlang'szlib
module. The clients are responsible for compressing and decompressing messages that are sent and received, respectively.
Use mix:
$ mix compile
DistSync
allows for distributed syncing of directories. First, the server must be started from within an iex
session:
$ iex --sname server --cookie cookie_string -S mix
iex(server@machine1)1> DistSync.Server.start_link
{:ok, #PID<0.example.0>}
Once the server has started, the client can be started. This does not have to be on the same node, or even on the same computer on the network:
$ iex --sname client --cookie cookie_string -S mix
iex(client@machine2)1> DistSync.Client.sync("dirA/", "server@machine1")
{:ok, {#PID<0.example1.0>, #PID<0.example2.0>}}