Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Priority Messages #73

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

rickard-green
Copy link
Contributor

@rickard-green rickard-green commented Jan 7, 2025

In some scenarios it is important to propagate certain information to a process quickly without the receiving process having to search the whole message queue which can become very inefficient if the message queue is long. This EEP introduces the concept of priority messages to the language which aim to solve this issue.

eeps/eep-0076.md Outdated Show resolved Hide resolved
eeps/eep-0076.md Outdated Show resolved Hide resolved
eeps/eep-0076.md Outdated Show resolved Hide resolved
eeps/eep-0076.md Outdated Show resolved Hide resolved
eeps/eep-0076.md Outdated Show resolved Hide resolved
eeps/eep-0076.md Outdated Show resolved Hide resolved
eeps/eep-0076.md Outdated Show resolved Hide resolved
eeps/eep-0076.md Outdated Show resolved Hide resolved
eeps/eep-0076.md Outdated Show resolved Hide resolved
eeps/eep-0076.md Show resolved Hide resolved
Copy link
Contributor

@Maria-12648430 Maria-12648430 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pretty cool, I was hoping for something like this for a while ❤️

Comment on lines +53 to +54
long time until the receiver fetch this message. The situation will at this
point very likely have become even worse.
Copy link
Contributor

@Maria-12648430 Maria-12648430 Jan 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or the situation could have resolved itself by then (like, if the messages occur in sudden bursts in a by and large relaxed scenario), at which point it would be too late to change handling strategies.

Copy link
Contributor Author

@rickard-green rickard-green Jan 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the message comes so late that it does not reflect the current state and you may not want to act on it. I'll rewrite this.

Comment on lines +186 to +189
The receiver process can at any time disable reception of certain priority
messages by passing `false` as second argument to any of the above listed
`process_flag/2` BIF calls.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If a sending process specified in a priority process flag exits, will it automatically be removed from the process flags of the receiver?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the reference implementation this is currently not done, but this can be implemented.

Comment on lines +168 to +174
* *Priority Marked Messages* - A message is marked as a priority message by the
sender by passing the option `priority` in the option list that is passed as
third argument to the `erlang:send/3` BIF. The receiver opts-in for reception
of priority marked messages from a specific sender by calling the
`process_flag/2` BIF like this:
`process_flag({priority_marked_message, SenderPid}, true)`.
Copy link
Contributor

@Maria-12648430 Maria-12648430 Jan 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder, how could this be put to work for system messages originating from sys and, by extension, from proc_lib? One would probably want to prioritize those, but AFAICT the sending process is the calling or a freshly spawned process, ie it can't be known to set the SenderPid for the priority_marked_message flag in the receiving process.

Copy link
Contributor

@Maria-12648430 Maria-12648430 Jan 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that I think about it, this restriction to prioritize messages from a specific sender pid comes with a catch.

Contrived example:
Let's say a process A configures priority_marked_message for messages from another process B. Process B however starts another process C to perform some complicated task (in order to stay itself responsive meanwhile), and in the end this process C should send a priority message on behalf of process B to process A. But since process C is not configured for priority_marked_message in process A, this message (which, again, is sent on behalf of but not by process B) won't be prioritized in the mailbox of process A.

So, as a first idea, maybe priority_marked_message should accept aliases too?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder, how could this be put to work for system messages originating from sys and, by extension, from proc_lib?

To me it feels a bit like stretching what the priority messages was intended for

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, as a first idea, maybe priority_marked_message should accept aliases too?

Hmm, I like it. Perhaps the priority option can be replaced with priority aliases all together.

Comment on lines +138 to +141
multiple priority messages would accumulate in reverse order. Having these two
sets of messages ordered internally by reception order at least to me feels the
most useful. Just as in the case of ordinary messages we will probably want to
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO, keeping reception order is important, imperative, the only sensible thing to do. More so since the receiving process can't distinguish prioritized from ordinary messages.


Note that the reception order of signals is not changed. If a process sends an
ordinary message and then a priority message to a another process, the ordinary
message will be received first and then the priority message will be received.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"received" here doesn't mean "returned by receive" I take it, since the whole point of this proposal is to change that order?

Copy link
Contributor

@Maria-12648430 Maria-12648430 Jan 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Certainly, but it leads to an interesting point.

If the order of events turns out like this...

  1. process A sends ordinary message M to process B
  2. process A sends prio message P to process B
  3. process B calls receive
  4. process B calls receive

... then process B will receive the messages in order P then M.

However, if the order of events turns out like this...

  1. process A sends ordinary message M to process B
  2. process B calls receive
  3. process A sends prio message P to process B
  4. process B calls receive

... then process B will receive the messages in order M then P.

To be clear, that is pretty much as expected. It could/should be mentioned maybe.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"received" here doesn't mean "returned by receive" I take it, since the whole point of this proposal is to change that order?

