# MongoDB Handler

A singleton MongoDB handler with context manager support for MongoDB collections and automatic retry capabilities.

## Features

- **Singleton Pattern**: Ensures only one instance of the MongoDB handler exists
- **Context Manager**: Automatically manages connection lifecycle
- **Retry Capability**: Automatically retries MongoDB operations on failure
- **Connection Pooling**: Configurable connection pooling
- **Graceful Degradation**: Handles connection failures without crashing

## Usage

```python
from Controllers.Mongo.database import mongo_handler

# Use the context manager to access a collection
with mongo_handler.collection("users") as users_collection:
    # Perform operations on the collection
    users_collection.insert_one({"username": "john", "email": "john@example.com"})
    user = users_collection.find_one({"username": "john"})
    # Connection is automatically closed when exiting the context
```

## Configuration

MongoDB connection settings are configured via environment variables with the `MONGO_` prefix:

- `MONGO_ENGINE`: Database engine (e.g., "mongodb")
- `MONGO_USER`: MongoDB username
- `MONGO_PASSWORD`: MongoDB password
- `MONGO_HOST`: MongoDB host
- `MONGO_PORT`: MongoDB port
- `MONGO_DB`: Database name
- `MONGO_AUTH_DB`: Authentication database

## Monitoring Connection Closure

To verify that MongoDB sessions are properly closed, you can implement one of the following approaches:

### 1. Add Logging to the `__exit__` Method

```python
def __exit__(self, exc_type, exc_val, exc_tb):
    """
    Exit context, closing the connection.
    """
    if self.client:
        print(f"Closing MongoDB connection for collection: {self.collection_name}")
        # Or use a proper logger
        # logger.info(f"Closing MongoDB connection for collection: {self.collection_name}")
        self.client.close()
        self.client = None
        self.collection = None
        print(f"MongoDB connection closed successfully")
```

### 2. Add Connection Tracking

```python
class MongoDBHandler:
    # Add these to your class
    _open_connections = 0
    
    def get_connection_stats(self):
        """Return statistics about open connections"""
        return {"open_connections": self._open_connections}
```

Then modify the `CollectionContext` class:

```python
def __enter__(self):
    try:
        # Create a new client connection
        self.client = MongoClient(self.db_handler.uri, **self.db_handler.client_options)
        # Increment connection counter
        self.db_handler._open_connections += 1
        # Rest of your code...
        
def __exit__(self, exc_type, exc_val, exc_tb):
    if self.client:
        # Decrement connection counter
        self.db_handler._open_connections -= 1
        self.client.close()
        self.client = None
        self.collection = None
```

### 3. Use MongoDB's Built-in Monitoring

```python
from pymongo import monitoring

class ConnectionCommandListener(monitoring.CommandListener):
    def started(self, event):
        print(f"Command {event.command_name} started on server {event.connection_id}")
    
    def succeeded(self, event):
        print(f"Command {event.command_name} succeeded in {event.duration_micros} microseconds")
    
    def failed(self, event):
        print(f"Command {event.command_name} failed in {event.duration_micros} microseconds")

# Register the listener
monitoring.register(ConnectionCommandListener())
```

### 4. Add a Test Function

```python
def test_connection_closure():
    """Test that MongoDB connections are properly closed."""
    print("\nTesting connection closure...")
    
    # Record initial connection count (if you implemented the counter)
    initial_count = mongo_handler.get_connection_stats()["open_connections"]
    
    # Use multiple nested contexts
    for i in range(5):
        with mongo_handler.collection("test_collection") as collection:
            # Do some simple operation
            collection.find_one({})
    
    # Check final connection count
    final_count = mongo_handler.get_connection_stats()["open_connections"]
    
    if final_count == initial_count:
        print("Test passed: All connections were properly closed")
        return True
    else:
        print(f"Test failed: {final_count - initial_count} connections remain open")
        return False
```

### 5. Use MongoDB Server Logs

You can also check the MongoDB server logs to see connection events:

```bash
# Run this on your MongoDB server
tail -f /var/log/mongodb/mongod.log | grep "connection"
```

## Best Practices

1. Always use the context manager pattern to ensure connections are properly closed
2. Keep operations within the context manager as concise as possible
3. Handle exceptions within the context to prevent unexpected behavior
4. Avoid nesting multiple context managers unnecessarily
5. Use the retry decorator for operations that might fail due to transient issues

## LXC Container Configuration

### Authentication Issues

If you encounter authentication errors when connecting to the MongoDB container at 10.10.2.13:27017, you may need to update the container configuration:

1. **Check MongoDB Authentication**: Ensure the MongoDB container is configured with the correct authentication mechanism

2. **Verify Network Configuration**: Make sure the container network allows connections from your application

3. **Update MongoDB Configuration**:
   - Edit the MongoDB configuration file in the container
   - Ensure `bindIp` is set correctly (e.g., `0.0.0.0` to allow connections from any IP)
   - Check that authentication is enabled with the correct mechanism

4. **User Permissions**:
   - Verify that the application user (`appuser`) exists in the MongoDB instance
   - Ensure the user has the correct roles and permissions for the database

### Example MongoDB Container Configuration

```yaml
# Example docker-compose.yml configuration
services:
  mongodb:
    image: mongo:latest
    container_name: mongodb
    environment:
      - MONGO_INITDB_ROOT_USERNAME=admin
      - MONGO_INITDB_ROOT_PASSWORD=password
    volumes:
      - ./init-mongo.js:/docker-entrypoint-initdb.d/init-mongo.js:ro
    ports:
      - "27017:27017"
    command: mongod --auth
```

```javascript
// Example init-mongo.js
db.createUser({
  user: 'appuser',
  pwd: 'apppassword',
  roles: [
    { role: 'readWrite', db: 'appdb' }
  ]
});
```

## Troubleshooting

### Common Issues

1. **Authentication Failed**:
   - Verify username and password in environment variables
   - Check that the user exists in the specified authentication database
   - Ensure the user has appropriate permissions

2. **Connection Refused**:
   - Verify the MongoDB host and port are correct
   - Check network connectivity between application and MongoDB container
   - Ensure MongoDB is running and accepting connections

3. **Resource Leaks**:
   - Use the context manager pattern to ensure connections are properly closed
   - Monitor connection pool size and active connections
   - Implement proper error handling to close connections in case of exceptions
