Repository navigation
Defer reading the OpenAPI spec until it is first requested - #569
jonbartels wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
🟢 Approval recommended
No active review issues were identified for the change deferring OpenAPI specification loading until the first request.
No open issues 🎉
What changed in this PR
Defers OpenAPI specification reading until the first request, reducing server startup work while preserving configuration registration in SwaggerServlet.
Changes:
- Removes the eager
.read()call after building the OpenAPI context. - Adds a comment explaining that
OpenApiResourcereads the specification on its first request.
| File | Description |
|---|---|
server/src/main/java/com/mirth/connect/server/servlets/SwaggerServlet.java |
Retains OpenAPI configuration registration without eagerly reading the specification. |
Review effort: Balanced
tonygermano
left a comment
There was a problem hiding this comment.
Not approved yet. The change is good. I'll approve once the comment and the commit message describe it as a trade-off. The startup read wasn't wasted: it warmed up the first spec request (~460 ms → ~700 ms in your results).
-
Code comment: please reword it, for example:
// Only register the configuration here. OpenApiResource builds its own child context from it // and reads the spec on the first request; it never uses a read done here. Reading here would // only warm up classes for that first request, at the cost of slower server startup. -
Commit message: please add a paragraph after "Register the configuration only", for example:
The startup read did warm up class loading and Jackson introspection, so the first spec request now takes longer (about 460 ms to 700 ms on a warm start). That request normally comes from someone opening the Swagger UI, which is a better place for the cost than server startup.
FYI, no action needed: Claude checked the change against the swagger-core 2.2.41 sources. OpenApiResource reads the spec in its own child context with its own cache, so the served spec is unchanged. Claude also started the server built from this PR and from fdff780 and compared the responses: /api/openapi.json, /api/4.7.0/openapi.json and /api/openapi.yaml return the same content with keys sorted, and the Swagger UI page loads.
🤖 Drafted with Claude Code
SwaggerServlet read the full OpenAPI spec during server startup. Nothing used that result: OpenApiResource, which serves /api/openapi.json and /api/openapi.yaml, builds its own child context from the configuration SwaggerServlet registers and reads the spec again on the first request. Register the configuration only. The served spec is unchanged, and the Swagger servlet's init drops from about 660 ms to about 120 ms on a warm start, taking about 0.5 s off the time until the web server is up. The startup read did warm up class loading and Jackson introspection, so the first spec request now takes longer (about 460 ms to 700 ms on a warm start). That request normally comes from someone opening the Swagger UI, which is a better place for the cost than server startup. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Jon Bartels <jonathan.bartels@gmail.com>
5ff7cdb to
142bb4e
Compare
tonygermano
left a comment
There was a problem hiding this comment.
Thanks for the quick turnaround on my requests and good find!
There was a problem hiding this comment.
Can you add a call to the endpoint to CI? This would allow silent failures since nothing would be logged if the document cannot be generated on boot.
I agree with the change - but need testing here. I would not go wild. A 200 with assert contains x string is sufficient
What
SwaggerServlet.initno longer calls.read()on the OpenAPI context it builds. It still registers the configuration (title, description, and the resource classes to document).Why
SwaggerServletdoesn't serve anything; its only job is to register the OpenAPI configuration. The spec is served by swagger'sOpenApiResourceinside Jersey. On its first request,OpenApiResourcelooks up a context under its own servlet's id. It doesn't find one, so it creates a child context whose parent is the default contextSwaggerServletregistered, and reads the spec again. The spec read at startup was never used.Evidence: with the current code, the first
/api/openapi.jsonrequest already takes ~460 ms (and the first/api/4.7.0/openapi.json~230 ms), even thoughSwaggerServlethad just read the spec during startup.Results
Apple M4, JDK 25, default Derby config, no channels; warm medians of 5 runs:
SwaggerServlet.initstart()/api/openapi.jsonrequest/api/openapi.jsonrequestsThe cost of the first spec request moves from startup to whoever requests it first (normally someone opening the Swagger UI).
Verification
/api/openapi.json,/api/4.7.0/openapi.jsonand/api/openapi.yamlall return 200 with the "Open Integration Engine Client API" title.jq -S). The raw bytes differ, but key order already varies between two runs of unmodified code../gradlew :server:test: 834 of 836 pass. The two failures (TcpDispatcherTest.testMLLPMessageSent,testMessageSentWhenOneOfMultipleClientsClosed) are aBindExceptionon the test's ports, which count up from 9000. On my machine IntelliJ listens on 9000/9003, so these fail on unmodifiedmainas well.🤖 Generated with Claude Code