Production Deployment Guide¶
Comprehensive guide for deploying Morphium applications in production environments.
Performance Baseline¶
Morphium Performance Advantage: - At least 10% faster than standard MongoDB Java driver with mapping - Performance advantage comes from: - Custom wire protocol implementation optimized for Morphium's mapping - Reduced object creation overhead - Optimized BSON serialization/deserialization - Connection pool efficiency
Pre-Deployment Checklist¶
1. Configuration Review¶
Connection Pool Sizing:
// Standard recommendation: 10-100 connections for most scenarios
cfg.connectionSettings().setMaxConnectionsPerHost(50); // Sweet spot for most loads
cfg.connectionSettings().setMinConnectionsPerHost(10); // Maintain baseline
Performance Testing Validation:
// Verify your specific load requirements
MorphiumConfig cfg = new MorphiumConfig();
// Start conservative, monitor, and adjust
cfg.connectionSettings().setMaxConnectionsPerHost(25);
cfg.connectionSettings().setMinConnectionsPerHost(5);
cfg.connectionSettings().setMaxWaitTime(5000); // Fail fast to detect issues
// Monitor and adjust based on actual load:
// - CPU utilization < 70%
// - No threads waiting for connections
// - Response times within SLA
2. Security Configuration¶
Authentication Setup:
cfg.authSettings().setMongoLogin(System.getenv("MONGO_USERNAME"));
cfg.authSettings().setMongoPassword(System.getenv("MONGO_PASSWORD"));
// For replica set operations (if needed)
cfg.authSettings().setMongoAdminUser(System.getenv("MONGO_ADMIN_USER"));
cfg.authSettings().setMongoAdminPwd(System.getenv("MONGO_ADMIN_PWD"));
Network Security:
// Note: Wire protocol driver has limitations
// - No MongoDB Atlas support
// - No SSL/TLS connections
// Deploy in trusted network environments or use network-level encryption
3. Environment-Specific Configurations¶
Development Environment¶
MorphiumConfig createDevConfig() {
MorphiumConfig cfg = new MorphiumConfig();
cfg.connectionSettings().setDatabase("myapp_dev");
cfg.clusterSettings().addHostToSeed("dev-mongo", 27017);
cfg.connectionSettings().setMaxConnectionsPerHost(10); // Lower for dev
cfg.cacheSettings().setGlobalCacheValidTime(30000); // Shorter cache for dev
// Debug logging configured via log4j2.xml
return cfg;
}
Staging Environment¶
MorphiumConfig createStagingConfig() {
MorphiumConfig cfg = new MorphiumConfig();
cfg.connectionSettings().setDatabase("myapp_staging");
cfg.clusterSettings().addHostToSeed("staging-mongo1", 27017);
cfg.clusterSettings().addHostToSeed("staging-mongo2", 27017);
cfg.connectionSettings().setMaxConnectionsPerHost(25); // Moderate load
cfg.connectionSettings().setRetriesOnNetworkError(5);
cfg.authSettings().setMongoLogin(System.getenv("MONGO_USERNAME"));
cfg.authSettings().setMongoPassword(System.getenv("MONGO_PASSWORD"));
return cfg;
}
Production Environment¶
MorphiumConfig createProductionConfig() {
MorphiumConfig cfg = new MorphiumConfig();
cfg.connectionSettings().setDatabase("myapp_prod");
// Full replica set configuration
cfg.clusterSettings().addHostToSeed("prod-mongo1", 27017);
cfg.clusterSettings().addHostToSeed("prod-mongo2", 27017);
cfg.clusterSettings().addHostToSeed("prod-mongo3", 27017);
cfg.clusterSettings().setReplicaSetName("prod-replica-set");
// Production-tuned connection pool
cfg.connectionSettings().setMaxConnectionsPerHost(50);
cfg.connectionSettings().setMinConnectionsPerHost(10);
cfg.connectionSettings().setMaxWaitTime(3000); // Fail fast
cfg.connectionSettings().setRetriesOnNetworkError(10);
cfg.connectionSettings().setSleepBetweenErrorRetries(200);
// Longer connection lifecycles for stability
cfg.connectionSettings().setMaxConnectionIdleTime(300000); // 5 minutes
cfg.connectionSettings().setMaxConnectionLifetime(1800000); // 30 minutes
// Production cache settings
cfg.cacheSettings().setGlobalCacheValidTime(300000); // 5 minute default cache
// Messaging optimization
cfg.messagingSettings().setMessagingWindowSize(200);
cfg.messagingSettings().setMessagingMultithreadded(true);
cfg.messagingSettings().setUseChangeStream(true);
// Security
cfg.authSettings().setMongoLogin(System.getenv("MONGO_USERNAME"));
cfg.authSettings().setMongoPassword(System.getenv("MONGO_PASSWORD"));
// Note: Logging is configured via Log4j configuration files, not programmatically
return cfg;
}
Deployment Strategies¶
1. Container Deployment (Docker)¶
Dockerfile:
FROM openjdk:21-jdk-slim
# Application setup
WORKDIR /app
COPY target/myapp.jar app.jar
COPY config/morphium-prod.properties morphium.properties
# JVM optimization for containers
ENV JAVA_OPTS="-Xms2g -Xmx4g -XX:+UseG1GC -XX:MaxGCPauseMillis=200"
# Health check endpoint
HEALTHCHECK --interval=30s --timeout=3s --start-period=60s --retries=3 \
CMD curl -f http://localhost:8080/health || exit 1
EXPOSE 8080
CMD java $JAVA_OPTS -jar app.jar
Docker Compose with MongoDB:
version: '3.8'
services:
mongodb:
image: mongo:7.0
restart: always
environment:
MONGO_INITDB_ROOT_USERNAME: admin
MONGO_INITDB_ROOT_PASSWORD: ${MONGO_ADMIN_PASSWORD}
volumes:
- mongodb_data:/data/db
ports:
- "27017:27017"
app:
build: .
restart: always
environment:
MONGO_USERNAME: ${MONGO_USERNAME}
MONGO_PASSWORD: ${MONGO_PASSWORD}
JAVA_OPTS: "-Xms2g -Xmx4g -XX:+UseG1GC"
ports:
- "8080:8080"
depends_on:
- mongodb
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
volumes:
mongodb_data:
2. Kubernetes Deployment¶
ConfigMap for Configuration:
apiVersion: v1
kind: ConfigMap
metadata:
name: morphium-config
data:
morphium.properties: |
database=myapp_prod
hosts=mongo-0.mongo,mongo-1.mongo,mongo-2.mongo
replica_set_name=rs0
max_connections_per_host=50
min_connections_per_host=10
max_wait_time=3000
retries_on_network_error=10
global_cache_valid_time=300000
Deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: myapp:latest
ports:
- containerPort: 8080
env:
- name: MONGO_USERNAME
valueFrom:
secretKeyRef:
name: mongo-secret
key: username
- name: MONGO_PASSWORD
valueFrom:
secretKeyRef:
name: mongo-secret
key: password
- name: JAVA_OPTS
value: "-Xms2g -Xmx4g -XX:+UseG1GC -XX:MaxGCPauseMillis=200"
volumeMounts:
- name: config
mountPath: /app/config
resources:
limits:
memory: "5Gi"
cpu: "2000m"
requests:
memory: "2Gi"
cpu: "1000m"
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 60
periodSeconds: 30
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
volumes:
- name: config
configMap:
name: morphium-config
Monitoring and Observability¶
1. Application Metrics¶
Health Check Implementation:
@RestController
public class HealthController {
@Autowired
private Morphium morphium;
@GetMapping("/health")
public ResponseEntity<Map<String, Object>> health() {
Map<String, Object> health = new HashMap<>();
try {
// Test database connectivity
long start = System.currentTimeMillis();
morphium.createQueryFor(User.class).limit(1).asList();
long responseTime = System.currentTimeMillis() - start;
// Get driver statistics
Map<DriverStatsKey, Double> stats = morphium.getDriver().getDriverStats();
health.put("status", "UP");
health.put("database", Map.of(
"status", "UP",
"responseTime", responseTime + "ms"
));
health.put("connectionPool", Map.of(
"connectionsInUse", stats.get(DriverStatsKey.CONNECTIONS_IN_USE).intValue(),
"connectionsInPool", stats.get(DriverStatsKey.CONNECTIONS_IN_POOL).intValue(),
"threadsWaiting", stats.get(DriverStatsKey.THREADS_WAITING_FOR_CONNECTION).intValue(),
"errors", stats.get(DriverStatsKey.ERRORS).intValue()
));
return ResponseEntity.ok(health);
} catch (Exception e) {
health.put("status", "DOWN");
health.put("error", e.getMessage());
return ResponseEntity.status(503).body(health);
}
}
@GetMapping("/metrics")
public ResponseEntity<Map<String, Object>> metrics() {
Map<DriverStatsKey, Double> stats = morphium.getDriver().getDriverStats();
Map<String, Object> metrics = new HashMap<>();
stats.forEach((key, value) ->
metrics.put(key.name().toLowerCase(), value));
return ResponseEntity.ok(metrics);
}
}
2. Logging Configuration¶
Production Logging Configuration (log4j2.xml):
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<File name="MorphiumLog" fileName="/var/log/myapp/morphium.log">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} [%level] %logger{36} - %msg%n"/>
</File>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%level] %logger{36} - %msg%n"/>
</Console>
</Appenders>
<Loggers>
<!-- Morphium package logging -->
<Logger name="de.caluga.morphium" level="WARN" additivity="false">
<AppenderRef ref="MorphiumLog"/>
</Logger>
<!-- Driver-specific logging for troubleshooting -->
<Logger name="de.caluga.morphium.driver.wire.PooledDriver" level="INFO" additivity="false">
<AppenderRef ref="MorphiumLog"/>
</Logger>
<!-- Messaging logging -->
<Logger name="de.caluga.morphium.messaging" level="INFO" additivity="false">
<AppenderRef ref="MorphiumLog"/>
</Logger>
<Root level="INFO">
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration>
Log Rotation Configuration (logrotate):
/var/log/myapp/morphium.log {
daily
rotate 30
compress
delaycompress
missingok
create 0644 app app
postrotate
systemctl reload myapp || true
endscript
}
3. Alerting Rules¶
Connection Pool Alerts:
// Monitor connection pool utilization
Map<DriverStatsKey, Double> stats = morphium.getDriver().getDriverStats();
double utilization = stats.get(DriverStatsKey.CONNECTIONS_IN_USE) /
stats.get(DriverStatsKey.CONNECTIONS_IN_POOL);
if (utilization > 0.8) {
// Alert: High connection pool utilization
alertManager.send("HIGH_CONNECTION_POOL_UTILIZATION",
"Connection pool " + (utilization * 100) + "% full");
}
if (stats.get(DriverStatsKey.THREADS_WAITING_FOR_CONNECTION) > 0) {
// Alert: Thread starvation
alertManager.send("THREAD_STARVATION",
"Threads waiting for connections: " +
stats.get(DriverStatsKey.THREADS_WAITING_FOR_CONNECTION));
}
double errorRate = stats.get(DriverStatsKey.ERRORS) /
stats.get(DriverStatsKey.CONNECTIONS_OPENED);
if (errorRate > 0.05) {
// Alert: High error rate
alertManager.send("HIGH_ERROR_RATE",
"Connection error rate: " + (errorRate * 100) + "%");
}
Backup and Recovery¶
1. Database Backup Strategy¶
MongoDB Backup (using mongodump):
#!/bin/bash
# backup-script.sh
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR="/backups/mongodb"
DATABASE="myapp_prod"
# Create backup
mongodump --host rs0/mongo1:27017,mongo2:27017,mongo3:27017 \
--db $DATABASE \
--out $BACKUP_DIR/$DATE \
--username $MONGO_USERNAME \
--password $MONGO_PASSWORD
# Compress backup
tar -czf $BACKUP_DIR/backup_${DATABASE}_${DATE}.tar.gz -C $BACKUP_DIR $DATE
rm -rf $BACKUP_DIR/$DATE
# Cleanup old backups (keep 30 days)
find $BACKUP_DIR -name "backup_${DATABASE}_*.tar.gz" -mtime +30 -delete
echo "Backup completed: backup_${DATABASE}_${DATE}.tar.gz"
2. Message Queue Backup¶
Since messaging uses MongoDB collections:
#!/bin/bash
# Include message collections in backup
mongodump --host rs0/mongo1:27017,mongo2:27017,mongo3:27017 \
--db myapp_prod \
--collection msg_queue \
--collection msg_queue_locks \
--out /backups/messaging/$(date +%Y%m%d_%H%M%S)
3. Disaster Recovery Procedures¶
Recovery Checklist: 1. Restore MongoDB data from latest backup 2. Verify replica set status and elect primary 3. Update application configuration if needed 4. Start application services 5. Verify connectivity and basic operations 6. Monitor for any issues
Recovery Script:
#!/bin/bash
# disaster-recovery.sh
BACKUP_FILE=$1
DATABASE="myapp_prod"
if [ -z "$BACKUP_FILE" ]; then
echo "Usage: $0 <backup_file.tar.gz>"
exit 1
fi
# Extract backup
TEMP_DIR=$(mktemp -d)
tar -xzf $BACKUP_FILE -C $TEMP_DIR
# Stop application
kubectl scale deployment myapp --replicas=0
# Restore database
mongorestore --host rs0/mongo1:27017,mongo2:27017,mongo3:27017 \
--db $DATABASE \
--drop \
--dir $TEMP_DIR/myapp_prod \
--username $MONGO_USERNAME \
--password $MONGO_PASSWORD
# Verify replica set status
mongo --host mongo1:27017 --eval "rs.status()"
# Restart application
kubectl scale deployment myapp --replicas=3
# Cleanup
rm -rf $TEMP_DIR
echo "Recovery completed. Monitor application logs for issues."
Performance Monitoring¶
1. Key Metrics to Track¶
Application Level:
// Connection pool metrics
- connections_in_use / connections_in_pool (target: < 0.8)
- threads_waiting_for_connection (target: 0)
- connection_errors (target: < 5% of total)
// Performance metrics
- average_response_time (target: < SLA requirements)
- throughput (ops/second)
- cache_hit_ratio (target: > 0.7 for cached entities)
System Level:
# Monitor these system metrics
- CPU utilization (target: < 70%)
- Memory usage (target: < 80%)
- Network I/O
- Disk I/O and space
2. Performance Testing in Production¶
Canary Deployment Testing:
// Gradual rollout with performance monitoring
// Deploy to 5% of traffic, monitor metrics, then expand
@Component
public class PerformanceMonitor {
private final MeterRegistry meterRegistry;
@EventListener
public void onQueryExecution(QueryExecutionEvent event) {
Timer.Sample sample = Timer.start(meterRegistry);
sample.stop(Timer.builder("morphium.query.duration")
.tag("collection", event.getCollection())
.register(meterRegistry));
}
}
Troubleshooting Common Production Issues¶
Issue: Connection Pool Exhaustion¶
Symptoms: Timeouts, high response times Solution:
// Immediate: Increase pool size
cfg.connectionSettings().setMaxConnectionsPerHost(100);
// Long-term: Optimize query patterns and add caching
Issue: Memory Leaks¶
Symptoms: Gradual memory increase, OOM errors Solution:
// Add connection lifecycle limits
cfg.connectionSettings().setMaxConnectionLifetime(600000); // 10 minutes
cfg.connectionSettings().setMaxConnectionIdleTime(180000); // 3 minutes
// Review cache sizes
@Cache(maxEntries = 5000, timeout = 300000) // Bounded cache
Issue: Network Partitions¶
Symptoms: Intermittent connection failures Solution:
// Increase retry tolerance
cfg.connectionSettings().setRetriesOnNetworkError(15);
cfg.connectionSettings().setSleepBetweenErrorRetries(500);
cfg.clusterSettings().setServerSelectionTimeout(10000);
This comprehensive deployment guide covers the essential aspects of running Morphium in production, from initial configuration to ongoing monitoring and maintenance.