Proposal: `Task.finish/2`

38 views
Skip to first unread message

Myron Marston

unread,
Oct 4, 2016, 12:49:19 PM10/4/16
to elixir-lang-core

In our application, we commonly use tasks while responding to HTTP requests to fetch metadata from the database that is used to “decorate” the returned data with some non-essential bits of info (think things like tags, added_at timestamps, etc). Given that this data is non-essential, we only want to wait on the task for a limited amount of time, and if the database query has not completed in that time, then we just want to respond without the extra data. We’ve tended to use this pattern:

task = Task.async(fn -> perform_db_query(...) end)
main_data = perform_main_request_logic(...)

case Task.yield(task, timeout) do
  {:ok, result} -> merge_metadata(main_data, result)
  nil ->
    Task.shutdown(task)
    main_data
end

This has worked OK, but on a recent github issue, @fishcakez helped me realize there’s a race condition in this pattern. The task could complete between the call to Task.yield/2 and Task.shutdown/1, and the logic as I’ve written it would throw the result away. Instead, it is better to do this at the end:

case Task.yield(task, timeout) || Task.shutdown(task) do
  {:ok, result} -> merge_metadata(main_data, result)
  nil -> main_data
end

Given that it’s an easy mistake to make, I think it would be beneficial if Task.yield(task, timeout) || Task.shutdown(task) was encapsulated as a function on the Task module. Maybe something like Task.finish/2. Basically, it’s like Task.await/2, but with a couple differences:

If the task completes, it returns {:ok, result} instead of just result
If the task times out, it returns nil instead of causing an exit.

If we weren’t concerned with backwards compatibility, it would make sense to rename Task.await/2 to Task.await!/2 and then make this Task.await/2 — but obviously we can’t do that. Task.finish/2 is the best name I’ve been able to come up with.

Thoughts?

Myron

José Valim

unread,
Oct 4, 2016, 1:14:45 PM10/4/16
to elixir-l...@googlegroups.com
I would prefer to not add another function and verb to the Task API. Otherwise it can be confusing to figure out which one you need from await, yield, shutdown and finish. The current API was designed to compose, so I would prefer to improve the documentation on both yield and shutdown to mention those scenarios.
--
You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-core+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/146cc734-b511-44d7-b647-5a1bf0dd37f3%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


--


José Valim
Skype: jv.ptec
Founder and Director of R&D

Reply all
Reply to author
Forward
0 new messages