fix(patch): gate phases on the window deadline, queue undelivered results, retry startup inventory
The window deadline no longer kills a running package manager: it only gates the start of each phase, and a started upgrade runs under a 2 hour backstop that sends SIGTERM on Linux. A PatchResult whose send fails is queued and flushed on the next command stream, retaking the reboot decision. deb822 folded Suites continuation lines are filtered with the field. The startup static inventory report is retried until it succeeds.
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
package agentsync
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"gitea.hostxtra.co.uk/vantage/vantage-shared/grpc/pb"
|
||||
)
|
||||
|
||||
// maxPendingResults bounds the results kept for a later stream. A host that
|
||||
// cannot reach the control plane for a very long time drops its oldest
|
||||
// results rather than growing without limit.
|
||||
const maxPendingResults = 32
|
||||
|
||||
// pendingResult is a PatchResult that could not be sent, with what the reboot
|
||||
// decision needs to be taken again when it finally can be.
|
||||
type pendingResult struct {
|
||||
msg *pb.AgentMessage
|
||||
requested bool // the command's RebootIfRequired
|
||||
deadline time.Time // the command's deadline, zero for a manual run
|
||||
}
|
||||
|
||||
// resultQueue holds undelivered PatchResults until the next command stream.
|
||||
type resultQueue struct {
|
||||
mu sync.Mutex
|
||||
items []pendingResult
|
||||
max int
|
||||
}
|
||||
|
||||
// push appends p, dropping the oldest entry when the queue is full. It
|
||||
// reports whether an entry was dropped.
|
||||
func (q *resultQueue) push(p pendingResult) bool {
|
||||
q.mu.Lock()
|
||||
defer q.mu.Unlock()
|
||||
q.items = append(q.items, p)
|
||||
if len(q.items) > q.max {
|
||||
q.items = q.items[len(q.items)-q.max:]
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// flush sends the queued results oldest first, removing each once sent. It
|
||||
// stops at the first failure and keeps that entry and everything after it
|
||||
// for the next stream. The lock is held throughout so two streams never
|
||||
// deliver the same result.
|
||||
func (q *resultQueue) flush(send func(pendingResult) error) {
|
||||
q.mu.Lock()
|
||||
defer q.mu.Unlock()
|
||||
for len(q.items) > 0 {
|
||||
if err := send(q.items[0]); err != nil {
|
||||
return
|
||||
}
|
||||
q.items = q.items[1:]
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user