ElixirSync

Concurrently sync the contents of two or more system directories, in the style of a tool like Dropbox


Compilation

mix escript.build

Usage

./esync [dir1] [dir2] [...]

Explanation

The script spawns two threads per directory:

The first class of thread maintains an internal state of md5 digests of the contents of the files of their respective directories. This is done with

new_digests = build_digests_map new_files_list
...
defp build_digests_map(files) do
  Enum.map(files, &({&1, get_digest(&1)})) |> Enum.into %{}
end

defp get_digest(file) do
  {:ok, contents} = File.read file
  :crypto.hash(:md5, contents)
end

Then the server threads compute which files have changed:

updated_files = for file <- new_files_list,
      Map.get(new_digests, file, nil) != Map.get(old_file_digests, file, nil),
      do: file

The fetcher threads simply receive messages and write or delete files as necessary:

defp fetch_loop(dir, time_delay) do
  receive do
    {:update, filename, contents} -> spawn_link(__MODULE__, :handle_fetch_update, [dir, filename, contents])
    {:delete, filename} -> spawn_link(__MODULE__, :handle_fetch_delete, [dir, filename])
  end
  :timer.sleep(time_delay)
  fetch_loop(dir, time_delay)
end

Challenges

Some of the challenges inherent in a problem like this:

And how these challenges were solved: