What is Home Assistant's concurrency model?

I’m trying to understand in detail how some aspects of HA work internally. Specifically I’d like to understand how HA handles internal concurrency when executing automations, scripts etc.

NOTE: I am not talking about the parallel/sequence feature to parallelise actions within a script.

It is very common for things such as sensors to be referenced by multiple automations/scripts and for those automations/scripts to modify the state of other things that are also shared between automations and scripts, which in turn may also affect the value of sensors… Without some kind of well defined (and enforced) concurrency model this could be fraught with issues. Since I don’t see lots of issues being reported I am assuming that HA takes care of this, at least to some degree, and would like to understand how it does so.

Q1. Is it possible for multiple HA automations to be triggered concurrently?

Q2. When executing an automation, can other automations (and any scripts or services that they may invoke) also execute concurrently? If so are there any dependencies enforced, or sequencing applied?

Q3. Does HA provide any guarantees about automation (and script) execution such as some concept of isolation/transactions? For example, an executing automation/script does not see any changes made to shared objects by other concurrently executing scripts/automations while it is executing?

Q4. Does HA provide any equivalent to a mutex which is usable in automations and scripts?

If there is useful documentation on this available anywhere then by all means feel free to point me to it.

I think HA just run automations/scripts when a trigger is triggered, which normally means when a sensor value is updated.
Because sensors are often updated in intervals, then previous scripts are usually done when next update occur.
A sensor update can trigger multiple scripts.
If sensor values are needed in the scripts, then they are read at that time, which means there are no freeze and values can be updated in the time from script start to sensor read.

I do not know the answers to your questions but the following documentation may help to find the answer:

Also, I have re-tagged your post for the Developers section.

The concurrency model is not well-defined anywhere in the documentation. The only way to know how things really work is to read the source code.

I agree it is hard to find the answer to this. The only thing that I can add that may shed a little light on this is the Mode setting that is assiogbned individually to every script and every automation, and here are the options when I pull one of those up to see what it is (the same choices apply to scripts and automations). Essentially when a script or automation is triggered, it is handled under the assigned “mode”:

image

@KruseLuds That’s interesting. How do you access those options in the GUI I can’t seem to find a way to see/change them?

By going to the dots next to traces in an automation.

I take it you did read this Understanding automations - Home Assistant

at least some of your basic questions are answered/explained there.

more fundamental architecture on the event bus is found here Core architecture | Home Assistant Developer Docs

@Mariusthvdb yes, thanks, I have read that and other stuff in the same areas of the docs. Doesn’t really answer my questions sadly as I’m trying to understand concurrency implications (such as race conditions, deadlocks, …) between concurrent executions of different automations/scripts that happen to reference/modify the same objects. From what I have read so far, it seems like a Mutex type entity is potentially needed in such cases.

  1. Yes
  2. Yes. No
  3. No
  4. You could use an input_boolean
3 Likes

Yes. HA triggers create listeners for automations. These listeners are shared between automations if the listener is pointing to the same entity. When executing, new threads will be generated so that things run at the same time. Keep in mind that HA uses an event loop so some things may run in series if you look at the timestamp but it will be off by 0.001 seconds or less.

This was answered above.

Actions themselves are typically blocking. So they are ran in sequence and will wait until the action is complete before moving on.

For variables in the automation, yes. For entities outside, no.

Use an input boolean.

EDIT: Tinkerers answers are more succinct :wink: Beat me to the punch.

1 Like

Think of automations as separate instances that can run whenever. When in parallel mode, multiple instances of a single automation can occur. Each is isolated, however entities outside the automation may cause each instance to behave differently.

2 Likes

@petro Yes, that is kind of my point. However, I don’t think an input boolean is sufficient since it needs to support atomic test and set. i.e. an operation that will turn on the input boolean only if it is not already turned on, which is serialised for all other ‘test-and-set’ access to the input boolean and where the test and set operation indicates as an output whether it turned the boolean on or not. AFAIK, input boolean does not behave like that nor have such an operation (and indeed you would not want it to behave like that by default).

That’s how input booleans work already. Turn on events on input booleans that are on does not invoke other triggers.

In general HA suppresses same state state-changes.

And if it hypothetically didn’t, you can use an if statement in an automation to achieve your wanted results.

e.g.

if:
- condition: state
  entity_id: input_boolean.xxx
  state: 'off'
then:
- action: homeassistant.turn_on
  target:
    entity_id: input_boolean.xxx
1 Like

I’ve done that in the past, but always wondered if that wasn’t an extra step we could avoid, (since HA already has the catch for same state ) and as such be more efficient without.

theoretically speaking.

(most used usecase probably would be turn_off lights that are on… simply send turn_off would suffice, no need to check for lights being on)

1 Like

It’s not needed at all for input_booleans. For hardware backed entities, it may be needed.

2 Likes

from Programming perspective, it is using async model via asyncio library, so assuming that there is one thread running the even loop, everything is running sequentially. Of course, long pole requests are driven outside of main loop so main loop can execute other non-blocking calls. So, it is benefiting somehow parallelization without managing the threads.

but, on user perspective, many things seem to be happening at the same time, eventually the code is executed sequentially following asyncio model.

more information here: Asynchronous programming | Home Assistant Developer Docs

1 Like

@fuatakgun well, that’s not really true though. Sure, segments of code are executed sequentially (all code between calls to yield) but from a user’s perspective that doesn’t really help much, unless we have a detailed understanding of how HA breaks down the execution of scripts, automations etc. into asyncio tasks and where the yields may occur in those tasks.

Consider two HA scripts (or automations) A and B executing ‘concurrently’ (from the user’s perspective). Both scripts contain the following code:

if input_boolean.lock is off
then
    turn on input_boolean.lock
else
    throw an error
endif

Even with the described asyncio based model, without understanding exactly how HA executes the scripts containing this code we don’ know if a race condition is possible. i.e. is it possible that both A and B might execute the ‘if … is off’ (and both see that it is indeed off) before either of them turns it on? If so, both will then turn it on and so we cannot use an input_boolean as a mutex. If HA guarantees that under any and all circumstances the if test and the subsequent turning on of the input_boolean are executed as an atomic unit then we are good. This is, I suppose, the essence of my original questions.

As an aside that may be interestiong or useful for some, for automations that fail, there is even a “rertry” integration :slight_smile:

Like I already told you, the only way to know is to carefully analyse the source code.
You can start here.