Hone logo
Hone
Problems

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:

  1. ConnectionManager Struct: Create a struct named ConnectionManager.
  2. NewConnectionManager(): A constructor function to initialize and return a ConnectionManager.
  3. Connect(address string) (connectionID string, err error): This method should attempt to establish a connection to the given address. Upon successful connection, it should return a unique connectionID (e.g., a UUID or a sequential integer) and nil error. If connection fails, it should return an empty string and an appropriate error.
  4. GetConnection(connectionID string) (net.Conn, error): This method should retrieve an active connection by its connectionID. It should return the net.Conn and nil error if found, otherwise an error indicating the connection was not found.
  5. CloseConnection(connectionID string) error: This method should gracefully close a specific connection identified by connectionID. It should return nil if successful or if the connection doesn't exist.
  6. Shutdown() error: This method should close all active connections managed by the ConnectionManager and return any error encountered during the shutdown process.
  7. 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 connectionID should return an error.
  • Closing a non-existent connection should not result in an error.
  • Shutdown should iterate through all managed connections, close them, and clean up internal state.
  • Operations like Connect, GetConnection, CloseConnection, and Shutdown should be safe to call concurrently.

Edge Cases:

  • What happens if Connect is called with an invalid address format?
  • What happens if Connect is 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 Shutdown multiple 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 connectionID generated by Connect must be unique across all active connections.
  • The manager should handle up to 1000 concurrent Connect operations without significant performance degradation.
  • Memory usage for tracking connections should be reasonable, scaling linearly with the number of active connections.
  • The Connect method should have a timeout of 5 seconds for establishing a connection.

Notes

  • Consider using sync.Mutex or sync.RWMutex for protecting shared data structures within the ConnectionManager to ensure concurrency safety.
  • For generating unique connectionIDs, consider using the github.com/google/uuid package 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 net package in Go provides the necessary primitives for network operations.
  • Think about how you will store and retrieve connections. A map[string]net.Conn is a common starting point.
  • For error handling, use descriptive error messages. You might want to define custom error types for specific scenarios like ErrConnectionNotFound.
Loading editor...
go