I've been reading the code, and I think this is pretty safe, at least on server with MySQL 5.7.5+ or MariaDB 10.0.2+ (if your database is older than this, it's pretty old).
The code that updates smart groups has various checks in place to make sure it only does each group once in a request.
All the contacts in a smart group are stored in a "cache" table. (A cache is a temporary store of data that can be recreated as needs be, e.g. when updating the smart group). The cache is entirely separate from the manually added/removed contacts; the cache contains and only contains all the contacts in the group, whether they were included manually or because they match the criteria.
The process is:
Delete all the cache records for this group. This is important in the example of this question, since it means the search for "contacts whose related contact is in the group" will only be looking at the related contacts that are manually included in the group 👍
Creates a temporary table to store new cache entries.
Add to the temporary store by running the search, excluding any that are manually removed. In this question: this means we'll be adding the individuals that are related to the local campaign groups, unless they have been manually removed from the group for whatever reason.
Add to the temporary store a copy of all those manually added. Of course these contacts may already be in there (from step 2), but that's fine, it ensures every contact is only included once. By now, the temporary store has a copy of everyone that should be in the group. In our example, it means the temporary store now includes:
- All the local campaign groups (and possibly other contacts) that were manually added to the smart group.
- All the activists related to the local campaign groups, unless they had been manually removed.
But it goes on if this group has any child (sub) groups. This is not relevant to this question but I thought I'd include it for completeness' sake...
For each child group, it looks up the contacts in that, exclude any manually excluded from our group, and adds these to our temporary store (again, ignoring any duplicates).
It will also refresh the cache of any child smart groups, so if there was a smart group within the child groups that referenced our main smart group (!) it would not result in an infinite loop because it knows we've just updated [are updating] that, but I'm unclear what contacts it would see because:
- if it goes on the cache table, that's empty at this point.
- if it goes on (manually added + cache table) then it will see the manually added contacts only.
Finally, it empties our group's cache (again? I wonder why...) and copies all the entries from our temporary store into the cache table, and we're done!
So: I think, miraculously, this should actually work and be safe for the purpose stated in my original question!
Please comment, or post other answers though if you have any experience to share.