Skip to content

fix(orchestration): order post-sync actions and restart each unit once #17453

Closed

Description

@mrveiss

Parent: #17452

Problem

get_node_actions (autobot-slm-backend/api/roles.py:576) builds a node's post-sync list
role-major — walk the roles, extend per role — so the order is role iteration order and two
things go wrong.

A restart can precede the install it depends on. On the reported bar, Restart autobot-celery
was at position 9 while Install deps (Shared Library) was at 13 and Run migrations (Backend API)
at 15.

The same systemd unit is scheduled twice, in two ways:

Fix

autobot-slm-backend/api/_post_sync_plan.py, called from get_node_actions:

  • order install → schema → build → restart, stable within a category;
  • an unknown category sorts into the middle rather than after the restarts, so a category added to
    _classify_post_sync and not to the rank table degrades safely;
  • every unit scheduled at most once, taking larger restart groups first — a role grouping
    autobot-backend with autobot-celery means they come back together, so the group survives whole
    and the standalone subset is dropped rather than the group being shrunk.

Separate module because roles.py is at its size ratchet pin (691, shrink-only).

PostSyncActionBadges.vue numbers the badges — a numeral, not a word, so it adds no string to
eleven locales — and its docstring records that the order is the server's and must not be re-sorted
in the view.

Acceptance criteria

  • No restart action appears before any install, schema or build action.
  • No systemd unit appears in more than one action of a node's plan.
  • A restart group that contains another action's units entirely survives; the subset is dropped.
  • A partial overlap keeps the unscheduled remainder and its label matches what it restarts.
  • An unknown category sorts before the restarts.
  • Ordering is stable within a category, so output changes only when roles do.
  • A restart action with no unit is kept, not swallowed — dropping it would hide a misconfigured
    role behind a tidy list.
  • A plan with no duplicates is returned unchanged (the control that stops the above being
    satisfied by dropping restarts).
  • The view does not sort, filter or group the actions itself.

Activity

  1. mrveiss commented on Sep 25, 2026

    @mrveiss
    OwnerAuthor

    Delivered by PR #17456, merged to main as 4333c47fb0. Every criterion verified against merged
    code read back from base (git show origin/main:<path>), not against the PR diff.

    AC Evidence on origin/main
    No restart before install/schema/build _post_sync_plan.py:48 _CATEGORY_RANK = {"install": 0, "schema": 1, "build": 2, "restart": 4}, applied at :113 sorted(others, key=_rank) with restarts appended after. Test: test_a_restart_never_precedes_an_install, test_every_category_lands_in_dependency_order
    No unit in more than one action _without_duplicate_units() at :63. Tests: test_two_roles_owning_one_unit_produce_one_restart, test_a_unit_inside_a_group_is_not_restarted_alone_as_well
    Superset group survives, subset dropped :78 sorted(restarts, key=lambda a: -len(_units(a))) — largest group first, so a subset finds its units already scheduled. Test: test_the_intended_group_survives_and_the_subset_is_dropped
    Partial overlap keeps the remainder, label matches Test: test_a_partial_overlap_keeps_the_units_not_already_scheduled
    Unknown category before restarts :49 _UNKNOWN_CATEGORY_RANK = 3 against restart's 4, resolved at :60 via .get(..., _UNKNOWN_CATEGORY_RANK). Test: test_an_unknown_category_lands_before_the_restarts
    Stable within a category sorted is stable and _rank is the sole key. Test: test_two_roles_in_one_category_keep_their_incoming_order
    Restart with no unit kept, not swallowed Test: test_a_restart_with_no_unit_is_kept_rather_than_swallowed
    No-duplicate plan returned unchanged Test: test_a_plan_with_no_duplicates_is_returned_unchanged — the control that stops the dedupe criteria being satisfied by dropping restarts wholesale
    The view does not sort, filter or group PostSyncActionBadges.vue on main contains no .sort(, .filter(, groupBy, reduce( or computed(; it renders v-for="(action, index) in actions" at :104 and numbers steps with {{ index + 1 }} at :121, so order is the server's

    TestTheLiveBarThatPromptedThis replays the 22-button bar from the original report and asserts the
    two redundant restarts are gone, nothing restarts before the installs and the migration, and no
    action is lost.

    What this issue does NOT deliver, so it is not read as covering more than it does. The ordering
    is fixed on the manual surface only. The automated sync path still runs post-sync work per role
    (_sync_nodes_for_role), so a unit owned by two roles still restarts twice there — that asymmetry is
    #17455, and it is the reason #17455 asks for the same policy to be imported rather than
    reimplemented. Two of the owner's four original complaints (attribution, and being part of the
    code-sync process and its logs) remain open as #17454 and #17455.

    Closing: all nine criteria met with merged-code evidence.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions