MemberJunction / MemberJunction/MJ

MJServer accepts no connections until the entire bootstrap finishes, so a slow start looks like a dead server

Open
#4,432 0 comments 0 reactions 0 assignees View on GitHub
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.