mesh/delivery held for a person: merged without a passing check: only a person decides what follows
mesh/check checking e9a885cda925 on 62802cd96602
At 16:07:36 the builder rewrote about 120 done keys at once. Each woke a take-in of its own with no limit, and each take-in read the rest of the mesh through restOfTheMeshOf, which opened a licences and an identity pool per call. novox's store ran out of connections at 16:08 and refused everyone, the controller's retries keeping it full. - The serving controller lends its stores by inventory (shareStores); restOfTheMeshOf reads on them instead of opening pools per call, and a verb run as its own process still opens its own. stores opens each store once under a lock, since readers now share it. serve uses the shared identity store instead of a second pool. - The watch's take-ins and the start's look share one cap of 16 take-ins at once (takeInSlots); a take-in waiting for its next try holds none. - store.Ready backs off on a full store (SQLSTATE 53300), doubling its wait up to 5 s, and says once that the store is full. Issues 1945 and 1277.
59 lines
1.7 KiB
Go
59 lines
1.7 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"sync"
|
|
"sync/atomic"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// A burst of done keys runs at most startTakeIns take-ins at once, the watch's and the start's look together: at
|
|
// 16:07:36 on 2026-10-25 the builder rewrote about 120 done keys at once, each woke its own take-in, and each opened
|
|
// pools of its own until novox's store refused every connection (issues 1945 and 1277).
|
|
func TestABurstOfDoneKeysRunsAtMostSixteenTakeInsAtOnce(t *testing.T) {
|
|
was := doneKeyTakeOnce
|
|
t.Cleanup(func() { doneKeyTakeOnce = was })
|
|
var running, most atomic.Int64
|
|
var taken atomic.Int64
|
|
doneKeyTakeOnce = func(context.Context, *stores, string) error {
|
|
now := running.Add(1)
|
|
for {
|
|
m := most.Load()
|
|
if now <= m || most.CompareAndSwap(m, now) {
|
|
break
|
|
}
|
|
}
|
|
time.Sleep(2 * time.Millisecond)
|
|
running.Add(-1)
|
|
taken.Add(1)
|
|
return nil
|
|
}
|
|
ctx := context.Background()
|
|
var wg sync.WaitGroup
|
|
// The watch's wakes: one take-in each, in the background, as watchDoneKeys starts them.
|
|
for i := range 120 {
|
|
wg.Add(1)
|
|
go func() { defer wg.Done(); takeDoneKey(ctx, nil, fmt.Sprintf("burst-%03d", i)) }()
|
|
}
|
|
// And the start's look at the same moment, over other modules.
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
var start []string
|
|
for i := range 40 {
|
|
start = append(start, fmt.Sprintf("start-%03d", i))
|
|
}
|
|
doneKeysAtStart(ctx, func(context.Context) ([]string, error) { return start, nil },
|
|
func(m string) { takeDoneKeyAtStart(ctx, nil, m) })
|
|
}()
|
|
wg.Wait()
|
|
if got := taken.Load(); got != 160 {
|
|
t.Fatalf("took in %d modules, want 160", got)
|
|
}
|
|
if m := most.Load(); m > startTakeIns {
|
|
t.Fatalf("ran %d take-ins at once, want at most %d", m, startTakeIns)
|
|
}
|
|
}
|