Review before merge: refuse a silent disagreement, and bound the sweep
Three things found reading this back, each of which would have been quiet. A consumer that keeps several holders of one provision (ADR 0094) gets a login per holder, and a provider derives from the login — so it would make a resource per holder while the consumer is told one value for the requirement. That is issue 124's own failure one case to the side: authenticate, then be refused on every object. Refused now, naming both ends. The sweep runs inside somebody's build and was unbounded. At most two hundred artifacts and sixty seconds, stopping at the first refusal because a store that refuses one refuses all; the rest is offered again next build. The citation and migration renumbers are in the commit before this one.
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/novox/mesh-controller/internal/artifacts"
|
||||
"github.com/novox/mesh-controller/internal/inventory"
|
||||
@@ -49,28 +50,43 @@ func collect(ctx context.Context, inv *inventory.Inventory) {
|
||||
return
|
||||
}
|
||||
|
||||
// **Bounded, because this runs inside somebody's build.** The first sweep of a mesh that has
|
||||
// never collected has the whole history to get through, and a person waiting on `build` should
|
||||
// not pay for it. Two bounds, and what is left over is simply offered again next time —
|
||||
// builds are frequent, and the point is that the store stops growing, not that it empties
|
||||
// tonight.
|
||||
within, stop := context.WithTimeout(ctx, sweepBudget)
|
||||
defer stop()
|
||||
store := artifacts.Store{Address: address}
|
||||
|
||||
var done []string
|
||||
var refused int
|
||||
for _, reference := range references {
|
||||
switch err := store.LetGo(ctx, reference); {
|
||||
case err == nil, errors.Is(err, artifacts.Gone):
|
||||
var left int
|
||||
for i, reference := range references {
|
||||
if i >= mostPerSweep || within.Err() != nil {
|
||||
left = len(references) - i
|
||||
break
|
||||
}
|
||||
err := store.LetGo(within, reference)
|
||||
if err == nil || errors.Is(err, artifacts.Gone) {
|
||||
// Gone is the outcome wanted, already true. Recorded so the next sweep does not ask
|
||||
// again for ever.
|
||||
done = append(done, reference)
|
||||
default:
|
||||
refused++
|
||||
if refused == 1 {
|
||||
// Once per sweep. A store that refuses one refuses all of them, and a hundred
|
||||
// identical lines would bury the reason.
|
||||
fmt.Fprintf(os.Stderr, "the artifact store kept %s: %v\n", reference, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
// **Stopped at the first refusal, not pushed through.** A store that refuses one refuses
|
||||
// all of them — deletion disabled, the store down, the network gone — so going on would
|
||||
// be a hundred identical failures and a hundred identical log lines in front of whoever
|
||||
// was building something.
|
||||
fmt.Fprintf(os.Stderr, "the artifact store kept %s, so nothing more was asked of it: %v\n",
|
||||
reference, err)
|
||||
left = len(references) - i
|
||||
break
|
||||
}
|
||||
|
||||
if len(done) > 0 {
|
||||
// Recorded outside `within`: the deletions happened, and losing the record of them because
|
||||
// the sweep ran out of budget would mean asking about them again for ever.
|
||||
if err := inv.MarkCollected(ctx, done); err != nil {
|
||||
// Said, and that is all: the artifacts are gone either way, and the only cost of an
|
||||
// unrecorded collection is that the next sweep asks about them again.
|
||||
fmt.Fprintf(os.Stderr, "the store let go of %d artifact(s) and the record of it did not keep: %v\n",
|
||||
len(done), err)
|
||||
return
|
||||
@@ -78,7 +94,15 @@ func collect(ctx context.Context, inv *inventory.Inventory) {
|
||||
fmt.Fprintf(os.Stderr, "the artifact store let go of %d artifact(s) the mesh no longer keeps\n",
|
||||
len(done))
|
||||
}
|
||||
if refused > 0 {
|
||||
fmt.Fprintf(os.Stderr, "%d artifact(s) were not collected; the next build asks again\n", refused)
|
||||
if left > 0 {
|
||||
fmt.Fprintf(os.Stderr, "%d more to collect; the next build asks again\n", left)
|
||||
}
|
||||
}
|
||||
|
||||
// mostPerSweep is how many artifacts one sweep will ask about. Enough that a mesh building
|
||||
// several times a day converges within days of this landing; small enough that no single build
|
||||
// waits on the whole backlog.
|
||||
const mostPerSweep = 200
|
||||
|
||||
// sweepBudget is the longest a sweep will keep a build waiting.
|
||||
const sweepBudget = 60 * time.Second
|
||||
|
||||
Reference in New Issue
Block a user