# Troubleshooting Guide Common issues and their solutions when using Morphium. ## Connection Issues ### Cannot Connect to MongoDB **Symptoms:** - `MorphiumDriverException: Could not get connection to [host] in time [timeout]ms` - Application startup fails with connection timeout **Diagnosis:** ```xml ``` **Common Causes & Solutions:** 1. **Incorrect host/port configuration** ```java // Check your host configuration cfg.clusterSettings().addHostToSeed("correct-hostname", 27017); ``` 2. **Network connectivity issues** - Verify MongoDB is running: `mongosh --host hostname:port` - Check firewall rules and network policies - Test connectivity: `telnet hostname 27017` 3. **Authentication failures** ```java // Verify credentials cfg.authSettings().setMongoLogin("correct-username"); cfg.authSettings().setMongoPassword("correct-password"); ``` 4. **Connection pool exhaustion** ```java // Increase connection limits cfg.connectionSettings().setMaxConnectionsPerHost(200); cfg.connectionSettings().setMaxWaitTime(10000); ``` ### Replica Set Connection Issues **Symptoms:** - `MorphiumDriverException: No primary node defined - not connected yet?` - Intermittent connection failures **Solutions:** 1. **Verify replica set configuration** ```javascript // In MongoDB shell rs.status() rs.conf() ``` 2. **Check replica set name** ```java cfg.clusterSettings().setReplicaSetName("correct-replica-set-name"); ``` 3. **Add all replica set members to seed list** ```java cfg.clusterSettings().addHostToSeed("mongo1", 27017); cfg.clusterSettings().addHostToSeed("mongo2", 27017); cfg.clusterSettings().addHostToSeed("mongo3", 27017); ``` ### Every write of one entity type takes exactly `wtimeout` after a node went down **Symptom:** after a replica-set member crashed or was shut down, `store()` for one entity type (often a test entity such as `UncachedObject`) takes exactly its `@WriteSafety.timeout` (10 s by default in the test suite) on every call, while writes of other types are fast. The server logs `Write concern timeout ... w=3, acknowledged=1/2` or similar; the client logs nothing, because the write *did* succeed on the primary and the missed replication only arrives as a `writeConcernError`, which Morphium does not surface yet (#408). **Cause:** the entity uses `@WriteSafety(level = WAIT_FOR_ALL_SLAVES)`. Morphium turns that into a numeric `w` equal to the number of replica-set members the driver can currently reach. Until the driver's heartbeat has failed against the dead member once (at most one `heartbeatFrequency` plus the connect timeout), `w` is still the full member count and cannot be satisfied. **What to check:** - The hang should end by itself after the detection window. If it does not, the driver still counts the dead member as reachable: enable debug logging for `de.caluga.morphium.Morphium` and watch `Active nodes now: N` on each write, and `Could not create connection to host` / `Still cannot connect to host` from the `PooledDriver` heartbeat. - `heartbeatFrequency` (`clusterSettings().setHeartbeatFrequency(ms)`) bounds the window; a lower value shortens it at the cost of more hello traffic. - `timeout = 0` on the annotation means "wait forever": writes inside the window never return. Use a finite `wtimeout`. - If you cannot afford the window, switch the entity to `SafetyLevel.MAJORITY`, which the server evaluates against the real configuration and which tolerates a minority of members being down. See [Write Safety](./developer-guide.md#write-safety-writesafety) in the developer guide for the semantics of each level. ## Performance Issues ### Slow Query Performance **Symptoms:** - High response times - CPU usage spikes - Memory growth **Diagnosis:** ```xml ``` ```java // Monitor driver statistics Map stats = morphium.getDriver().getDriverStats(); System.out.println("Connections in use: " + stats.get(DriverStatsKey.CONNECTIONS_IN_USE)); System.out.println("Connections in pool: " + stats.get(DriverStatsKey.CONNECTIONS_IN_POOL)); ``` **Solutions:** 1. **Add missing indexes** ```java @Entity @Index({"fieldName", "composite,field"}) // Add compound indexes public class MyEntity { @Index private String frequentlyQueriedField; } // Create indexes manually morphium.ensureIndicesFor(MyEntity.class); ``` 2. **Optimize query patterns** ```java // Use projection to limit returned fields Query q = morphium.createQueryFor(MyEntity.class) .f("status").eq("active") .project("name", "email"); // Only return specific fields // Use pagination for large result sets List results = q.skip(offset).limit(pageSize).asList(); ``` 3. **Review caching configuration** ```java @Entity @Cache(timeout = 60000, maxEntries = 10000) // Adjust cache settings public class MyEntity { /* ... */ } ``` ### Memory Issues **Symptoms:** - `OutOfMemoryError` - Gradual memory increase - Long GC pauses **Diagnosis:** ```java // Monitor connection pool stats Map stats = morphium.getDriver().getDriverStats(); Double connectionsInUse = stats.get(DriverStatsKey.CONNECTIONS_IN_USE); Double connectionsInPool = stats.get(DriverStatsKey.CONNECTIONS_IN_POOL); System.out.println("Memory usage - Connections: " + connectionsInUse + "/" + connectionsInPool); ``` **Solutions:** 1. **Connection pool sizing** ```java // Adjust pool size based on actual usage cfg.connectionSettings().setMaxConnectionsPerHost(50); // Reduce if too high cfg.connectionSettings().setMaxConnectionIdleTime(60000); // Close idle connections sooner cfg.connectionSettings().setMaxConnectionLifetime(300000); // Shorter connection lifetime ``` 2. **Cache tuning** ```java @Entity @Cache(maxEntries = 1000, timeout = 30000, strategy = Cache.ClearStrategy.LRU) public class MyEntity { /* ... */ } ``` 3. **Large result set handling** ```java // Use iterator for large datasets MorphiumIterator iterator = query.asIterable(100); // Process in batches while (iterator.hasNext()) { MyEntity entity = iterator.next(); // Process entity } ``` ## Messaging Issues ### Messages Not Being Processed **Symptoms:** - Messages accumulating in queue - Listeners not receiving messages - High message latency **Diagnosis:** ```java // Check messaging statistics MessagingStatsKey stats = messaging.getStats(); System.out.println("Messages processed: " + stats.getMessagesProcessed()); System.out.println("Messages failed: " + stats.getMessagesFailed()); ``` **Solutions:** 1. **Check listener registration** ```java // Ensure listener is properly registered messaging.addListenerForTopic("mytopic", (morphiumMessaging, msg) -> { System.out.println("Processing: " + msg.getMsg()); return null; // or return response message }); // Verify messaging is started messaging.start(); ``` 2. **Increase processing capacity** ```java // Configure for higher throughput cfg.messagingSettings().setMessagingWindowSize(200); cfg.messagingSettings().setMessagingMultithreadded(true); cfg.messagingSettings().setPollPauseTime(100); // Reduce pause time ``` 3. **Check for exceptions in listeners** ```java messaging.addListenerForTopic("mytopic", (morphiumMessaging, msg) -> { try { // Your processing logic return processMessage(msg); } catch (Exception e) { log.error("Message processing failed", e); // Decide whether to return error response or null return null; } }); ``` ### Message Ordering Issues **Symptoms:** - Messages processed out of order - Race conditions in message processing **Solutions:** 1. **Use exclusive messaging for ordering** ```java Msg msg = new Msg("ordered-topic", "content", "value"); msg.setExclusive(true); // Ensure only one listener processes at a time messaging.sendMessage(msg); ``` 2. **Single-threaded processing for critical topics** ```java // Configure single-threaded processing for specific topics cfg.messagingSettings().setMessagingMultithreadded(false); ``` ## Logging and Debugging ### Enable Debug Logging ```xml ``` ### Key Log Patterns to Watch **Connection Issues:** ``` ERROR: Could not get connection to [host] in time [timeout]ms WARN: Connection timeout ERROR: Could not create connection to host [host] ``` **Performance Issues:** ``` WARN: Connection pool exhausted DEBUG: Query took [time]ms WARN: Cache hit ratio low: [ratio] ``` **Messaging Issues:** ``` ERROR: Message processing failed WARN: Message queue size growing: [size] DEBUG: Message processed in [time]ms ``` ## Monitoring Key Metrics ### Connection Pool Metrics ```java Map stats = morphium.getDriver().getDriverStats(); // Monitor these key metrics: Double connectionsInUse = stats.get(DriverStatsKey.CONNECTIONS_IN_USE); Double connectionsInPool = stats.get(DriverStatsKey.CONNECTIONS_IN_POOL); Double connectionsOpened = stats.get(DriverStatsKey.CONNECTIONS_OPENED); Double connectionsClosed = stats.get(DriverStatsKey.CONNECTIONS_CLOSED); Double errors = stats.get(DriverStatsKey.ERRORS); Double threadsWaiting = stats.get(DriverStatsKey.THREADS_WAITING_FOR_CONNECTION); ``` ### Performance Alerts Set up monitoring for these conditions: 1. **Connection pool exhaustion** - `connectionsInUse / maxConnectionsPerHost > 0.8` 2. **High error rate** - `errors / (connectionsOpened + errors) > 0.05` 3. **Thread starvation** - `threadsWaiting > 0` for extended periods 4. **Connection churn** - High `connectionsOpened` and `connectionsClosed` rates ## Common Configuration Mistakes ### 1. Undersized Connection Pool ```java // Problem: Too few connections for load cfg.connectionSettings().setMaxConnectionsPerHost(5); // Too small // Solution: Size based on concurrent operations cfg.connectionSettings().setMaxConnectionsPerHost(50); ``` ### 2. Excessive Connection Timeout ```java // Problem: Long timeouts hide connection issues cfg.connectionSettings().setMaxWaitTime(60000); // Too long // Solution: Fail fast to detect issues cfg.connectionSettings().setMaxWaitTime(5000); ``` ### 3. Missing Indexes ```java // Problem: No indexes on frequently queried fields @Entity public class User { private String email; // Frequently queried but not indexed } // Solution: Add appropriate indexes @Entity @Index({"email", "status,email"}) // Single and compound indexes public class User { @Index private String email; private String status; } ``` ### 4. Inefficient Cache Configuration ```java // Problem: Cache settings don't match usage patterns @Cache(timeout = 1000, maxEntries = 100) // Too short timeout, too small cache // Solution: Match cache to data access patterns @Cache(timeout = 300000, maxEntries = 10000, strategy = Cache.ClearStrategy.LRU) ``` ## PoppyDB-Specific Issues ### Illegal Opcode / Wire Protocol Corruption **Symptoms:** - `MorphiumDriverNetworkException: Illegal opcode 0` or random large opcode values - `wrong section ID` errors **Cause (fixed in 6.2.2):** Concurrent writes to the same Netty channel from different threads. Change stream event responses were written from background threads while the I/O thread was writing other responses. **Solution:** Upgrade to 6.2.2+. If you're on an older version, reduce concurrent change stream watches per connection. ### Thread Leak (PooledDriver) **Symptoms:** - Thread count grows continuously over time - `ConnectionWaiter` threads never terminate - Eventually `OutOfMemoryError: unable to create new native thread` **Cause (fixed in 6.2.2):** `PooledDriver.close()` did not signal waiting `ConnectionWaiter` threads. They remained blocked on `waitCounterCondition` forever. **Solution:** Upgrade to 6.2.2+. As a workaround, ensure `Morphium.close()` is always called in a finally block. ### Change Stream Events Lost After Collection Drop **Symptoms:** - `ChangeStreamMonitor` receives events from a previously dropped collection - Resume-after delivers stale or duplicate events **Cause (fixed in 6.2.2):** The change stream history was not properly purged on collection drop, and in-flight async events could sneak past the drop boundary. **Solution:** Upgrade to 6.2.2+. The drop operation now advances the sequence counter and purges history both before and after the drop notification. ### Tailable Cursor Returns Empty on PoppyDB **Symptoms:** - Tailable cursor `getMore` returns immediately with no data on PoppyDB - Works fine on real MongoDB **Cause (fixed in 6.2.2):** `getMore` for tailable cursors did not send `maxTimeMS`, causing PoppyDB to return immediately instead of waiting for new data. **Solution:** Upgrade to 6.2.2+. The tailable cursor now sends `maxTimeMS=5000` (5s polling interval) by default. ## Getting Help When reporting issues, include: 1. **Configuration details** (sanitized) 2. **Error logs** with timestamps 3. **Driver statistics** output 4. **MongoDB version** and topology 5. **Java version** and JVM settings 6. **Morphium version** 7. **Steps to reproduce** the issue ```java // Collect diagnostic information System.out.println("Morphium version: " + Morphium.getVersion()); System.out.println("Java version: " + System.getProperty("java.version")); System.out.println("Driver stats: " + morphium.getDriver().getDriverStats()); System.out.println("Configuration: " + morphium.getConfig().toString()); ```