Skip to content

Commit

Permalink
[perf] Kill tasks directly rather than through the supervisor
Browse files Browse the repository at this point in the history
When lots of things happen in lexical, it's necessary to kill some
outstanding tasks that won't finish. When lots of things need to be
killed (as in when a lot of files are changed), killing them can
become a bottleneck. This is because we were using terminate_child,
which makes a genserver call through the supervisor and waits for the
pid to exit before it returns. This, in turn would cause the
supervisor to back up and the task queue to die because of timeouts.

Replacing this with `Process.exit` removes the bottleneck and doesn't
seem to have any adverse effects.
  • Loading branch information
scohen committed Sep 4, 2024
1 parent d6e1a73 commit 1d9f81a
Showing 1 changed file with 7 additions and 10 deletions.
17 changes: 7 additions & 10 deletions apps/server/lib/lexical/server/task_queue.ex
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ defmodule Lexical.Server.TaskQueue do
defp maybe_log_task(reason, request_id),
do: Logger.warning("Request id #{request_id} failed with reason #{inspect(reason)}")

defp as_task(request_id, {m, f, a}) do
defp as_task(request_id, {m, f, a} = mfa) do
handler = fn ->
try do
case apply(m, f, a) do
Expand All @@ -93,7 +93,7 @@ defmodule Lexical.Server.TaskQueue do
end
end

run_task(handler)
run_task(handler, mfa)
end

defp write_reply(response) do
Expand All @@ -119,21 +119,18 @@ defmodule Lexical.Server.TaskQueue do
end

defp write_error(id, message, code \\ :internal_error) do
error =
ResponseError.new(
code: code,
message: message
)
error = ResponseError.new(code: code, message: message)

Transport.write(%{id: id, error: error})
end

defp run_task(fun) when is_function(fun) do
Task.Supervisor.async_nolink(task_supervisor_name(), fun)
defp run_task(fun, mfa) when is_function(fun) do
task = Task.Supervisor.async_nolink(task_supervisor_name(), fun)
%Task{task | mfa: mfa}
end

defp cancel_task(%Task{} = task) do
Task.Supervisor.terminate_child(task_supervisor_name(), task.pid)
Process.exit(task.pid, :canceled)
end
end

Expand Down

0 comments on commit 1d9f81a

Please sign in to comment.