Correct. The signal is received before it is inserted into the message queue. The name of the receive expression is in my opinion quite misleading. A less misleading name (although not as nice name) could have been fetch_message_from_message_queue.

The reason for not having options for accepting all priority marked messages,
all exit messages, or all monitor messages as priority messages is the risk of
introducing bugs when code in other modules are called from the process
accepting priority messages. For example, if a process enables all monitor
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this point should be emphasized. Disabling the message receive order guarantees should be opt-in and based on explicit agreement between sender and receiver (which may be the same module).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess when you say "the same module" you really mean "the same process"? 😅

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, I mean the same module. Say a gen_server where an API function (called from outside the server process) ends up passing a priority message to the server process -- the code for both sender and receiver is in the same module.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, ok, after re-reading the rest of the paragraph I get that. However, the significance of the module from which a call is made is unclear to me. Why does it matter? 🤔

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only significance is that with both sender and receiver in the same module they are in a better position to agree on the validity of using priority messages, which otherwise would be totally invalid.

My point is that priority messages break long-standing Erlang semantics and are totally invalid in the general case. They must be opt-in, which requires a close relationship between sender and receiver.

eeps/eep-0076.md Outdated
in the future might be modified in a way so that it relies on the selective
receive optimization taking effect. Therefor I find it unacceptable to disable
the selective receive optimization. The priority message implementation needs
to be able to preserve the selective receive optimization.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd say MUST not needs.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've rephrased this.

Comment on lines +168 to +171
* *Priority Marked Messages* - A message is marked as a priority message by the
sender by passing the option `priority` in the option list that is passed as
third argument to the `erlang:send/3` BIF. The receiver opts-in for reception
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should there be an !-like operator for sending messages prioritized (like, say, !!)? Probably not, having a convenient operator may lead to abuse and/or over-use. Also, it would be somewhat hard to factor hypothetical priority levels into it.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we want to encourage the use of priority messages that much. They should be rare enough to not need an operator.

third argument to the `erlang:send/3` BIF. The receiver opts-in for reception
of priority marked messages from a specific sender by calling the
`process_flag/2` BIF like this:
`process_flag({priority_marked_message, SenderPid}, true)`.
Copy link

@potatosalad potatosalad Jan 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be possible to allow something like this for allowing any process on the current node to send priority messages to the receiving process?

process_flag({priority_marked_message, local}, true).

Otherwise, for cases like overload protection where it may be a system-wide decision to change strategies, it could require having a secondary process used solely for the purpose of forwarding high priority messages to the receiving process based them opting in to receive priority messages from this secondary proxy process.

Demonstration the "priority proxy" thing:

-module(eep76_local_senders).

worker_start() ->
    spawn(fun() -> worker_init(foo) end).

worker_init(Strategy) ->
    % Option 1: spawn a "proxy" process
    PriorityProxy = priority_proxy_start(self()),
    erlang:process_flag({priority_marked_message, PriorityProxy}, true),

    % Option 2: allow priority message from any local process
    erlang:process_flag({priority_marked_message, local}, true),

    true = erlang:register(worker, self()),
    worker_loop(Strategy).

worker_loop(Strategy) ->
    receive
        {change_strategy, NewStrategy} ->
            worker_loop(NewStrategy)
    end.

priority_proxy_start(Worker) ->
    spawn(fun() -> priority_proxy_init(Worker) end).

priority_proxy_init(Worker) ->
    true = erlang:register(priority_proxy, self()),
    priority_proxy_loop(Worker).

priority_proxy_loop(Worker) ->
    receive
        Message ->
            erlang:send(Worker, Message, [priority])
            priority_proxy_loop(Worker)
    end.

change_strategy(Strategy) ->
    spawn(fun() ->
        % Option 1: send to a "proxy" process
        erlang:send(priority_proxy, {change_strategy, Strategy}, [priority])
        
        % Option 2: send directly to the worker process
        erlang:send(worker, {change_strategy, Strategy}, [priority])
    end).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that @Maria-12648430's suggestion could solve this by distributing a priority alias to the processes that should be able to send such priority messages.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Double checking my understanding, that idea might look like this, right?

% receiver
PriorityAlias = erlang:alias([priority]).
% distribute to "senders" out of band

% sender (any process)
PriorityAlias ! priority_message_here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@essen
Copy link

essen commented Jan 8, 2025

If you send priority messages fast enough, the process may never get to read normal messages again. This warrants a big warning in the documentation, or to make the implementation read from both queues. Considering the intended use case, the former sounds good enough.

@rickard-green
Copy link
Contributor Author

If you send priority messages fast enough, the process may never get to read normal messages again. This warrants a big warning in the documentation, or to make the implementation read from both queues. Considering the intended use case, the former sounds good enough.

Yes, but I'd argue that you are misusing the feature if that should happen and quite a lot of features can destroy a system if misused (for example process priorities). Agree about the warning.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants