EEP 76: Priority Messages

This brings up a point I didn’t clarify. I don’t necessarily know what process will send a priority message.

This is not a statement or endorsement of crypto or proof-of-work algorithms.

Consider proof of work done by a group of Tasks (to use an Elixir term). Almost certainly, only one will actually solve some problem. However, I still want to report progress to a monitor. I don’t want to, and I don’t need to, know or give identifiers to these tasks.

To relate to one of my points, those tasks may spawn their own processes - do I need to bubble up the priority messages and have all children processes listen to priority messages? I can’t have a grandchild send a priority message without a fair amount of added noise and ceremony, and the chain may be long. I’ve now introduced a new message queue that is slower with no existing runtime guarantees.

I don’t agree. A process gives its “consent” to other processes to send it signals when it shares its pid. The pattern matching is just a way for the receiver to select signals between a subset of received signals (i.e, message signals). Another way the receiver selects between incoming signals is the trap_exit process flag. In one scenario it is transformed into a message which is put into the message queue, in the other it is (potentially) terminating the receiver. Other ways to select how an incoming signal is handled are to demonitor() a monitor or unalias() an alias. An important part here is that both senders and receivers need to agree on a protocol. If the sending process doesn’t adhere to the protocol, bad things might happen.

Similarly when it comes to priority messages. You “consent” to others sending you priority messages by sharing the priority alias. Creating a priority alias and sharing it, enabling a priority monitor, or enabling a priority link are different ways for the receiver to select how to handle the incoming signals comparable to the trap_exit process flag, demonitor(), unalias(). Both senders and receivers needs to agree on the protocol to use. If senders do not adhere to the protocol, bad things might happen. This is, however, just the same as in the case when ordinary messages or other signals are passed using a pid.

I have a really hard time buying the argument that priority alias will lead to abuse like “my messages are more important”. When it comes to priority aliases, it is even possible to prevent other processes that should not use it from getting access to the alias (assuming that the other processes are not trying to hack the system and more or less tries to attack it). In the pid scenario any other process can get hold of your pid by calling the processes() BIF.

When basing priority messages on a tag like the 'PRIORITY' tag above it is actually much easier to abuse it in the “my messages are more important” way than when using the proposed priority aliases.

This will make it a bit harder to abuse, but will still be easier to abuse in the “my messages are more important” way than priority aliases.

The priority aliases does not focus on the who like the original priority_marked_message process flag. The priority_marked_message process flag unnecessarily got who focused since I hadn’t thought of this use of alias (thanks @Maria-12648430) which is much better. I haven’t updated the PR for the EEP yet, but hope to do that soon.

When creating a priority alias you share it with other processes that are expected to use it according to an agreed upon protocol as in all other communication between processes. The design of the protocol is important. It is always possible to make a protocol that is impossible to understand, hard to extend, etc. Nothing with priority aliases prevents you from designing a protocol that is easy to understand, extend, etc.

The intent with the priority messaging is also not that every process in the system should make use of it. The intent is to solve very special scenarios that you cannot solve in an efficient way today without resorting to polling workarounds (which also wont be as efficient as the priority messaging).

3 Likes

I really like the simplicity of what José said. I don’t know enough about the effort involved to implement this feature but if we set the following:

process_flag(priority_messages_tag, ‘FOCUS’)

Couldn’t we just reuse the existing mailbox methodology to create a separate ‘priority’ mailbox and when this line is processed:

receive
  {'FOCUS', do_some_work} -> ...
end

Could the running process (due to the existence of the process_flag) direct itself to check the second “priority” mailbox?

Regarding how messages are put onto the mailbox today it (for a layman) doesn’t seem like too much of a stretch that if we use:

send(Pid, do_some_work, [priority])

The runtime would direct this to the mailbox created via the process_flag call. Additionally if you send a message with [priority] flagged when that mailbox doesn’t exist it should match what happens if you send to a dead process (imo).

+1 to this idea.

I got a private message regarding this topic. I’d like to keep the discussion in this thread and/or in the EEP PR, so I’m giving the answer here.

The question was more or less: Priority aliases makes it really defensive. Why can we not let the programmer be in charge of whether something needs to be a priority message or not? When there are many processes involved that needs to be able to receive priority messages, it gets cumbersome to keep track of all priority aliases. I think I am not misrepresenting the question, but if so, please correct me. Regardless, I know that there are more people of this opinion.

