pacman keeps one mirror list for the mesh, ranked in the write that opens each round and written by every node (ADR 0585, issue 1876)
Each node's reflector ranked its own list on its own timer, and on 2026-10-31 one node's lagging first mirror left it a Node.js version behind the rest. The list is now the module's state mirrors, key list: the node that opens a week's round ranks it with the module's reflector options, keeps only mirrors whose last sync is no older than the newest found, and the round's key names the list's revision. Every keeper writes /etc/pacman.d/mirrorlist whole from the key through the root helper's new verb mirrorlist, when the key changes and when the file differs, and never writes an empty or unreadable list. reflector.timer is declared stopped and disabled, and reflector.conf saves nothing. pacman_mirrors answers the shared list, its ranker and age and whether this node's file matches; with refresh it asks this node's keeper to rank again, refused while a round runs, naming it. The round's integration test proves the opening write and both nodes' files. Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
This commit is contained in:
@@ -8,7 +8,8 @@ package main
|
||||
//
|
||||
// - pacman's database lock released (an inotify watch on its directory: the lock file deleted) ends a
|
||||
// transaction, whoever ran it: `installed` and then `updates` are read again;
|
||||
// - the mirror list rewritten (reflector writes /etc/pacman.d/mirrorlist): `updates` is read again;
|
||||
// - the mirror list rewritten (the keeper writes /etc/pacman.d/mirrorlist from the mesh's list, ADR 0585): it is
|
||||
// written again when it differs from that list, and `updates` is read again;
|
||||
// - a day after `updates` was last read: it is read again, since the repositories change what an upgrade
|
||||
// would do without any transaction on the node.
|
||||
//
|
||||
@@ -138,6 +139,8 @@ type Hooks struct {
|
||||
AfterTransaction []func(k *Keeper, t Transaction) error
|
||||
Changes []StateHook
|
||||
Wakes []Wake
|
||||
// MirrorsRewritten: after the mirror list file was written or replaced, before `updates` is read again.
|
||||
MirrorsRewritten []func(k *Keeper) error
|
||||
}
|
||||
|
||||
// keeperHooks is the hooks the keeper runs with, built once the keeper is connected: the one place a part of
|
||||
@@ -159,7 +162,15 @@ func keeperHooks(k *Keeper) Hooks {
|
||||
u := &Upgrades{Node: k.Node, Config: k.Config, Machine: k.Machine, Root: root, Events: k.Events,
|
||||
Installed: k.State(StateInstalled), Updates: k.State(StateUpdates), Runs: k.State(StateRuns),
|
||||
Records: k.State(StateRecords), Rounds: k.State(StateRounds), Asks: k.State(StateAsks), AskRecord: k.AskRecord,
|
||||
Check: k.Check}
|
||||
Check: k.Check, Mirrors: k.State(StateMirrors), Rank: k.Rank}
|
||||
// writeMirrors writes this node's mirror list file from the shared list when they differ (ADR 0585 §3).
|
||||
writeMirrors := func(*Keeper) error {
|
||||
wrote, err := u.WriteMirrors()
|
||||
if wrote {
|
||||
k.logf("[pacman] keeper: %s written from the mesh's mirror list", Mirrorlist)
|
||||
}
|
||||
return err
|
||||
}
|
||||
k.upgrades = u
|
||||
const roundWake = 1 // the round's step is the second wake
|
||||
roundStep := func(k *Keeper, now time.Time) time.Time {
|
||||
@@ -199,7 +210,16 @@ func keeperHooks(k *Keeper) Hooks {
|
||||
}},
|
||||
{State: StateRounds, Key: "**", Handle: onRoundChange},
|
||||
{State: StateRuns, Key: "**", Handle: onRoundChange},
|
||||
// The mesh's mirror list: written to this node's file at the keeper's start and on every change (ADR 0585 §3).
|
||||
{State: StateMirrors, Key: MirrorsKey, Handle: func(k *Keeper, c stdio.StateChange) error {
|
||||
if c.Op != "put" {
|
||||
return nil
|
||||
}
|
||||
return writeMirrors(k)
|
||||
}},
|
||||
},
|
||||
// And the file written again when it differs from the list: a hand edit, or reflector run by hand.
|
||||
MirrorsRewritten: []func(*Keeper) error{writeMirrors},
|
||||
Wakes: []Wake{
|
||||
func(k *Keeper, now time.Time) time.Time {
|
||||
if !now.Equal(k.started) {
|
||||
@@ -239,6 +259,10 @@ func (noRoot) Rollback(run string, plan []RollbackItem) error {
|
||||
return errors.New(noRootSaid + ": no rollback can start")
|
||||
}
|
||||
|
||||
func (noRoot) Mirrorlist([]string) error {
|
||||
return errors.New(noRootSaid + ": no mirror list can be written")
|
||||
}
|
||||
|
||||
func (noRoot) Maps() ([]Mapped, error) {
|
||||
return nil, errors.New(noRootSaid + ": no process's maps can be read")
|
||||
}
|
||||
@@ -267,9 +291,11 @@ type Keeper struct {
|
||||
// AskRecord reads the operator channel's record of one of the module's asks (process.Conn.Ask).
|
||||
AskRecord func(id string) (*stdio.StateEntry, error)
|
||||
// Root carries out the acts that need root; nil is the root helper's client (newRootActs). Check is the
|
||||
// after-upgrade check; nil is the machine's (AfterCheck). A test sets both.
|
||||
// after-upgrade check; nil is the machine's (AfterCheck). Rank ranks the mesh's mirrors (RankMirrors, set by the
|
||||
// process); nil ranks none. A test sets them.
|
||||
Root RootActs
|
||||
Check func(run Run, record *Record) Check
|
||||
Rank func(conf []byte, round string) (MirrorList, error)
|
||||
|
||||
upgrades *Upgrades
|
||||
|
||||
@@ -390,6 +416,11 @@ func (k *Keeper) signal(s Signal) {
|
||||
}
|
||||
}
|
||||
case MirrorsRewritten:
|
||||
for _, h := range k.Hooks.MirrorsRewritten {
|
||||
if err := h(k); err != nil {
|
||||
k.logf("[pacman] keeper: the mirror list rewritten: %v", err)
|
||||
}
|
||||
}
|
||||
k.refreshUpdates()
|
||||
}
|
||||
}
|
||||
@@ -606,6 +637,10 @@ func keeper() int {
|
||||
DBChanged: func() time.Time { return modified(filepath.Join(PacmanDBDir, "local")) },
|
||||
MakeHooks: keeperHooks,
|
||||
}
|
||||
// It ranks the mesh's mirrors with reflector when it opens a round or is asked a refresh (ADR 0585 §2, §4).
|
||||
k.Rank = func(conf []byte, round string) (MirrorList, error) {
|
||||
return RankMirrors(conf, k.Node, k.Clock.Now(), round)
|
||||
}
|
||||
logf("[pacman] keeper of %s is starting as %s", account.Node, account.User)
|
||||
if err := runKeeper(ctx, account, k); err != nil && ctx.Err() == nil {
|
||||
logf("[pacman] keeper stopped: %v", err)
|
||||
|
||||
Reference in New Issue
Block a user