MemberJunction / MemberJunction/MJ
MJServer accepts no connections until the entire bootstrap finishes, so a slow start looks like a dead server
- Dominant language
- TSQL
- Stars
- 29
- Forks
- 6
- Avg merge
- 2d 1h
- Merged PRs (30d)
- 323
Description
## What happens
`httpServer.listen(...)` is the last thing `serve()` does (`packages/MJServer/src/index.ts:1517`, on `next` today). Everything — metadata load, engine configuration, provider setup, the `onBeforeServe` hook — runs first. Until all of it completes the port is closed.
From outside, a server that is working correctly but starting slowly is indistinguishable from one that has died. A health probe gets connection-refused, a load balancer pulls the instance, and an operator watching a deploy sees a failure with no error, because nothing has failed.
On the slowest hosting tiers this window is minutes, not seconds.
## Why it matters more than it sounds
The failure is silent and it inverts the diagnosis. Three separate investigations we have done started from "the API is down" and ended at "the API was still booting" — including one where the container platform's own start-time limit killed the process partway through bootstrap, so it never finished starting and never would have, and the logs showed only a truncated startup sequence.
There is also no way to tell "still booting" from "booting and stuck", because there is nothing to ask.
## Suggested shape
Bind the port early and gate traffic on a readiness flag, rather than gating the bind:
- listen first, then run bootstrap;
- serve a `/health` (or equivalent) responder immediately, reporting `starting` → `ready` → `failed`, ideally naming the current bootstrap phase;
- reject or queue real GraphQL traffic while `starting`, so early requests do not see a half-configured provider;
- report a bootstrap that throws as `failed` on that endpoint rather than exiting silently.
The phase name is the part that pays for itself: "still loading metadata after 4 minutes" is actionable, "connection refused" is not.
## Notes
Filed as an issue rather than a PR because the right shape is a design decision for the MJServer owners — in particular what early requests should do, and whether readiness belongs on the existing GraphQL endpoint or a separate one. Happy to implement whichever shape you prefer.
Contributor guide
Research direction
Start in packages/MJServer/src/index.ts at serve(), especially the current httpServer.listen(...) call around line 1517 and the bootstrap steps before it. Review the existing GraphQL entry point and startup hooks to define where readiness is exposed and how early traffic is handled. Done means slow bootstrap is externally distinguishable from failure, with starting, ready, and failed states reported without accepting half-configured requests.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- graphql, typescript
- Domain
- api, backend, observability
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100