Go Connection Manager: Robust Network Handling
This challenge focuses on building a resilient and efficient connection manager in Go. You'll implement a system that can establish, track, and gracefully close network connections, handling potential errors and ensuring resources are managed effectively. This is crucial for any application that interacts with external services or maintains long-lived client connections.
Problem Description
Your task is to design and implement a ConnectionManager in Go that can manage a pool of network connections. The manager should be able to:
- Establish Connections: Accept a network address and establish a new connection to it.
- Track Connections: Maintain a record of all active connections, ideally allowing for retrieval by a unique identifier.
- Handle Errors: Detect and report connection errors (e.g., network unreachable, authentication failures, connection resets).
- Graceful Shutdown: Provide a mechanism to close all active connections cleanly, releasing underlying resources.
- Concurrency Safety: Ensure that the connection management operations are safe to be called from multiple goroutines concurrently.
Key Requirements:
ConnectionManagerStruct: Create a struct namedConnectionManager.NewConnectionManager(): A constructor function to initialize and return aConnectionManager.Connect(address string) (connectionID string, err error): This method should attempt to establish a connection to the givenaddress. Upon successful connection, it should return a uniqueconnectionID(e.g., a UUID or a sequential integer) andnilerror. If connection fails, it should return an empty string and an appropriate error.GetConnection(connectionID string) (net.Conn, error): This method should retrieve an active connection by itsconnectionID. It should return thenet.Connandnilerror if found, otherwise an error indicating the connection was not found.CloseConnection(connectionID string) error: This method should gracefully close a specific connection identified byconnectionID. It should returnnilif successful or if the connection doesn't exist.Shutdown() error: This method should close all active connections managed by theConnectionManagerand return any error encountered during the shutdown process.- Error Handling: Implement robust error handling for all operations.
Expected Behavior:
- Successfully connecting to a valid address should result in a new connection being tracked.
- Attempting to get a connection with an invalid or closed
connectionIDshould return an error. - Closing a non-existent connection should not result in an error.
Shutdownshould iterate through all managed connections, close them, and clean up internal state.- Operations like
Connect,GetConnection,CloseConnection, andShutdownshould be safe to call concurrently.
Edge Cases:
- What happens if
Connectis called with an invalid address format? - What happens if
Connectis called repeatedly for the same address? - What happens if a connection is lost unexpectedly after being established? (While full detection of spontaneous disconnects might be out of scope for the basic implementation, consider how your manager might eventually discover this, e.g., upon attempting to write to it.)
- Calling
Shutdownmultiple times.
Examples
Example 1: Successful Connection and Retrieval
// Assume a mock TCP server is listening on "localhost:8080"
manager := NewConnectionManager()
id, err := manager.Connect("localhost:8080")
if err != nil {
// Handle connection error
}
conn, err := manager.GetConnection(id)
if err != nil {
// Handle retrieval error
}
// conn is a valid net.Conn object
fmt.Printf("Successfully connected and retrieved connection with ID: %s\n", id)
Example 2: Closing a Connection
manager := NewConnectionManager()
id, _ := manager.Connect("localhost:8081") // Assume successful
err := manager.CloseConnection(id)
if err != nil {
// Handle closing error
}
_, err = manager.GetConnection(id)
if err != nil {
// Expected: Error indicating connection not found or closed
fmt.Printf("Connection %s is no longer available: %v\n", id, err)
}
Example 3: Shutdown
manager := NewConnectionManager()
id1, _ := manager.Connect("localhost:8082")
id2, _ := manager.Connect("localhost:8083")
fmt.Println("Initiating shutdown...")
err := manager.Shutdown()
if err != nil {
// Handle shutdown errors
}
fmt.Println("Shutdown complete.")
_, err = manager.GetConnection(id1)
if err != nil {
fmt.Printf("Connection %s is closed after shutdown: %v\n", id1, err)
}
_, err = manager.GetConnection(id2)
if err != nil {
fmt.Printf("Connection %s is closed after shutdown: %v\n", id2, err)
}
Constraints
- The
connectionIDgenerated byConnectmust be unique across all active connections. - The manager should handle up to 1000 concurrent
Connectoperations without significant performance degradation. - Memory usage for tracking connections should be reasonable, scaling linearly with the number of active connections.
- The
Connectmethod should have a timeout of 5 seconds for establishing a connection.
Notes
- Consider using
sync.Mutexorsync.RWMutexfor protecting shared data structures within theConnectionManagerto ensure concurrency safety. - For generating unique
connectionIDs, consider using thegithub.com/google/uuidpackage or a simple counter if the expected number of connections is manageable. - When closing connections, ensure that any associated goroutines or resources managed by your application related to that connection are also cleaned up.
- The
netpackage in Go provides the necessary primitives for network operations. - Think about how you will store and retrieve connections. A
map[string]net.Connis a common starting point. - For error handling, use descriptive error messages. You might want to define custom error types for specific scenarios like
ErrConnectionNotFound.