Commit ee55b8c0ca
Verified · cmc
Layout: unified · split
internal/config/config.go +45
| @@ -24,6 +24,15 @@ import ( | ||
| 24 | 24 | // and webhook deliveries. |
| 25 | 25 | const DefaultWriteRate = 60 |
| 26 | 26 | |
| 27 | // Pack generation defaults for a four-core host: a full clone of a large | |
| 28 | // repository runs git at about 1.5 cores (Performance wiki page). | |
| 29 | const ( | |
| 30 | DefaultPackConcurrency = 3 | |
| 31 | DefaultPackPerPrincipal = 2 | |
| 32 | DefaultPackQueue = 32 | |
| 33 | DefaultPackQueueWait = time.Minute | |
| 34 | ) | |
| 35 | ||
| 27 | 36 | type Config struct { |
| 28 | 37 | Server Server `toml:"server"` |
| 29 | 38 | SSH SSH `toml:"ssh"` |
| @@ -214,6 +223,37 @@ type Limits struct { | ||
| 214 | 223 | // account. |
| 215 | 224 | MaxReposPerUser int `toml:"max_repos_per_user"` |
| 216 | 225 | MaxBytesPerUser int64 `toml:"max_bytes_per_user"` |
| 226 | // PackConcurrency caps git pack generation (upload-pack and | |
| 227 | // upload-archive) running at once across SSH, smart HTTP and git://. | |
| 228 | // PackPerPrincipal caps it per account, or per client address on the | |
| 229 | // anonymous transports. PackQueue is how many may wait for a slot, | |
| 230 | // for at most PackQueueWait ("60s"). For the three counts 0 takes the | |
| 231 | // default and a negative value turns that bound off. | |
| 232 | PackConcurrency int `toml:"pack_concurrency"` | |
| 233 | PackPerPrincipal int `toml:"pack_per_principal"` | |
| 234 | PackQueue int `toml:"pack_queue"` | |
| 235 | PackQueueWait string `toml:"pack_queue_wait"` | |
| 236 | } | |
| 237 | ||
| 238 | // PackLimits resolves the pack_* settings for packlimit.New. A zero | |
| 239 | // count is no bound. | |
| 240 | func (l Limits) PackLimits() (max, per, queue int, wait time.Duration) { | |
| 241 | pick := func(v, def int) int { | |
| 242 | switch { | |
| 243 | case v == 0: | |
| 244 | return def | |
| 245 | case v < 0: | |
| 246 | return 0 | |
| 247 | } | |
| 248 | return v | |
| 249 | } | |
| 250 | wait = DefaultPackQueueWait | |
| 251 | if d, err := time.ParseDuration(l.PackQueueWait); err == nil && d > 0 { | |
| 252 | wait = d | |
| 253 | } | |
| 254 | return pick(l.PackConcurrency, DefaultPackConcurrency), | |
| 255 | pick(l.PackPerPrincipal, DefaultPackPerPrincipal), | |
| 256 | pick(l.PackQueue, DefaultPackQueue), wait | |
| 217 | 257 | } |
| 218 | 258 | |
| 219 | 259 | type Mail struct { |
| @@ -444,6 +484,11 @@ func (c Config) Validate() error { | ||
| 444 | 484 | if c.Limits.MaxReposPerUser < 0 || c.Limits.MaxBytesPerUser < 0 || c.Limits.MaxSnippetsPerUser < 0 { |
| 445 | 485 | errs = append(errs, errors.New("limits.max_repos_per_user, max_bytes_per_user and max_snippets_per_user must not be negative")) |
| 446 | 486 | } |
| 487 | if w := c.Limits.PackQueueWait; w != "" { | |
| 488 | if d, err := time.ParseDuration(w); err != nil || d <= 0 { | |
| 489 | errs = append(errs, fmt.Errorf("limits.pack_queue_wait %q must be a positive duration such as 60s", w)) | |
| 490 | } | |
| 491 | } | |
| 447 | 492 | if c.Push.Enabled { |
| 448 | 493 | for _, f := range []struct{ name, val string }{ |
| 449 | 494 | {"push.key_file", c.Push.KeyFile}, |
internal/config/config_test.go +21
| @@ -12,6 +12,7 @@ import ( | ||
| 12 | 12 | "testing" |
| 13 | 13 | |
| 14 | 14 | "filippo.io/age" |
| 15 | "time" | |
| 15 | 16 | ) |
| 16 | 17 | |
| 17 | 18 | func writeConfig(t *testing.T, body string) string { |
| @@ -52,6 +53,11 @@ func TestContradictions(t *testing.T) { | ||
| 52 | 53 | body string |
| 53 | 54 | wantErr string |
| 54 | 55 | }{ |
| 56 | { | |
| 57 | "bad pack_queue_wait", | |
| 58 | minimal + "\n[limits]\npack_queue_wait = \"soon\"\n", | |
| 59 | "limits.pack_queue_wait", | |
| 60 | }, | |
| 55 | 61 | { |
| 56 | 62 | "registration open without smtp", |
| 57 | 63 | minimal + "\n[registration]\nmode = \"open\"\n", |
| @@ -389,3 +395,18 @@ func TestBackupRecipients(t *testing.T) { | ||
| 389 | 395 | t.Fatalf("default: %v, %v", cfg.Backup, err) |
| 390 | 396 | } |
| 391 | 397 | } |
| 398 | ||
| 399 | func TestPackLimits(t *testing.T) { | |
| 400 | max, per, queue, wait := Limits{}.PackLimits() | |
| 401 | if max != DefaultPackConcurrency || per != DefaultPackPerPrincipal || queue != DefaultPackQueue || wait != DefaultPackQueueWait { | |
| 402 | t.Fatalf("defaults: %d %d %d %s", max, per, queue, wait) | |
| 403 | } | |
| 404 | max, per, queue, wait = Limits{PackConcurrency: -1, PackPerPrincipal: -1, PackQueue: -1, PackQueueWait: "5s"}.PackLimits() | |
| 405 | if max != 0 || per != 0 || queue != 0 || wait != 5*time.Second { | |
| 406 | t.Fatalf("off: %d %d %d %s", max, per, queue, wait) | |
| 407 | } | |
| 408 | max, per, queue, _ = Limits{PackConcurrency: 8, PackPerPrincipal: 3, PackQueue: 64}.PackLimits() | |
| 409 | if max != 8 || per != 3 || queue != 64 { | |
| 410 | t.Fatalf("set: %d %d %d", max, per, queue) | |
| 411 | } | |
| 412 | } | |