Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions bench/lanelab/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@ go run ./bench/lanelab/gosim -proof -pace flat -trace # one line per t
implementation agreeing with the first is not evidence about the build. So
`gosim` replays the same three scenarios against the code that ships:
`lane.Default()`'s real ledger primed from this directory's sheet fixture at
`lane.SheetWeight`, the real chooser, the real `lane.Watch`, the real
`lane.DefaultBudget()`, over `internal/lane/lanestub` — the fake router, which
`lane.SheetWeight`, the real chooser, the real `lane.Watch`, and the real purse
the plan carries (`lane.Spending(lane.PlanFor(...))`), over `internal/lane/lanestub` — the fake router, which
honours `provider.order`/`only`/`ignore`, names the serving lane on every chunk,
sends a usage frame with the exact cost, and counts the streams a client
abandoned. **The ship decision is taken on the Go run** (see
Expand Down
11 changes: 6 additions & 5 deletions bench/lanelab/gosim/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -591,11 +591,12 @@ func run(w *world, s scenario, policy string, seed, n, speedup int, trace bool)
r := &router{
world: w, scen: s, policy: policy, seed: seed,
stub: stub, ledger: ledger,
// THE DESIGN'S OWN BUDGET, and it is the shipped one rather than a
// figure written here: two hedges in any twenty requests and a tenth of
// recent spend. An arm that picked its own allowance would be measuring
// a router nobody ships.
budget: lane.DefaultBudget(),
// THE DESIGN'S OWN RAIL, and it is the shipped one rather than a figure
// written here: what a call may spend rescuing itself is its own patience
// converted through λ, which [lane.PlanFor] derives and [lane.Spending]
// reads. An arm that picked its own allowance would be measuring a router
// nobody ships.
purse: lane.Spending(lane.PlanFor(lane.Choice{}, lane.Pace{}, proofRole, theMoment)),
client: &http.Client{},
speedup: speedup,
total: n,
Expand Down
33 changes: 15 additions & 18 deletions bench/lanelab/gosim/proof.go
Original file line number Diff line number Diff line change
Expand Up @@ -1033,7 +1033,7 @@ func runProofSeed(w *world, s scenario, c proofCase, seed, n, speedup int, trace

p := &prover{
world: w, scen: s, kase: c, seed: seed, stub: stub, ledger: ledger,
budget: lane.DefaultBudget(), client: &http.Client{},
client: &http.Client{},
speedup: speedup, total: n, at: theMoment, trace: trace, pace: pace, mix: mix,
policy: policy,
}
Expand Down Expand Up @@ -1172,7 +1172,6 @@ type prover struct {
seed int
stub *lanestub.Server
ledger lane.Ledger
budget *lane.Budget
client *http.Client
speedup int
total int
Expand Down Expand Up @@ -1373,7 +1372,7 @@ func (p *prover) headFor(choice lane.Choice) string {
func (p *prover) plan(choice lane.Choice, head string) control.Plan {
plan := lane.PlanFor(choice, p.paceFor(head), proofRole, p.at)
plan.Pinned = len(choice.Only) > 0
plan.Purse = lane.Spending(p.budget)
plan.Purse = lane.Spending(plan)
plan.Think = lane.Thinks(p.world.model, "", p.at)
return plan
}
Expand Down Expand Up @@ -1523,8 +1522,9 @@ func (p *prover) mark(out *trial, act control.Act, asked time.Time, late time.Du
// answer carries out the first act.
//
// A HEDGE GOES OUT AS A DEMAND, to the lane the controller named, and the purse
// is SPENT here rather than where it was asked: [lane.Budget.Allow] counts the
// arm it allows, and the controller only ever asked whether one was affordable.
// is asked again here rather than trusted from where the controller asked it:
// the price is this arm's own and the rail is the CALL'S BUDGET
// ([lane.Spending]), which asks and never spends, so asking twice costs nothing.
// An offer with a reader is left standing, because a pin is asked and this bench
// has nobody to answer with. An offer with nobody there becomes the borrow §E
// describes — and that conversion is this file's, not the build's.
Expand All @@ -1534,10 +1534,9 @@ func (p *prover) answer(ctx context.Context, act control.Act, primary *armed, se
return false
}
price := p.priceOf(act.Lane)
if !p.budget.Allow(p.at, price) {
if purse := p.plan(lane.Choice{}, act.Lane).Purse; purse != nil && !purse.Allows(price, p.at) {
return false
}
p.budget.NoteHedge(price, p.at)
out.armed, out.armPrice = true, price
out.usd += price
arm := p.open(ctx, nil, []string{act.Lane})
Expand Down Expand Up @@ -1598,8 +1597,6 @@ func (p *prover) close(index int, served string, out trial) {
if out.thought && out.answered {
lane.NoteThought(p.world.model, "", thinkFor, p.at)
}
p.budget.NoteRequest(p.at)
p.budget.NoteSpend(out.usd, p.at)
if p.trace {
fmt.Fprintf(os.Stderr, "proof %-22s/%d %4d served %-14s %-36s action %7.2fs s %7.2fs $%.6f%s\n",
p.kase.name, p.seed, index, served, kindWords[out.kind]+"/"+out.reason+fmt.Sprintf(" W%.2f A%.2f", out.wait, out.cost), out.action, out.silence, out.usd,
Expand Down Expand Up @@ -1962,17 +1959,17 @@ func proofGates(rows []proofRow, store, mix, policy string) []proofGate {
return out
}
if !steady || !natural {
// THE PURSE IS WHAT BOUNDS SPENDING NOTHING ELSE BOUNDS, and it is what
// grades the bill wherever §K's own clause is not deciding it: on a cold
// store, where the arms are exploration, and on the stress mix, where
// the total is mostly correct rescues. The ceiling is read off the
// shipped budget rather than restated here, because a figure written
// down twice is a figure that will drift from the one the build
// enforces.
purse := 100 * lane.DefaultBudget().Share()
// WHAT BOUNDS SPENDING IS NOW PER CALL, SO THE AGGREGATE BAR IS THE
// BENCH'S OWN. The shipped rail is [control.Plan.SpendUSD] — one call's
// patience converted through λ — and it publishes no share of a bill for
// a grader to read. A bench is the thing that states a bar, so this one
// states it: a tenth of the bill is what the deleted purse allowed, and
// holding the new rail to the old number is what makes the replacement a
// claim rather than a hope.
const purse = 10.0
out = append(out, proofGate{
Criterion: "loser spend inside the purse",
Threshold: fmt.Sprintf("<= %.0f%% of the bill, which lane.DefaultBudget() allows", purse),
Threshold: fmt.Sprintf("<= %.0f%% of the bill, which the deleted purse allowed", purse),
Measured: fmt.Sprintf("%.2f%% of $%.4f", spendPct, usd),
Where: "every case",
Value: spendPct, Gated: true, Pass: spendPct <= purse})
Expand Down
17 changes: 5 additions & 12 deletions bench/lanelab/gosim/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import (
"time"

"github.com/Agent-Field/aforge-v2/internal/lane"
"github.com/Agent-Field/aforge-v2/internal/lane/control"
"github.com/Agent-Field/aforge-v2/internal/lane/lanestub"
)

Expand Down Expand Up @@ -81,7 +82,7 @@ type router struct {
seed int
stub *lanestub.Server
ledger lane.Ledger
budget *lane.Budget
purse control.Purse
client *http.Client
speedup int
total int
Expand Down Expand Up @@ -264,10 +265,6 @@ func (r *router) one(index int) (record, error) {
map[bool]string{true: " HEDGED", false: ""}[out.hedged],
map[bool]string{true: " BROKEN:" + broken, false: ""}[broken != ""])
}
// THE BUDGET'S DENOMINATOR IS REQUESTS, so it is told about every one of
// them and not only about the ones that needed rescuing (Part III, C4).
r.budget.NoteRequest(r.at)
r.budget.NoteSpend(out.usd, r.at)

// The world moves on by what this took plus the seconds somebody spends
// reading whatever of it they read.
Expand Down Expand Up @@ -344,7 +341,7 @@ func (r *router) plain(order, only, ignore []string, req lane.Request) (answered
}

// race is one request with the shipped rescue on it: the real [lane.Watch] over
// the real [lane.Choice], and the real [lane.DefaultBudget].
// the real [lane.Choice], and the call's own budget ([lane.Spending]).
//
// THE WINNER IS THE FIRST STREAM TO SPEAK, which is what the shipped transport
// does (`internal/provider/hedge.go` picks the speaker). The loser is cancelled
Expand Down Expand Up @@ -403,14 +400,10 @@ func (r *router) race(choice lane.Choice, req lane.Request) (answered, error) {
case <-hedgeAt:
hedgeAt = nil
estimate := r.estimate(rescue)
if !r.budget.Allow(r.at, estimate) {
if r.purse != nil && !r.purse.Allows(estimate, r.at) {
continue
}
hedged = true
// A hedge is charged when it is FIRED and at the estimate it was
// allowed on, because the stream that loses is cancelled and never
// reports what it cost.
r.budget.NoteHedge(estimate, r.at)
altBegan = time.Now()
alt = r.start(ctx, nil, []string{rescue}, nil, req)
altFirst, altDone = alt.first, alt.done
Expand Down Expand Up @@ -495,7 +488,7 @@ func (r *router) priceOfServing(name string) float64 {
}

// estimate is what the alternative is expected to cost, which is what the
// budget is asked about.
// purse is asked about.
func (r *router) estimate(name string) float64 { return r.priceOfServing(name) }

// settle turns one finished stream into the answer, in the world's units.
Expand Down
18 changes: 16 additions & 2 deletions cmd/aforge-census/report.go
Original file line number Diff line number Diff line change
Expand Up @@ -489,9 +489,15 @@ func surprises(out io.Writer, rows, finishes []row) {
wildest float64
)
var (
cutByABound int
exhausted int
cutByABound int
exhausted int
rescueDenied int
)
// refusedBy is what stopped a rescue the controller had already called for,
// by the machine word the row carries. It is a reading of its own because a
// rescue that was ASKED FOR and did not happen is the one shape a census of
// waits cannot see any other way: the row looks like an ordinary slow call.
refusedBy := map[string]int{}
appliedBy := map[string]int{}
endedBy := map[string]int{}
started := map[string]bool{}
Expand Down Expand Up @@ -530,6 +536,10 @@ func surprises(out io.Writer, rows, finishes []row) {
cutByABound++
appliedBy[word]++
}
if word := strings.TrimSpace(r.Refused); word != "" {
rescueDenied++
refusedBy[word]++
}
if r.Exhaust() {
exhausted++
}
Expand Down Expand Up @@ -567,12 +577,16 @@ func surprises(out io.Writer, rows, finishes []row) {
fmt.Fprintf(out, "| closed by the transport because no path wrote it | %d | %d |\n", unwritten, len(finishes))
fmt.Fprintf(out, "| cut by a bound this build set, and says which | %d | %d |\n", cutByABound, len(finishes))
fmt.Fprintf(out, "| exhaust of a race that was won, counted as a failure until now | %d | %d |\n", exhausted, len(finishes))
fmt.Fprintf(out, "| a rescue the controller called for that never left, and says why | %d | %d |\n", rescueDenied, len(finishes))
if unwritten > 0 {
fmt.Fprintf(out, "\nClosed as: %s.\n", topOf(endedBy, 6))
}
if cutByABound > 0 {
fmt.Fprintf(out, "\nCut by: %s.\n", topOf(appliedBy, 6))
}
if rescueDenied > 0 {
fmt.Fprintf(out, "\nRescues refused by: %s.\n", topOf(refusedBy, 6))
}
if wildest > absurdSeconds {
fmt.Fprintf(out, "\nThe largest figure in seconds on any row is %g.\n", wildest)
}
Expand Down
2 changes: 1 addition & 1 deletion docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -519,7 +519,7 @@ throws away work that was already paid for.
| 3 | the controller, mid-stream | `internal/lane/control` | `W(s)` over the gap between two VISIBLE tokens exceeds acting, and leaving beats finishing | the same acts | the same two bounds. Commitment is the same inequality with the tokens already written on the other side |
| 4 | the ceiling | `internal/lane/roles.go` | the silence reaches the role's own patience, whatever is believed | something is done: hedge, ask, or report | `VisiblePatience` **10s** × the role's `Patience` (×0.5 probe … ×6 standing, judge, design) |
| 5 | the dead path | `internal/lane/watch.go`, `internal/provider/armwatch.go` | an act fired and NOTHING has reached this stream — no token, no comment | names the act `no heartbeat` and flags it, so no belief is charged for somebody's wifi | `lane.DeadPathFloor` **3s** of silence before the claim may be made at all: a handshake on a cold path can honestly outlast a fast lane's whole believed wait |
| 6 | the purse | `internal/lane/hedge.go` | asked at the instant an act would send | allows or refuses the arm; a refusal turns the act into a report | at most **a tenth** of the last hour's spend, counted over `requestWindow` **20** requests; `maxArms` **4** for one question |
| 6 | the purse | `internal/lane/hedge.go`, `internal/lane/watch.go` | asked before the controller decides AND before the arm sends, with one price | allows or refuses the arm; a refusal turns the act into a report and the row says `plan cannot pay` | THE CALL'S OWN BUDGET: `control.Plan.SpendUSD` = `Role.GiveUp()` × what a second of this role's wait is worth (`λ / AttentionValue`, over `AttentionValue` **90** s/$) — $1.00 for `talk`, $0.75 for `leaf.unattended`; `maxArms` **4** for one question, and it is the only thing that counts arms |
| 7 | the pacing wait | `internal/provider/retry.go` | a 429 | waits and re-sends; the phase clock says `paced` with the router's own `Retry-After` as a real countdown | `baseBackoff` **700ms** doubled and jittered, one wait never over `maxProviderWait` **1 min**; the call ends at `rateLimitAttempts` **6** / `watchedPacingBudget` **2 min** watched, `patientAttempts` **60** / `patientPacingBudget` **10 min** unwatched |
| 8 | the relax ladder | `internal/provider/endpoints.go` | a refusal about the request's SHAPE (400/404/unsupported parameter) | re-asks at once with one field dropped | **6 rungs**, then `maxFallbackModels` **2** other models — and **NO WAIT BETWEEN RUNGS**, because nothing here is backing off from a fault |
| 9 | admission | `internal/provider/limiter.go` | more than `limiterCeiling` **64** requests in flight | queues, and hands the next freed slot to the waiter | **no timer at all.** `sharedLimiter.acquire` selects on the queue and the caller's context and nothing else; a request held here is held by the caller's own deadline |
Expand Down
Loading
Loading