Skip to content
quarry Artifact [8dc8a4c594]
Login

Artifact [8dc8a4c594]

Artifact 8dc8a4c594cf5a822eb8ac955d245e1f633dc612d4ac75f6b887cdc0b5bd4ada:


package main

import (
	"sync"
	"sync/atomic"
	"testing"
	"time"
)

// Startup registration runs a bounded number of repositories at once
// and finishes only when every one has: the window becomes the slowest
// store plus a little, not the sum. Serially, 26 stores took 46–97s
// and the forge refused every sync for that long (a3b9623e).
func TestRegistrationRunsBoundedAndWaitsForAll(t *testing.T) {
	const items, bound = 12, 4
	var inFlight, peak, done int64
	var mu sync.Mutex
	list := make([]int, items)
	for i := range list {
		list[i] = i
	}
	start := time.Now()
	eachConcurrently(list, bound, func(int) {
		cur := atomic.AddInt64(&inFlight, 1)
		mu.Lock()
		if cur > peak {
			peak = cur
		}
		mu.Unlock()
		time.Sleep(20 * time.Millisecond)
		atomic.AddInt64(&inFlight, -1)
		atomic.AddInt64(&done, 1)
	})
	took := time.Since(start)
	if done != items {
		t.Fatalf("FAIL: returned with %d of %d finished", done, items)
	}
	if peak > bound || peak < 2 {
		t.Fatalf("FAIL: peak in flight %d, want between 2 and %d", peak, bound)
	}
	// 12 × 20ms serially is 240ms; at 4 wide it is ~60ms. Generous
	// bound so a loaded machine does not flake, tight enough that
	// serial execution fails it.
	if took > 180*time.Millisecond {
		t.Fatalf("FAIL: took %s; that is the serial cost", took)
	}
	t.Logf("ok: %d items, peak %d in flight, %s", items, peak, took.Round(time.Millisecond))
}

// A bound below one still runs everything, one at a time.
func TestRegistrationBoundOfZeroStillRunsEverything(t *testing.T) {
	var n int64
	eachConcurrently([]int{1, 2, 3}, 0, func(int) { atomic.AddInt64(&n, 1) })
	if n != 3 {
		t.Fatalf("FAIL: ran %d of 3", n)
	}
	t.Log("ok")
}