The consume side behind a seam, and the window wiring into the loop
The outbound half went behind `Bus` and the transport stopped reaching its callers; this is the other half, and the larger one. Every handler took `amqp.Delivery`, so the serving loop could not move to another bus without moving enrolment, reports, builds, upgrades and catch-up with it in one breath. `Control` states one message in the mesh's words — took it, dropped it, or held it for the store — and `Inbound` is where messages come from. The AMQP implementation is today's loop moved rather than changed: same queues, same prefetch, same holding, because the mesh is running on it and a bus nothing speaks yet is no reason to alter the one every node is on. The window (window.go) is now what decides, instead of the conditions that were inlined in the loop. Two things that surfaced in the wiring: **Supersession is asked before the store, not after.** A report about a declaration the mesh has moved past would otherwise wait out a restarting store to be written and then overwrite what the node is doing now. **Half of a report is not about a declaration, and that half is never stale.** What the machine *is* — the tunnel it took over, the ports its own bundle holds, what an adopted node found, a node moving its overlay key — reaches the mesh on a report and nowhere else. A rekey set aside as stale is a node whose overlay key never moves, and no retry is coming, because the node said it once. So staleness is asked only of a report that is purely an apply's account. The one thing holding-in-memory can do that holding-in-the-server cannot is named rather than hidden: `About` sets aside a held message when a newer one about the same thing arrives, and the bus being built ignores it because the digest answers the same question.
This commit is contained in:
+15
-1
@@ -52,7 +52,7 @@ func (w StoreWindow) Decide(err error, declaredIn, outstanding string, heldFor t
|
||||
// **Staleness is checked before the store, not after.** A redelivery that lost its race is
|
||||
// not worth waiting on a store for, and asking the store first would mean a message about a
|
||||
// superseded declaration holding a slot in the window that a current one needs.
|
||||
if declaredIn != "" && outstanding != "" && declaredIn != outstanding {
|
||||
if Superseded(declaredIn, outstanding) {
|
||||
return Stale
|
||||
}
|
||||
if err == nil {
|
||||
@@ -69,6 +69,20 @@ func (w StoreWindow) Decide(err error, declaredIn, outstanding string, heldFor t
|
||||
return Hold
|
||||
}
|
||||
|
||||
// Superseded says a message is about a declaration the mesh has already moved past.
|
||||
//
|
||||
// Stated on its own because it is asked in two places for one reason: here, so the window's whole
|
||||
// decision is in one pure function, and by the serving loop *before* it asks the store, because
|
||||
// that is the point — a message about the past must not wait on a store, or it holds a slot in the
|
||||
// window that a current message needs.
|
||||
//
|
||||
// Unanswerable is not stale. A message that names no declaration, and a node the mesh has never
|
||||
// sent one, both give nothing to compare: the mesh acts on the message rather than guessing, which
|
||||
// is also what keeps a host built before reports carried the digest from going silent.
|
||||
func Superseded(declaredIn, outstanding string) bool {
|
||||
return declaredIn != "" && outstanding != "" && declaredIn != outstanding
|
||||
}
|
||||
|
||||
// RedeliverAfter is how long the server should hold a naked message before trying again.
|
||||
//
|
||||
// Backed off, and bounded. A store restarting is back in seconds; a store that is gone is not
|
||||
|
||||
Reference in New Issue
Block a user