If we look at only the explicit priority message scenario (no exit signals and such). In my first iteration of this, I was thinking of having a process flag priority_messaging with a boolean value in order to opt-in for this. If you set it to true, the process would be able to receive priority messages sent using the send/3 BIF with the priority option; otherwise, these messages would be treated as ordinary messages. This is actually what I think is needed. Nothing more. I don’t think there should be too many safety-nets. I am of the opinion that users that want to shoot themselves in the feet will always find a way to shoot themselves in the feet, no matter what.

However, I suspected that there would be complaints about the potential abuse of this feature. Therefore I restricted it even more. The registration of senders allowed to pass priority messages felt like it would easily defeat such arguments. It, however, got a bit too restrictive. Then the idea (which was not mine) of using aliases for determining whether or not a message should be interpreted as priority message came up and I felt that this would fit perfectly. It can be viewed as a capability and can easily be shared among the processes that should be allowed to do this. Now programmers would able to be as restrictive as they wanted or as non-restrictive as they wanted (non-restrictive by publishing the priority alias somewhere together with the pid) and everyone would be happy. I also don’t think that it will be that much of a hassle to keep track of a two-tuple {Pid, PrioAlias} compared to Pid.

I don’t see the suggested versions with match-specs and tagged tuples as good options, mainly since they add overhead for each message (but also for other reasons) which will add up, especially in the (in my opinion very important) scenarios with long message queues. If you should be able to configure these things in runtime it gets even worse where you also run into synchronization issues.

1 Like

Correcting myself, this is actually not the case with the tagged tuple proposal.

Second correction. It actually depends…

I had reservations about these priority messages when the proposal was in the “only processes I list here may send priority messages to me” phase, which was way too restrictive. But the priority alias approach and being selective in how widely you publish your priority alias seems like a good solution.

3 Likes

Thank you for taking the time to reply to my proposal.

A process gives its “consent” to other processes to send it signals when it shares its pid.

Yes, that’s a good point. Most of my comments were about the original proposal, where you had to tag who sent the message. I do agree priority aliases do solve some of my concerns.

I do worry about the need to track how aliases go through the system to figure out which messages are a priority. On the other hand, you are right, I can simply manually tag all priority messages, and organize that in the code myself. Perhaps a recommendation to be added to the docs. :+1:


Something else to consider is that both the original and the aliases proposal will likely require a combination of monitoring, linking, or supervision trees to be used with named processes. As you said, a process gives consent to other processes to send it signals by sharing its pid, and in many cases this is done by a registry. However, if you get the alias from a named process, store it in your state, and the process crashes, the alias is now dead, and your message will go into the void.

This means that the processes need to be linked, be part of the same supervision, or you need to monitor, making sure you retrieve a new alias somehow. This also has an impact on the design of priority ad-hoc messages, such as the system ones, if that’s ever desired. Of course, you cannot request a priority alias just in time, as that request itself won’t be a priority and you are back to square one. You can find alternative solutions, such as having the process store its alias on a ETS table during init or have a process whose sole job is to forward priority messages, but that’s additional human and computer work.

The original proposal had a similar issue but in the other direction, the receiver had to monitor the callers it wanted to prioritize. I believe those issues do not exist if you put the “burden of proof” on the message itself. At the end of the day, both proposals want to prevent abuse, but one does so by choosing the messages and the other by choosing the sender.

EDIT: The above is not meant to be a blocker but mostly a disclaimer. It is clear this feature is for power users, so it is probably fine to assume they’d be familiar with the failure semantics and properties. :slight_smile:

1 Like

Something else to consider is that both the original and the aliases proposal will likely require a combination of monitoring, linking, or supervision trees to be used with named processes.

With named processes the sender doesn’t know the receiver beforehand (conceptually), and who is an unknown sender to say what has higher priority (edit: as compared to, say, a supervisor telling one of its children that it should terminate)? That strikes me as an odd protocol but let’s roll with it.

Your registry is free to store {Pid, PrioAlias} together. Even with normal messages, you need to monitor whoever you expect a reply from, so I don’t see the problem in monitoring Pid and sending the message over PrioAlias. It’s no different from monitoring Pid and sending the message over Pid.

Edit: if we put it differently, your paragraph …

However, if you get the alias from a named process, store it in your state, and the process crashes, the alias is now dead, and your message will go into the void.

Can also be phrased as if you get the pid from a named process, store it in your state, and the process crashes, the pid is now dead, and your message will go into the void.

2 Likes

You are definitely correct. The only contention part is this sentence:

