Is there a way to set the priority of a genserver process to :high?

Hello!

Is there a way to set the priority of a genserver process to :high?

On a more general note, a periodic genserver (scheduled via Process.send_after/3 sending a message to self()) generally executes the handle_info callback on time, but at times will be scheduled after 2x/3x the desired period. Are there any ways to mitigate this (apart from setting the priority, which might not even be effective?)

Thanks!

1 Like

Is it possible that your GenServer has other messages in it’s mail box? The “problem” with send_after/3 is that it will only send the message at that time. Meaning if your process already has messages, it will deal with those ones first. Which may seem like it is not running your message until some time later.

I have never actually had the need to adjust a processes priority, but I do remember people advising against this. A quick search does not yield specifics on it though. Maybe someone else can fill in the blanks here.

At the end of the day, if you have a particularily busy system, the only things you can really do is to either optimize the slow part(s) of your application, start distributing the workload, or just get a larger machine to run the work.

Thanks for the response!

Is it possible that your GenServer has other messages in it’s mail box? The “problem” with send_after/3 is that it will only send the message at that time. Meaning if your process already has messages, it will deal with those ones first. Which may seem like it is not running your message until some time later.

No, this is not the case. The worker only receives one kind of the message, from itself, to schedule more work (which itself is done async). So even if the mailbox is full with the same kind of message, I’d imagine its because the worker is not getting scheduled.

I have never actually had the need to adjust a processes priority, but I do remember people advising against this. A quick search does not yield specifics on it though. Maybe someone else can fill in the blanks here.

Yeah I read this, but only for :max, which is used by the Erlang runtime.

From what I have read, messing with the process priority is a really good way to cause unintended, problematic things to happen.

Can you elaborate on your actual use case? How often are you trying to schedule work?

1 Like

I am trying to schedule work once every 2000 ms. The genserver spawns a process that fetches stock price data and performs some calculations on it.

Are you sure that the GenServer isn’t blocked on something else leaving it unable to respond in a timely manner? Lots of functions are a disguised GenServer.call/3 which is a blocking, selective receive with a default timeout of 5000 ms.

Receiving messages in Elixir, or a few things you need to know in order to avoid performance issues

1 Like

If you’re scheduling it to run every 2 seconds but it’s actually taking 4 to 6 seconds then either the super is very overloaded or there’s something else going on. Can you show the code you’re using?

2 Likes

This is the relevant part of the code

  def init(state) do
    schedule_work(state.execution_period)
    {:ok, state}
  end

  def handle_info(:work, state) do

    schedule_work(state.execution_period)

    spawn_link(fn -> WorkerModule.work(state.data) end)

    {:noreply, %{state | last_execution_time_in_msec: System.monotonic_time(:millisecond)}}
  end

  defp schedule_work(interval) do
    Process.send_after(self(), :work, interval)
  end

@peerreynders I don’t think I am calling any blocking functions.

Thanks!

:wave:

Are you using NIFs anywhere? Those might block the schedulers.

Hmm, I have a similar scheduling server in Kiq.Periodic.Scheduler and it schedules reliably for days down to the millisecond.

Is there a chance the worker you are spawning is crashing? If so it would be restarted infrequently enough that the supervisor would keep restarting it. The crash + restart + delay could be enough to put it off schedule.

Thanks for the replies!

@idi527 I am not sure :frowning: Is there any way to find out(maybe through the observer?)

@sorentwo That is very interesting that it works for you. When mine works, it is mostly reliable (drift of around ~1ms per invocation). I am pretty sure it’s not crashing. I am using :sys.trace/2 to print state and see last_execution_time_in_msec and I don’t see crashes.

Can you show how you’re computing the offset?

Using spawn_link makes the asynchronous code that you are trying to execute effect the genserver. If the spawned process dies so does the genserver and maybe this is something that you don’t want. Have you tried to use only spawn? Also if this solves the problem but you still want to monitor the spawned processes you can monitor them with something like Process.monitor. What do you think?

1 Like

Quick update, I don’t seem to notice this issue anymore. I don’t think I changed anything, so this is a little surprising. I will update the thread if I notice it again.