Speed up looking up actions by attachment point.

Review Request #14639 — Created Oct. 16, 2025 and updated

Information

Review Board
release-7.1.x

Reviewers

When populating the UI, we fetch actions based on various attachment
points. When we do this, the registry has to iterate through every
action every time, returning each one matching the attachment point.
This is algorithmically slow, and only gets worse as we expand usage of
actions.

Now, when registering an action, the action gets placed in a cache of
attachment points to actions. Unregistering will remove that action from
the cache. This makes lookup very fast.

Unit tests pass.

Tested various parts of the UI, making sure the right actions are in
the right places.

Summary ID
Speed up looking up actions by attachment point.
When populating the UI, we fetch actions based on various attachment points. When we do this, the registry has to iterate through every action every time, returning each one matching the attachment point. This is algorithmically slow, and only gets worse as we expand usage of actions. Now, when registering an action, the action gets placed in a cache of attachment points to actions. Unregistering will remove that action from the cache. This makes lookup very fast.
1aadbfc839c86cccf3f52ceb3d31c692005070a4
Description From Last Updated

We should use a walrus operator here to make a variable out of action.parent_id, like you did below.

maubinmaubin

Can use action_id here too.

maubinmaubin

Might as well do the walrus operator here too.

maubinmaubin

I'm assuming this is part of the future proofing, where in the future action.attachment can possibly be a list of …

maubinmaubin

Same notes here about the attachment variable.

maubinmaubin

We need to add from __future__ import annotations to this file, especially because you're using dict[...] syntax for type hints.

daviddavid

Perhaps we should make _by_attachment_point a defaultdict(dict)?

daviddavid

Just in case an action failed to register properly, we probably should catch KeyError here and log.

daviddavid

Do we want to make it so we clean up empty dicts from the cache after actions are unregistered?

daviddavid

These older tests are still using the global actions_registry. Can we update these to construct a _TestActionsRegistry and then get …

daviddavid
maubin
  1. 
      
  2. reviewboard/actions/registry.py (Diff revision 1)
     
     
     
    Show all issues

    We should use a walrus operator here to make a variable out of action.parent_id, like you did below.

    1. I'm going to drop this one because it's code that's being fully rewritten in a later change. This affects pretty much everything in registry.py that's relevant here. I want to keep the file largely untouched up until that point for code archaeology reasons.

  3. reviewboard/actions/registry.py (Diff revision 1)
     
     
    Show all issues

    Can use action_id here too.

  4. reviewboard/actions/registry.py (Diff revision 1)
     
     
     
     
    Show all issues

    Might as well do the walrus operator here too.

  5. reviewboard/actions/registry.py (Diff revision 1)
     
     
     
     
     
    Show all issues

    I'm assuming this is part of the future proofing, where in the future action.attachment can possibly be a list of strings?

    We should name the attachments list attachments instead of having all variables be attachment. Note that the for loop variable is also named attachment. After the loop runs, the attachment list variable would now point to the last item in the list instead of the list itself. Doesn't really matter since we don't use the variable again, but its confusing and will cause problems in case we do use it in the future.

    How about something like this

    attachments = action.attachment
    
    if attachments:
        if isinstance(attachments, str):
            attachments = [attachments]
    
        for attachment in attachments:
            ...
    
    1. I probably should just nuke this, because it's nuked in a future change as part of the big change I mentioned in my earlier comment. All this is going away. I can just undo it for now.

  6. reviewboard/actions/registry.py (Diff revision 1)
     
     
     
     
     
     
     
     
    Show all issues

    Same notes here about the attachment variable.

  7. 
      
chipx86
maubin
  1. Ship It!
  2. 
      
david
  1. 
      
  2. reviewboard/actions/registry.py (Diff revision 2)
     
     
     
     
    Show all issues

    We need to add from __future__ import annotations to this file, especially because you're using dict[...] syntax for type hints.

  3. reviewboard/actions/registry.py (Diff revision 2)
     
     
     
     
     
     
    Show all issues

    Perhaps we should make _by_attachment_point a defaultdict(dict)?

  4. reviewboard/actions/registry.py (Diff revision 2)
     
     
    Show all issues

    Just in case an action failed to register properly, we probably should catch KeyError here and log.

  5. Show all issues

    Do we want to make it so we clean up empty dicts from the cache after actions are unregistered?

    1. Sure. Probably a rare enough event, but cleanup is easy.

  6. reviewboard/actions/tests/test_registry.py (Diff revision 2)
     
     
     
     
    Show all issues

    These older tests are still using the global actions_registry. Can we update these to construct a _TestActionsRegistry and then get rid of the tearDown() implementation instead?

    1. I'll tackle this separately. I don't want to add any more to this change.

  7. 
      
chipx86
Review request changed
Change Summary:
  • Moved to a defaultdict for the attachment point lookup.
  • Added logging when an action ID is missing from an attachment point during unregistration.
  • Empty attachment points are removed from the lookup table after unregistration.
Commits:
Summary ID
Speed up looking up actions by attachment point.
When populating the UI, we fetch actions based on various attachment points. When we do this, the registry has to iterate through every action every time, returning each one matching the attachment point. This is algorithmically slow, and only gets worse as we expand usage of actions. Now, when registering an action, the action gets placed in a cache of attachment points to actions. Unregistering will remove that action from the cache. This makes lookup very fast.
cf043b93c85f24b3bb432ce50774d56f1540c89a
Speed up looking up actions by attachment point.
When populating the UI, we fetch actions based on various attachment points. When we do this, the registry has to iterate through every action every time, returning each one matching the attachment point. This is algorithmically slow, and only gets worse as we expand usage of actions. Now, when registering an action, the action gets placed in a cache of attachment points to actions. Unregistering will remove that action from the cache. This makes lookup very fast.
1aadbfc839c86cccf3f52ceb3d31c692005070a4

Checks run (2 succeeded)

flake8 passed.
JSHint passed.
david
  1. Ship It!
  2.