The idea of composing individual applications and programs sounds very powerful, and I like the idea of using it as a way to compose programs written in different languages together (a Rails server talking to a Clojure ML service that uses Rust bindings to do hardcore number crunching, for example). That could be interesting. I don't know what I'd use it for, though.
What? If you don't need concurrency in a web app that means that your app can only serve one client at a time and other clients have to wait in line. That won't scale to more than a couple concurrent users.
You're right that microservices don't make sense for every web app. Especially when you're still figuring out product-market fit. But once you achieve a certain scale (both in number of users and number of developers) microservices/SOA starts to make a lot more sense.
And that's an area where the Erlang VM really shines. Erlang's concurrency model is awesome and fits really well with the hardware evolution to more cores: lightweight processes, immutability, message passing, location transparency, etc.
I do think Elixir and Phoenix have a lot of value because Phoenix has the "get shit done" factor of Rails and yet still allows for your application to scale once you've entered the market, and you don't have to drop everything and move to a more scalable language/architecture and slow down. Plus, Elixir's features and syntax make it a lot more accessible than Erlang.
That's interesting considering the reason Phoenix was made is because the author was having a lot of trouble doing concurrency in Ruby.
I'm the author of Sidekiq and not trying to debate; I'm simply ignorant of how Erlang, BEAM and the ecosystem solve these common problems, which usually persist no matter which language you use.
%% ping a remote node to connect to it
pong = net_adm:ping('[email protected]'),
%% start my application on the remote node
_ = spawn('[email protected]', application, start, [my_app]),
%% my_app is a gen_server, an otp process that abstracts
%% async request/response and makes it look more like
%% synchronous function calls
%% this example assumes the gen_server accepts the JobArgs
%% and immediately returns 'ok' as acknowledgement, but does
%% the work asynchronously
ok = gen_server:call({my_app, '[email protected]'}, {do_some_work, JobArgs})
if you wanted to distribute a number of jobs over a number of nodes you'd just repeat this process for each node and distribute your jobs however you choose, either by round robin or something more complicated like rendezvous hashing or somethingthis example assumes you're uninterested in the responses, but if you wanted to receive the results you could either send a pid (process identifier) along with the request that would be setup to collect the results and do any further processing or you could spawn a local process that made the actual call and blocked until it received the results
as for scheduling a job a week in the future, the naive way would be to setup a timer that, when it expires, spawns a process that can execute an arbitrary function. no one would do this, normally, however. you'd probably want to use something like sidekiq that can store the job off node and poll regularly to see if there's any jobs you should run
It doesn't have a scheduled job API yet, but there is a note in the readme that explains how to do this with quantum-elixir: https://github.com/joakimk/toniq#how-do-i-run-scheduled-or-r....
Start the processes that execute the code, get notified when they are finished. Literally a couple of lines of Erlang/Elixir. Not some large framework that does all that for you, it's in the language. It is built to run a lot of tasks in parallel and across many machines.
> How do you schedule a job to run in a week?
Like anyone else, put it in some permanent storage like redis. You can also keep it in memory, you can also use mnesia which is a distributed database that comes with Erlang.
my_list_of_job
|> distribute_tasks()
|> Enum.map(fn x -> Task.Supervisor.async(x, my_list_of_job) end)
|> Enum.map(fn x -> Task.await(x, your_timeout) end)
That is will distribute your worload on all the nodes you have that implement a runner for that. Of course you may want to make things a bit different, and i did not covered your load balancing that i hidden in distribute task, nor any error handling, but that would not be far harder imho. Until you enter the partition and idempotent part, but even that would not be that hard.There is a long running project to work on that to produce a nice job queue lib on Elixir Slack but noone on the interested group have time for now.
To schedule a job to run in a week you have a couple way. An easy first approach is to use Process.send_after/3 to send yourself a message after a week and handle that message in a week. Of course if you want persistence, etc, you will have to do it a bit differently, but not so much....
edit : now use the fn form instead of the shorthand & for ease of comprehension for non seasoned Elixir devs
I heard that Erlang/OTP (and thus Elixir) can transparently distribute work no matter where it gets physically executed but I'd just love to see an example -- freely accessible code, demo site, everything.
But that said, for where Erlang shines - backends and distributed services - first-class concurrency is damn near a must-have these days (see: Go, Erlang, etc. recent surge in use and popularity).
I ended up with Go, but had I more time to learn Erlang (or Elixir was 1.0 when I started looking) it would have been a solid contender.
Also, don't discount the incredibly solid and robust BEAM VM. If you are writing distributed services or servers (see: Riak, RabbitMQ) the overhead for spinning off "instances" or "processes" within the VM is virtually zero-cost, and in production systems has like sixty-four 9s of uptime. [0]
[0] ok, nine, but still: https://pragprog.com/articles/erlang