adamwight

adamwight

I’ve written a crude module for retrieving files from a remote server using rsync, since I didn’t see any existing tools already. This could be useful anywhere one needs to efficiently move files across filesystem or network boundaries, and could even be used for better control and monitoring of deployments. Does it sound like something I should package into a library?

The inner logic takes advantage of the --info=progress2 reporting mode for rsync, reading status lines to monitor progress:

port = Port.open(
  {:spawn_executable, System.find_executable("rsync")},
  [args: ["-a", "--info=progress2"] ++ remote_urls ++ [local_path],
    :binary, :exit_status, :hide, :use_stdio, :stderr_to_stdout]
)

receive do
  {^port, {:data, data}} ->
    ....

  {^port, {:exit_status, status}} ->
    ....
end

Showing Posts 1 to 10

dimitarvp

dimitarvp

I am not a reliable source but IMO that would be more interesting as a blog post showing how did you wrap rsync.

adamwight

adamwight OP

A blog post is a good idea, and I ended up publishing as a library so there’ll be something to write about:

dimitarvp

dimitarvp

Thanks. Had a quick look. IMO you should include a complete example with the error messages that your GenServer can also emit (not only the happy path). I’d be interested in just copy-pasting such an entire block of code in a project of mine and then tuning the separate case branches for my goals. Better peace of mind.

adamwight

adamwight OP

I was also curious about error cases, so I added some tests that show reasonable behavior when rsync emits random junk (forward to Logger at :warning level) and when the process crashes (forward error to linked parent).
Deleted: random claims that the tests are a full example.

Feel free to share the case statements you add for your own application goals.

adamwight

adamwight OP

I struggle with basic OTP principles. Is the normal way to handle a start_link failure to run it under a Supervisor? Anyone feel like sharing a snippet showing how to run a brittle command like this and catch the error?

In my own app I’m letting Oban handle the retry logic and in tests I’ve trapped the exit but these are side-stepping typical usage I suspect.

adamwight

adamwight OP

Thanks for the suggestion, here are my observations as a blog post: Elixir/Ports and external process wiring - ludd

pzingg

pzingg

Very informative post, Adam. I am using ports to communicate with long-running OS processes and struggle to understand the niceties about how to signal them from the BEAM. I did not know about erl_child_setup but I see that it is used by two other OS process-oriented libraries: alcove and prx.

adamwight

adamwight OP

I’d be curious to hear more about the obstacles that you’ve run into. If you mean “signal” in the generic sense of sending data back and forth, there are certainly some complexities but it should be possible to accomplish whatever you need, just set up a gen_server to write and read to and from the process.

But if you mean “signal” in the POSIX kill sense I think it’s impossible to do with the built-in functions, since the OS PID mapping is strictly internal and can’t be accessed from code. If this is the intention, you could write a wrapper in C or shell, which listens for messages from BEAM and translates those into signals to send to child. But I wait to hear more!

akash-akya

akash-akya

since the OS PID mapping is strictly internal and can’t be accessed from code.

BEAM does return OS PID: erlang — OTP 29.0.2 (erts 17.0.2)

iex> port = Port.open({:spawn_executable, "/usr/bin/cat"}, [])
#Port<0.11>

iex> {:os_pid, pid} = Port.info(port, :os_pid)
{:os_pid, 367095}

iex> {output, 0} = System.cmd("ps", [to_string(pid)], into: IO.stream())
    PID TTY      STAT   TIME COMMAND
 367095 ?        Ss     0:00 /usr/bin/cat
adamwight

adamwight OP

@akash-akya thanks for pointing this out! I updated the linked blog with your correction but unfortunately can’t edit my post above, so the misinformation will linger…

The other shortcoming of the “kill” approach which I should mention here is that it’s hard to include as an automatic clean-up, for example if the controlling BEAM process tree is shut down. That’s what lead me to push the signaling down into erl_child_setup or the analogous C shim program included in the rsync library.

— All posts loaded —

Where Next? Top

Trending in RFCs Top

manuel-rubio
There was some time when I started thinking about giving a boost to Lambdapad, the initiative from @garretsmith in Erlang that I loved wa...
New
Agostinho1965
Hey everyone — I’m putting together a practical, code-first book on building production-ready business applications with Phoenix LiveView...
New
andreasronge
You set up environments, each with its own tools, its own data and its own limits, and programs get evaluated in them. The same program r...
New

Other Trending Topics Top

JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
mhanberg
Hi everyone! The first release candidate for the Expert language server project is now available! We’ve published a press release detai...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Dmk
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews