markmark206
I would like to perform an action periodically, and I am looking for the simplest reasonable way to do this.
As an example, let’s say I want to delete old entries from a db table, every few hours. The action is idempotent, relatively inexpensive, precision “doesn’t matter,” and concurrent executions of multiple runs (e.g. from multiple replicas of the service) is not a problem (db transactions will handle them safely).
A commonly recommended approach for doing this seems to be a variation of using a GenServer with send_after (or, I suppose, spinning up an oban job;).
This makes a lot of sense, but I coded up just running an infinite supervised “do”+“sleep” recursion, and it seems to work, and it seems ridiculously concise and simple, and I can’t explain to myself why that wouldn’t be enough.
Is there any reason why I shouldn’t do this? ; )
If you have any thoughts / guidance on this, I would much appreciate them!
Thank you!
PS An example of what this might look like in code:
application.ex (starts the task, restart: :permanent):
defmodule MyApp.Application do
def start(_type, _args) do
children = [
...,
Supervisor.child_spec(
{Task, fn -> MyApp.Sweeper.delete_old_data(sleep_ms) end},
restart: :permanent
),
...
]
Supervisor.start_link(children, strategy: :one_for_one, name: MyApp.Supervisor)
end
end
where delete_old_data() just keeps doing the thing and sleeping, forever:
defmodule MyApp.Sweeper do
def delete_old_data(wait_ms) do
... delete old things ...
Process.sleep(wait_ms)
delete_old_data(wait_ms)
end
end
Trending in Questions
Other Trending Topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #library
- #deployment
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #podcasts
- #javascript
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixirconf-us
- #ai
- #blog-post
- #elixir-ls
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #hex
- #security
- #metaprogramming











Showing Posts 1 to 10- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
mayel
Maybe using Oban: Reliable Scheduled Jobs — Oban v2.23.0
kokolegorille
You are not supervised if You do so… what happens if delete old things fails?
UPDATE: Yes You are… sorry didn’t read your code properly
al2o3cr
What happens when this needs to shut down? I assume it will snooze right through the first round of “polite” notifications from its supervisor…
markmark206
Yes, this is exactly what I am curious about – can my solution be more lightweight than requiring introducing a dependency (especially as rich as oban, with its own database dependency and schemas, etc.).
I don’t need persistence for my “job” (which is part of what Oban provides) I just need to call a function every once in a while, for as long as my application is running.
Can a simple BEAM process (with receive after) and a supervisor be sufficient?
markmark206
This is a good point, but does it matter?
It seems like the process will be killed within 5 seconds (I am just using the default
:shutdownvalue according to Supervisor — Elixir v1.14.3 ), which seems fine – I am obv only usingdelete_old_data()for its side effects, and I do want the process to disappear (whether it is sleeping or asking the db to do the pruning) when the application shuts down.Is there any reason I should care?
cmo
I would use a GenServer at least so you have it in a file somewhere and not in
application.ex. Not sure what you’d gain from going the task or process method? Are you trying to reinvent a wheel or save some LOC? It certainly makes it more work to extend and hides the code away.If you had oban as a dep already that would probably be the right choice.
tfwright
Well, he did say he was aiming at simplest, and I’d actually tend to agree that a module not importing a separately defined behavior is a bit simpler…at least in principle? I’m also interested in anything more substantial he might lose though by starting here.
For myself I usually start with Quantum unless I know I’m going to need persistence. I do always know I am going to need scheduling.
kwando
It certainly works like you did but I think it is but I is more idiomatic to keep the details out of the application file. The very least you could do is to put the
child_spec/1inside theMyApp.Sweepermodule…but really I think a standard
GenServer+send_after/:timer.send_intervalis the way to go for this… easier to read and understandSebb
https://github.com/quantum-elixir/quantum-core
seems a great fit.
KristerV
came here to say this. Quantum is both reliable and readable. custom solutions may be reliable (thanks to supervisors), but they are very annoying to read a few months down the line.