If I convert a name to a process, then the process may die, and the PID is useless. But the named registry allows me to not do or care about it. But, as far as I know, there is no way to store the process + alias information in Erlang/OTP without resorting to a custom registry implementation (or using distributed ones which comes with other trade-offs). Of course, one can always be added.

You are also right that allowing an unknown sender to send a priority message may be weird, I can’t think of practical use case for it right now besides perhaps system messages, but I’d be remiss to not mention it. :slight_smile:

Of interest here is that because this mechanism adds some wrapping, the related changes no longer only have implication on mailbox ordering, but also on the structure of messages handled.
For one, it becomes possible for a third party to parade a non-priority message as a priority message, by knowing which tag is used and wrapping messages that way. Now you get non-priority messages handled by high-priority functions, and we replicate some of the reasons why return values in functions tend to require clearly tagged {ok, Val} | {error, Reason} structures that remove all ambiguity.

The other oddity here is that a given message M can present both as M or as {Tag, M} depending on process flags, meaning that in order to properly handle changes in priority order of the message queue, you also have to change the pattern matching rules with awareness of the general process’s options and state.

If you want to keep the “consent” mechanism proposed through aliases to make sure nobody accidentally sends you low priority messages disguised as high priority messages, you would either need to support multiple priority tags, or the ability to put a unique value in the flag (by generating a unique atom dynamically). While this isn’t the same as getting actual consent by pre-sharing the secret, it does require almost one half of the solution.

1 Like

One mechanism aims to control who can send priority messages, but once you get that access, you can send any message as priority, even messages that are not intended to. The other mechanism aims to control which messages are handled as a priority while giving everyone access.

The parading is not a concern to me because I would say many processes would crash today anyway if you start parading messages. It is more important to guarantee that low-priority messages are not accidentally handled as high and vice-versa (and you are right, to be 100% sure, you would have to fully wrap all messages).

Expanding on your last paragraph, the solutions could be combined too. You could include a tag in the priority alias, which is automatically added by the time of sending, and now you limit both who and what can be sent as a priority message. But I am not sure it is a good idea.

Never thought I’d see the day.

I do like Fred’s point about appups.

I’ve updated the the EEP 76. The process_flag/2 flags for identifying messages to handle as priority messages have been replaced by priority aliases and priority options.

4 Likes

What happens when attempting to send a prioritized message with a non-priority alias?

1 Like

It will be handled as an ordinary message.

After re-reading the posts here, I’m still not really convinced of the need for priority messages. I want to be very careful about giving my opinion here because it could very well be that I have never really run into any major problems with a full mailbox. In my experience, building a mailbox queue is an application problem and not a prorgamming problem. In the event that priorities do demonstrably need an OTP priority queue module or something similar or use an existing library, but don’t try to build this into the language itself.

1 Like

In my experience, building a mailbox queue is an application problem and not a prorgamming problem.

Yes that is true but the problem is that is if you get into the problem with a super long message queue, there is nothing you can do about it (except killing the process) and it is very hard to detect without using the said message queue.

Sure you can implement you own priority queue via ets or something else and poll that often enough, but you can’t use the message queue for that since it is very long and doing a selective
receive often enough to check the messages for a priority message will cause your overloaded
process to be even slower.

There is no good solution to the problem today so the erts (and/or language) needs to be extended by something to handle it. Priority messages are our proposed solution.

4 Likes

I see the priority messaging feature mainly as a good way to achieve what we once tried to to achieve (in a kinda clumsy way) with #8371: making early detection of parent exits and commands to child to shut down in a supervision tree possible (see the PR discussion for the motivation if you like). Making this possible for me is the main benefit.

Most of the concerns around the feature seem to be grounded in the misunderstanding that it is solely the senders decision if a message is deemed important or not. However, it is also (if not more so) the decision of the receiver if and to who he hands out an priority alias, if at all.

Also, I at least use to think of an Erlang application or library or system as a collaborative system. The processes composing such a system are not supposed to wage all-out wars on each other where priority messages are the new weapon of choice but to play nicely for the greater good of the environment. As far as that goes, I believe that library designers will tend to make wise choices when and when not to use priority messages. I mean, who would want to use a library that is greedy to the point where it harms the environment it is used in?

I’m not saying that the feature can’t be abused, or that it won’t be abused. There is bound to be a “priority messaging made easy”-library popping up, maybe more than one. However, abuse (in the sense of over-usage) should tend to bring things in such an environment close to what we have now. If everything is important, nothing is.

3 Likes