SpikerSoft.Api currently runs multiple blocking startup seed operations synchronously inside Program.cs (terms, areas of interest, postal codes, positions, lesson catalog hydration). Each API replica repeats this work unless idempotency shields it everywhere — costing cold-start latency and tying up CPUs on constrained hosts (edge / Jetson) before the pipeline is ready to serve traffic.
Move seeding behind a dedicated one-shot bootstrap job (container or Job/initContainer pattern in orchestration) that runs once per deploy/environment, then exits cleanly. API instances assume data is ready (or degrade gracefully).
Current behavior
See SpikerSoft.Api/Program.cs seeding block: TermsOfServiceSeederService, AreasOfInterestSeederService, PostalCodeSeederService, PositionSeederService, ILessonCatalogHydrationService.HydrateAsync. Failures are caught and logged; app continues — multiple replicas still race redundant work.
Desired behavior
SpikerSoft.Bootstrap (or similar console/Docker CMD) hosts the same DI + MediatR/repositories needed only for seeders — no ASP.NET listener.
Invocation: compose profiles, Swarm/K8s deploy hook, CI job, or docker compose run bootstrap documented in README / ops runbook.
Idempotency: existing seed methods stay safe to re-run; optional exit code 0 when nothing to do.
API: remove or gate inline seeding via config, e.g. Seeding:RunOnStartup default false in production; optional true for local dev only.
Locking (optional enhancement): distributed lock / "primary seeder" role so accidental parallel bootstrap doesn’t thundering-herd heavy stores.
Acceptance criteria
New bootstrap project/container published from repo with clear entrypoint.
docker-compose (and/or swarm stack) exposes a documented one-shot service or documented run invocation.
Production API defaults to no startup seeding; dev profile can keep ergonomic auto-seed if desired.
Health/readiness unaffected: APIs start without waiting for postal/lesson hydrate on every replica.
Short ADR or comment in bootstrap README referencing why (cold start / edge).
Labels
enhancement · backend · devops
## Summary
`SpikerSoft.Api` currently runs multiple **blocking startup seed operations** synchronously inside `Program.cs` (terms, areas of interest, postal codes, positions, lesson catalog hydration). Each API replica repeats this work unless idempotency shields it everywhere — costing cold-start latency and tying up CPUs on constrained hosts (edge / Jetson) before the pipeline is ready to serve traffic.
Move seeding behind a **dedicated one-shot bootstrap job** (container or `Job`/`initContainer` pattern in orchestration) that runs **once per deploy/environment**, then exits cleanly. API instances assume data is ready (or degrade gracefully).
## Current behavior
See `SpikerSoft.Api/Program.cs` seeding block: `TermsOfServiceSeederService`, `AreasOfInterestSeederService`, `PostalCodeSeederService`, `PositionSeederService`, `ILessonCatalogHydrationService.HydrateAsync`. Failures are caught and logged; app continues — multiple replicas still race redundant work.
## Desired behavior
1. **`SpikerSoft.Bootstrap`** (or similar console/Docker CMD) hosts the same DI + MediatR/repositories needed **only for seeders** — no ASP.NET listener.
2. **Invocation:** compose `profiles`, Swarm/K8s deploy hook, CI job, or `docker compose run bootstrap` documented in README / ops runbook.
3. **Idempotency:** existing seed methods stay safe to re-run; optional exit code `0` when nothing to do.
4. **API:** remove or gate inline seeding via config, e.g. `Seeding:RunOnStartup` default `false` in production; optional `true` for local dev only.
5. **Locking (optional enhancement):** distributed lock / "primary seeder" role so accidental parallel bootstrap doesn’t thundering-herd heavy stores.
## Acceptance criteria
- [ ] New bootstrap project/container published from repo with clear entrypoint.
- [ ] `docker-compose` (and/or swarm stack) exposes a documented one-shot service or documented `run` invocation.
- [ ] Production API defaults to **no** startup seeding; dev profile can keep ergonomic auto-seed if desired.
- [ ] Health/readiness unaffected: APIs start without waiting for postal/lesson hydrate on every replica.
- [ ] Short ADR or comment in bootstrap `README` referencing why (cold start / edge).
## Labels
`enhancement` · `backend` · `devops`
Slice 1 up: backend PR #246 — the five-step seeding sequence (terms → areas → postal[gated] → positions → curriculum store → lesson hydration) is extracted from inline Program.cs into a reusable StartupSeedRunner, behind a Seeding:RunOnStartup gate (default true — zero behavior change now). The runner keeps the API's swallow-and-log semantics and exposes throwOnFailure for the job path (a failed bootstrap run must exit non-zero).
Slice 2 (next): SpikerSoft.Bootstrap console host reusing the same runner, Dockerfile + CI workflow, and the infra one-shot service (gameserver-init restart-policy pattern) — with Seeding__RunOnStartup=false flipped on the API in the same infra change, so deploy ordering can never leave an environment without a seeder.
Slice 1 up: **backend PR #246** — the five-step seeding sequence (terms → areas → postal[gated] → positions → curriculum store → lesson hydration) is extracted from inline `Program.cs` into a reusable `StartupSeedRunner`, behind a `Seeding:RunOnStartup` gate (default **true** — zero behavior change now). The runner keeps the API's swallow-and-log semantics and exposes `throwOnFailure` for the job path (a failed bootstrap run must exit non-zero).
Slice 2 (next): `SpikerSoft.Bootstrap` console host reusing the same runner, Dockerfile + CI workflow, and the infra one-shot service (gameserver-init restart-policy pattern) — with `Seeding__RunOnStartup=false` flipped on the API **in the same infra change**, so deploy ordering can never leave an environment without a seeder.
backend #248: dotnet SpikerSoft.Api.dll --bootstrap — the API image itself runs the seed sequence with throwOnFailure and exits without starting Kestrel. Deliberately NOT a second project/Dockerfile/CI lane: same image CI already publishes, composition root identical by construction (no missing-DI drift à la #513).
infra #50: spikersoft-bootstrap one-shot stack (gameserver-init semantics — Complete stays done, failed runs retry ×3 and the #510-B reconciler re-runs failed one-shots) + Seeding__RunOnStartup=false on the API service in the same change.
Merge/deploy order: #248 → CI publishes image → pull /mnt/infrastructure → deploy spikersoft-bootstrap (verify Complete, exit 0 in docker service ps) → next API deploy picks up the env flip and replicas stop paying the seed pass on cold start. Close this ticket on that verification.
Slice 2 up, both halves:
- **backend #248**: `dotnet SpikerSoft.Api.dll --bootstrap` — the API image itself runs the seed sequence with `throwOnFailure` and exits without starting Kestrel. Deliberately NOT a second project/Dockerfile/CI lane: same image CI already publishes, composition root identical by construction (no missing-DI drift à la #513).
- **infra #50**: `spikersoft-bootstrap` one-shot stack (gameserver-init semantics — Complete stays done, failed runs retry ×3 and the #510-B reconciler re-runs failed one-shots) + `Seeding__RunOnStartup=false` on the API service in the same change.
**Merge/deploy order**: #248 → CI publishes image → pull /mnt/infrastructure → deploy `spikersoft-bootstrap` (verify `Complete`, exit 0 in `docker service ps`) → next API deploy picks up the env flip and replicas stop paying the seed pass on cold start. Close this ticket on that verification.
Board-sweep verified complete: backend PRs #246 (StartupSeedRunner + Seeding:RunOnStartup gate) + #248 (--bootstrap mode) + infra PR #50 (spikersoft-bootstrap one-shot stack) all merged; StartupSeedRunner wiring and the infra spikersoft-bootstrap/ stack verified in the trees. Closing.
Board-sweep verified complete: backend PRs #246 (StartupSeedRunner + Seeding:RunOnStartup gate) + #248 (--bootstrap mode) + infra PR #50 (spikersoft-bootstrap one-shot stack) all merged; StartupSeedRunner wiring and the infra spikersoft-bootstrap/ stack verified in the trees. Closing.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
SpikerSoft.Apicurrently runs multiple blocking startup seed operations synchronously insideProgram.cs(terms, areas of interest, postal codes, positions, lesson catalog hydration). Each API replica repeats this work unless idempotency shields it everywhere — costing cold-start latency and tying up CPUs on constrained hosts (edge / Jetson) before the pipeline is ready to serve traffic.Move seeding behind a dedicated one-shot bootstrap job (container or
Job/initContainerpattern in orchestration) that runs once per deploy/environment, then exits cleanly. API instances assume data is ready (or degrade gracefully).Current behavior
See
SpikerSoft.Api/Program.csseeding block:TermsOfServiceSeederService,AreasOfInterestSeederService,PostalCodeSeederService,PositionSeederService,ILessonCatalogHydrationService.HydrateAsync. Failures are caught and logged; app continues — multiple replicas still race redundant work.Desired behavior
SpikerSoft.Bootstrap(or similar console/Docker CMD) hosts the same DI + MediatR/repositories needed only for seeders — no ASP.NET listener.profiles, Swarm/K8s deploy hook, CI job, ordocker compose run bootstrapdocumented in README / ops runbook.0when nothing to do.Seeding:RunOnStartupdefaultfalsein production; optionaltruefor local dev only.Acceptance criteria
docker-compose(and/or swarm stack) exposes a documented one-shot service or documentedruninvocation.READMEreferencing why (cold start / edge).Labels
enhancement·backend·devopsSlice 1 up: backend PR #246 — the five-step seeding sequence (terms → areas → postal[gated] → positions → curriculum store → lesson hydration) is extracted from inline
Program.csinto a reusableStartupSeedRunner, behind aSeeding:RunOnStartupgate (default true — zero behavior change now). The runner keeps the API's swallow-and-log semantics and exposesthrowOnFailurefor the job path (a failed bootstrap run must exit non-zero).Slice 2 (next):
SpikerSoft.Bootstrapconsole host reusing the same runner, Dockerfile + CI workflow, and the infra one-shot service (gameserver-init restart-policy pattern) — withSeeding__RunOnStartup=falseflipped on the API in the same infra change, so deploy ordering can never leave an environment without a seeder.Slice 2 up, both halves:
dotnet SpikerSoft.Api.dll --bootstrap— the API image itself runs the seed sequence withthrowOnFailureand exits without starting Kestrel. Deliberately NOT a second project/Dockerfile/CI lane: same image CI already publishes, composition root identical by construction (no missing-DI drift à la #513).spikersoft-bootstrapone-shot stack (gameserver-init semantics — Complete stays done, failed runs retry ×3 and the #510-B reconciler re-runs failed one-shots) +Seeding__RunOnStartup=falseon the API service in the same change.Merge/deploy order: #248 → CI publishes image → pull /mnt/infrastructure → deploy
spikersoft-bootstrap(verifyComplete, exit 0 indocker service ps) → next API deploy picks up the env flip and replicas stop paying the seed pass on cold start. Close this ticket on that verification.Board-sweep verified complete: backend PRs #246 (StartupSeedRunner + Seeding:RunOnStartup gate) + #248 (--bootstrap mode) + infra PR #50 (spikersoft-bootstrap one-shot stack) all merged; StartupSeedRunner wiring and the infra spikersoft-bootstrap/ stack verified in the trees. Closing.