-
Notifications
You must be signed in to change notification settings - Fork 31
feat: add detailed component health checks for MySQL, Redis, and MongoDB #113
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
- Enhanced /health endpoint to show per-component status - Added type, host, port, database, version, and response time details - Returns error messages and types when components are down - Added MongoDB dependency and health check support
📝 WalkthroughWalkthroughAdds MongoDB support and wiring: a Maven dependency and application properties, plus a refactored HealthService that performs detailed health checks for MySQL, Redis, and MongoDB returning structured component statuses and a timestamped overall response. Changes
Sequence DiagramsequenceDiagram
participant Client
participant HealthService
participant MySQL
participant Redis
participant MongoDB
Client->>HealthService: GET /health
par Parallel Health Checks
HealthService->>MySQL: connect, simple query, fetch version
MySQL-->>HealthService: status (+ version / error)
HealthService->>Redis: PING, INFO for version
Redis-->>HealthService: PONG (+ version / error)
HealthService->>MongoDB: ping command, buildInfo for version
MongoDB-->>HealthService: pong (+ version / error)
end
HealthService->>HealthService: assemble components map with<br/>host, port, database, responseTimeMs, version, timestamp
HealthService-->>Client: JSON health response with components and timestamp
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. 📜 Recent review detailsConfiguration used: defaults Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
🔇 Additional comments (1)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 5
🤖 Fix all issues with AI agents
In @src/main/java/com/iemr/admin/service/health/HealthService.java:
- Around line 180-184: The Redis health check in HealthService currently calls
redisTemplate.execute with connection.ping() without any timeout; update the
health check (the method using redisTemplate.execute/RedisCallback) to enforce a
timeout by either configuring a connection timeout on the RedisConnectionFactory
used by redisTemplate (set the client/connection timeout property) or wrap the
execute call in a cancellable timed task (e.g., submit a
Callable/CompletableFuture that calls redisTemplate.execute(...) and use
get(timeout, unit)) and handle TimeoutException by treating Redis as unhealthy
and logging the timeout; ensure you reference redisTemplate and the
HealthService health-check method when making the change and propagate/return a
failure status on timeout.
- Around line 230-232: The pingResult null-check is incomplete and calling
pingResult.getDouble("ok") can return null and cause an NPE; change the check to
safely retrieve and verify the "ok" field (for example: use pingResult.get("ok",
Number.class) or pingResult.get("ok", Double.class'), ensure the returned value
is non-null, then call .doubleValue() and compare to 1.0), e.g., obtain Number
ok = pingResult.get("ok", Number.class); and use if (pingResult != null && ok !=
null && ok.doubleValue() == 1.0) to avoid auto-unboxing NPE in the HealthService
ping logic that calls mongoTemplate.getDb().runCommand.
- Around line 228-230: The MongoDB ping in HealthService
(mongoTemplate.getDb().runCommand(...)) lacks a timeout and can hang; add a
timeout by either supplying a maxTimeMS to the ping command (e.g., include a
maxTimeMS value in the Document/BSON passed to runCommand) or configure the
MongoClient's socket/timeout settings (MongoClientSettings socketTimeout or
serverSelectionTimeout) used by mongoTemplate; update the runCommand invocation
or the MongoClient configuration so the health check uses a bounded timeout
(e.g., 2–3s) to mirror the MySQL check.
- Around line 121-126: The health endpoint currently always adds sensitive DB
details via details.put("type", "MySQL"), details.put("host",
extractHost(dbUrl)), details.put("port", extractPort(dbUrl)),
details.put("database", extractDatabaseName(dbUrl)) (and similar Redis/MongoDB
blocks) and returns exception messages/classes; make detail exposure conditional
on a new config flag (e.g., boolean exposeHealthDetails) read from properties,
and wrap each sensitive addition behind if (exposeHealthDetails) checks so
host/port/database names are only added when true; for exceptions, stop
returning exception.getMessage()/getClass() to the HTTP response—keep internal
logging (use logger.error with the caught exception) and return a generic error
string in the health response; apply these changes to the MySQL block
(details.put calls), Redis and MongoDB detail blocks, and the three exception
handlers that currently expose exception details.
In @src/main/resources/application.properties:
- Around line 77-81: Add MongoDB authentication properties to the existing
spring.data.mongodb.* configuration: include spring.data.mongodb.username and
spring.data.mongodb.password (sourced from environment variables like
MONGO_USER/MONGO_PASSWORD), set spring.data.mongodb.authentication-database
(e.g., admin), and optionally enable TLS with
spring.data.mongodb.ssl.enabled=true for production; ensure credentials are
injected via external config or secrets manager (do not hardcode) and update any
connection-handling code to use these properties.
🧹 Nitpick comments (1)
src/main/java/com/iemr/admin/service/health/HealthService.java (1)
305-324: Consider using URI parsing for robustness.The manual string parsing works for standard MySQL JDBC URLs but could fail on edge cases (e.g., URLs with credentials, IPv6 addresses). Consider using Java's
URIclass or extracting connection metadata directly from theDataSource/Connectionmetadata.♻️ Alternative approach using Connection metadata
private String extractHost(String jdbcUrl) { try { // Try to get from connection metadata first try (Connection conn = dataSource.getConnection()) { DatabaseMetaData meta = conn.getMetaData(); String url = meta.getURL(); // Parse from metadata URL or use existing logic } } catch (Exception e) { // Fall back to string parsing } // ... existing string parsing as fallback }
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
pom.xmlsrc/main/java/com/iemr/admin/service/health/HealthService.javasrc/main/resources/application.properties
🔇 Additional comments (4)
src/main/java/com/iemr/admin/service/health/HealthService.java (3)
57-76: Good use of optional autowiring and default values.The
@Autowired(required = false)forMongoTemplateproperly handles deployments where MongoDB isn't configured. Default values in@Valueannotations provide sensible fallbacks.
78-114: LGTM! Well-structured health check orchestration.The method properly:
- Uses
LinkedHashMapfor consistent JSON ordering- Conditionally checks optional components (Redis, MongoDB)
- Aggregates overall health status from component statuses
263-299: LGTM! Version retrieval methods are well-implemented.Good practices observed:
- Non-critical operations with graceful degradation (return
nullon failure)- Debug-level logging for troubleshooting without cluttering logs
- Proper resource management with try-with-resources for MySQL
pom.xml (1)
203-206: MongoDB dependency correctly added.The dependency inherits its version from the Spring Boot parent (3.2.2), which is the standard approach for Spring Boot starters and ensures compatibility with other Spring Data components. The placement alongside the Redis starter is appropriate.
| // Add connection details | ||
| details.put("type", "MySQL"); | ||
| details.put("host", extractHost(dbUrl)); | ||
| details.put("port", extractPort(dbUrl)); | ||
| details.put("database", extractDatabaseName(dbUrl)); | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
# Check for health endpoint configuration and security setup
rg -n -C3 "health|HealthController|HealthService" --type=java | head -100Repository: PSMRI/Admin-API
Length of output: 10338
🏁 Script executed:
# Look for Spring Boot Actuator configuration
find . -type f \( -name "application.properties" -o -name "application.yml" -o -name "application.yaml" \) -exec grep -l "health\|actuator" {} \;Repository: PSMRI/Admin-API
Length of output: 41
🏁 Script executed:
# Check for Spring Security configuration
rg -n "EnableWebSecurity|SecurityFilterChain|authorizeRequests" --type=java | head -50Repository: PSMRI/Admin-API
Length of output: 41
🏁 Script executed:
# Look for any request mapping or endpoint configuration
rg -n -C2 "@RequestMapping|@GetMapping|@PostMapping" --type=java -g "*/controller/*" | head -100Repository: PSMRI/Admin-API
Length of output: 207
🏁 Script executed:
# Check the actual HealthService implementation
fd -t f "HealthService.java" --exec cat -n {} \;Repository: PSMRI/Admin-API
Length of output: 16914
🏁 Script executed:
# Find the controller that exposes the health endpoint
rg -n "@RequestMapping|@GetMapping|@RestController|@Controller" -A5 --type=java | grep -A5 "health"Repository: PSMRI/Admin-API
Length of output: 1796
🏁 Script executed:
# Look for application configuration files
find . -type f \( -name "*.properties" -o -name "*.yml" -o -name "*.yaml" \) | head -20Repository: PSMRI/Admin-API
Length of output: 453
Restrict database connection details exposure in public health endpoint.
The /health endpoint is intentionally public (JWT validation explicitly skipped in JwtUserIdValidationFilter), yet exposes database host, port, and database names for all components (MySQL, Redis, MongoDB). Additionally, exception messages and class names are exposed, which could leak version information. This aids reconnaissance and should be restricted.
Consider either:
- Restricting detail exposure to authenticated/internal requests
- Making detail exposure configurable via a property
This applies across all three database components (MySQL at lines 122-125, Redis at lines 177-178, MongoDB at lines 224-226, plus exception details at lines 163-164, 210-211, and 256-257).
🤖 Prompt for AI Agents
In @src/main/java/com/iemr/admin/service/health/HealthService.java around lines
121 - 126, The health endpoint currently always adds sensitive DB details via
details.put("type", "MySQL"), details.put("host", extractHost(dbUrl)),
details.put("port", extractPort(dbUrl)), details.put("database",
extractDatabaseName(dbUrl)) (and similar Redis/MongoDB blocks) and returns
exception messages/classes; make detail exposure conditional on a new config
flag (e.g., boolean exposeHealthDetails) read from properties, and wrap each
sensitive addition behind if (exposeHealthDetails) checks so host/port/database
names are only added when true; for exceptions, stop returning
exception.getMessage()/getClass() to the HTTP response—keep internal logging
(use logger.error with the caught exception) and return a generic error string
in the health response; apply these changes to the MySQL block (details.put
calls), Redis and MongoDB detail blocks, and the three exception handlers that
currently expose exception details.
| try { | ||
| String pong = redisTemplate.execute((RedisCallback<String>) connection -> connection.ping()); | ||
|
|
||
| // Use ping() from RedisConnection directly | ||
| String pong = redisTemplate.execute((RedisCallback<String>) connection -> | ||
| connection.ping() | ||
| ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing timeout for Redis health check.
The Redis ping() operation lacks an explicit timeout. Consider configuring a connection timeout in Redis properties or wrapping the call with a timeout mechanism to prevent blocking.
🤖 Prompt for AI Agents
In @src/main/java/com/iemr/admin/service/health/HealthService.java around lines
180 - 184, The Redis health check in HealthService currently calls
redisTemplate.execute with connection.ping() without any timeout; update the
health check (the method using redisTemplate.execute/RedisCallback) to enforce a
timeout by either configuring a connection timeout on the RedisConnectionFactory
used by redisTemplate (set the client/connection timeout property) or wrap the
execute call in a cancellable timed task (e.g., submit a
Callable/CompletableFuture that calls redisTemplate.execute(...) and use
get(timeout, unit)) and handle TimeoutException by treating Redis as unhealthy
and logging the timeout; ensure you reference redisTemplate and the
HealthService health-check method when making the change and propagate/return a
failure status on timeout.
| try { | ||
| // Run ping command to check MongoDB connectivity | ||
| Document pingResult = mongoTemplate.getDb().runCommand(new Document("ping", 1)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing timeout for MongoDB health check.
Unlike the MySQL check (which has 2s connection validation and 3s query timeout), the MongoDB ping command has no timeout. If MongoDB is unresponsive, this could cause the health endpoint to hang indefinitely.
Consider configuring a socket timeout or using MongoDB's maxTimeMS option:
♻️ Proposed fix with timeout
try {
// Run ping command to check MongoDB connectivity
- Document pingResult = mongoTemplate.getDb().runCommand(new Document("ping", 1));
+ Document pingCommand = new Document("ping", 1)
+ .append("maxTimeMS", 3000); // 3 second timeout
+ Document pingResult = mongoTemplate.getDb().runCommand(pingCommand);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| try { | |
| // Run ping command to check MongoDB connectivity | |
| Document pingResult = mongoTemplate.getDb().runCommand(new Document("ping", 1)); | |
| try { | |
| // Run ping command to check MongoDB connectivity | |
| Document pingCommand = new Document("ping", 1) | |
| .append("maxTimeMS", 3000); // 3 second timeout | |
| Document pingResult = mongoTemplate.getDb().runCommand(pingCommand); |
🤖 Prompt for AI Agents
In @src/main/java/com/iemr/admin/service/health/HealthService.java around lines
228 - 230, The MongoDB ping in HealthService
(mongoTemplate.getDb().runCommand(...)) lacks a timeout and can hang; add a
timeout by either supplying a maxTimeMS to the ping command (e.g., include a
maxTimeMS value in the Document/BSON passed to runCommand) or configure the
MongoClient's socket/timeout settings (MongoClientSettings socketTimeout or
serverSelectionTimeout) used by mongoTemplate; update the runCommand invocation
or the MongoClient configuration so the health check uses a bounded timeout
(e.g., 2–3s) to mirror the MySQL check.
| Document pingResult = mongoTemplate.getDb().runCommand(new Document("ping", 1)); | ||
|
|
||
| if (pingResult != null && pingResult.getDouble("ok") == 1.0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potential NullPointerException on MongoDB ping result check.
pingResult.getDouble("ok") returns null if the key doesn't exist, and comparing with == 1.0 will cause auto-unboxing NPE.
🐛 Proposed fix
- if (pingResult != null && pingResult.getDouble("ok") == 1.0) {
+ if (pingResult != null && Double.valueOf(1.0).equals(pingResult.getDouble("ok"))) {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| Document pingResult = mongoTemplate.getDb().runCommand(new Document("ping", 1)); | |
| if (pingResult != null && pingResult.getDouble("ok") == 1.0) { | |
| Document pingResult = mongoTemplate.getDb().runCommand(new Document("ping", 1)); | |
| if (pingResult != null && Double.valueOf(1.0).equals(pingResult.getDouble("ok"))) { |
🤖 Prompt for AI Agents
In @src/main/java/com/iemr/admin/service/health/HealthService.java around lines
230 - 232, The pingResult null-check is incomplete and calling
pingResult.getDouble("ok") can return null and cause an NPE; change the check to
safely retrieve and verify the "ok" field (for example: use pingResult.get("ok",
Number.class) or pingResult.get("ok", Double.class'), ensure the returned value
is non-null, then call .doubleValue() and compare to 1.0), e.g., obtain Number
ok = pingResult.get("ok", Number.class); and use if (pingResult != null && ok !=
null && ok.doubleValue() == 1.0) to avoid auto-unboxing NPE in the HealthService
ping logic that calls mongoTemplate.getDb().runCommand.
|
|
||
| ## MongoDB Configuration | ||
| spring.data.mongodb.host=mongodb-container | ||
| spring.data.mongodb.port=27017 | ||
| spring.data.mongodb.database=amrit |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing MongoDB authentication configuration.
The MongoDB configuration lacks authentication properties (spring.data.mongodb.username, spring.data.mongodb.password). While this may work for local development, production deployments should use authenticated connections.
Consider adding authentication and optionally TLS configuration:
spring.data.mongodb.username=${MONGO_USER}
spring.data.mongodb.password=${MONGO_PASSWORD}
spring.data.mongodb.authentication-database=admin
# Optional: Enable TLS for production
# spring.data.mongodb.ssl.enabled=true🤖 Prompt for AI Agents
In @src/main/resources/application.properties around lines 77 - 81, Add MongoDB
authentication properties to the existing spring.data.mongodb.* configuration:
include spring.data.mongodb.username and spring.data.mongodb.password (sourced
from environment variables like MONGO_USER/MONGO_PASSWORD), set
spring.data.mongodb.authentication-database (e.g., admin), and optionally enable
TLS with spring.data.mongodb.ssl.enabled=true for production; ensure credentials
are injected via external config or secrets manager (do not hardcode) and update
any connection-handling code to use these properties.
|


📋 Description
JIRA ID:
Please provide a summary of the change and the motivation behind it. Include relevant context and details.
✅ Type of Change
ℹ️ Additional Information
Please describe how the changes were tested, and include any relevant screenshots, logs, or other information that provides additional context.
Summary by CodeRabbit
New Features
Chores
✏️ Tip: You can customize this high-level summary in your review settings.