4 ms·
The BEAM is a huge win: having lightweight threads means you can often do away with things like Redis for job queues and PubSub stuff. I love this answer on Sta
by ashton314 6y ago
The BEAM is a huge win: having lightweight threads means you can often do away with things like Redis for job queues and PubSub stuff. I love this answer on StackOverflow by Elixir's creator:
https://stackoverflow.com/questions/32085258/how-can-i-schedule-code-to-run-every-few-hours-in-elixir-or-phoenix-framework?rq=1 https://stackoverflow.com/questions/32085258/how-can-i-sched...
So simple. Something that would require a job queue and a job runner fades away into a piece of the OTP application tree. When it crashes, it will even come right back up!
Phoenix feels a little too heavyweight for really small projects—maybe I'm spoiled having used Mojolicious's [0] single-file web servers. (Example on the linked page.) But for anything slightly larger, Phoenix scales really well. I work on a decently-large application in Phoenix for work and it's been an absolute joy to work with this langauge.
Typing could be better. Though, Dialyzer does a decent job of catching type errors. That's saved my neck on more than one occasion.
[0]: https://mojolicious.org/ https://mojolicious.org/
- PascalW 6y agoI don't really understand this sentiment of not needing a queue system. This is not much different from spawning a thread in Java to delay the email sending. For any serious application you want a job like that to be persisted so you can guarantee it runs even if your application is restarted. I know that Erlang/Elixir is designed for stateful applications and if you have a cluster and do hot deploys this is less of a problem, but who does that? Most Elixir applications I've seen are deployed as stateless systems just like any Ruby, Python, Node etc systems.
- polmuz 6y agoHaving a queue means you have a distributed system. How do you handle network problems, errors/retries, back pressure? OTP has excellent idiomatic tools for all that and more.
- jeremyjh 6y agoElixir developers who need a queue generally reach for Rabbit (which any language can use), or something backed in a database like rihanna[1] or honeydew[2]. Rolling your own distributed system is very much a last resort, and despite its excellent concurrency characteristics the BEAM still lacks basics such as a battle-tested raft implementation. [1]https://github.com/samsondav/rihanna https://github.com/samsondav/rihanna [2]https://github.com/koudelka/honeydew https://github.com/koudelka/honeydew
- zambal 6y ago> and despite its excellent concurrency characteristics the BEAM still lacks basics such as a battle-tested raft implementation. A couple of years ago the RabbitMQ team has published a raft library[1] which they use in their implementation of persistent queues. It has a flexible API and implementing your own state machines is quite straightforward, as it follows the OTP gen_* behaviour paradigm. And by now, I'd say it's pretty well battle-tested. [1] https://github.com/rabbitmq/ra https://github.com/rabbitmq/ra
- jeremyjh 6y agoI didn’t know about that - thank you for pointing me at it!
- moreaccountspls 6y agoSlightly offtopic, but consider a pattern of persisting the state machine of a given task instead of the task queue itself. For example, for a daily email job, you might have three states: UNSENT, BEGIN(begin_time), SENT. Then you can just have a supervisor job that scans your table or whatever and schedules jobs, retries, etc. as needed. Now you don't have to worry about the queue state at all!
- winrid 6y agoWait, how does that answer prevent only one instance of the job across the whole system? That's what I'd use Redis or something for - for locking.
- acdibble 6y agoIf there is only one process running this GenServer, there will only be one instance of the job in the system. An BEAM process, and therefore a GenServer, has an atomic message queue built in. Only the process itself can pop messages out of its queue, so there is no need for locking. It is not possible to get messages out of the queue concurrently. Messages can be processed concurrently, but that's after they've already been removed from the queue.
- winrid 6y agoWhat if you have 100 servers running the same application code and you just want to run this job, one time, for a customer? Usually I'd use a Cron with a distributed lock, which is served via Redis. Just curious as to how the BEAM solves it as I imagine it can.
- dnautics 6y agoYou can use tricks like the global spawn option to make processes globally unique. Doesn't scale infinitely, but for a 2->low double digits HA/load balanced system it should be fine.
- winrid 6y agoOh wow, so this will work across multiple instances of the BEAM?
- dnautics 6y agoIf they are clustered using erlang clustering, yes. https://erlang.org/doc/man/global.html https://erlang.org/doc/man/global.html Note that this works by taking a transaction lock on the entire cluster, so it's not recommended if you're making requests frequently. It also doesn't respect netsplits, unless you instruct in code to fail with a deficient cluster membership.
- nojito 6y agoHow is that stackoverflow answer good? You lose everything about the scheduled jobs when your app crashes. This doesn't even address the rampant memory leak issues that plague long running genservers.
- ashton314 6y agoSome jobs don’t need persistence, or might have everything about them stored in a Postgres DB. Examples: I have a project where I render lots of little PDFs as previews for a LaTeX editor. My use case means it’s good to keep them around for a short while; after an hour or so I want to delete them all so they don’t fill up my disk. Perfect case for this. The question mentions rebuilding a site map—that sounds like it doesn’t need to persist much state. Finally, at my work we have orders that sit in a Postgres DB that we regularly check to see if any are stuck. We do use the Quantum library for that, but a solution like this could theoretically work. > Rampant memory leak issues As long as you’re not sending this genserver bogus messages, I don’t see how memory would leak in this example. Erlang applications are known to run uninterrupted for years at a time, so I figure this is a solvable problem. When you do need persistence for more robust job scheduling, yes, having a real queue system in place is much better. You are correct there. I think the answer is good because it is so small and fits these simple use cases—the fact that you can handle those cases without needing something heavier is pretty awesome.