Go Session Management System
This challenge asks you to implement a robust session management system for a Go web application. Effective session management is crucial for maintaining user state across multiple HTTP requests, enabling features like user authentication, shopping carts, and personalized content. You will build a system that can create, retrieve, and invalidate user sessions.
Problem Description
You need to develop a Go package that provides the core functionality for managing user sessions. This system should allow for the creation of unique session IDs, the storage of session data associated with those IDs, and the ability to retrieve or delete session data based on the session ID.
Key Requirements:
- Session Creation: A function to generate a new, unique session ID. This ID should be cryptographically secure and sufficiently random to prevent guessing.
- Session Storage: A mechanism to store arbitrary key-value data for each session. This could be in-memory for simplicity in this challenge, but a real-world implementation might use a database or distributed cache.
- Session Retrieval: A function to retrieve session data given a session ID.
- Session Invalidation/Deletion: A function to remove a session and its associated data.
- Concurrency Safety: The session management system must be safe to use concurrently from multiple goroutines.
- Session Expiration (Optional but Recommended): Implement a mechanism to automatically remove sessions that have been inactive for a certain period.
Expected Behavior:
- When a new session is created, a unique ID is returned.
- Data can be stored and retrieved for a given session ID.
- If a session ID does not exist, attempting to retrieve its data should return an error or a clear indication of absence.
- Deleting a session should prevent further retrieval of its data.
- The system should handle multiple concurrent operations without data corruption or race conditions.
Edge Cases to Consider:
- What happens if a session ID is requested that has been deleted?
- How does the system handle very high concurrency?
- What if session data is extremely large? (While not a primary focus of this challenge, consider the implications).
Examples
Example 1: Basic Session Operations
// Assume 'sessionManager' is an instance of your session management system
// 1. Create a new session
sessionID, err := sessionManager.CreateSession()
if err != nil {
// Handle error
}
fmt.Println("Created Session ID:", sessionID)
// 2. Store data in the session
err = sessionManager.Set(sessionID, "username", "alice")
if err != nil {
// Handle error
}
err = sessionManager.Set(sessionID, "theme", "dark")
if err != nil {
// Handle error
}
// 3. Retrieve data from the session
username, ok := sessionManager.Get(sessionID, "username")
if ok {
fmt.Println("Username:", username) // Expected: Alice
}
theme, ok := sessionManager.Get(sessionID, "theme")
if ok {
fmt.Println("Theme:", theme) // Expected: dark
}
// 4. Attempt to retrieve non-existent data
cart, ok := sessionManager.Get(sessionID, "cart")
if !ok {
fmt.Println("Cart not found for session.") // Expected: Cart not found for session.
}
// 5. Invalidate the session
err = sessionManager.DeleteSession(sessionID)
if err != nil {
// Handle error
}
// 6. Attempt to retrieve data after deletion
_, ok = sessionManager.Get(sessionID, "username")
if !ok {
fmt.Println("Username not found after session deletion.") // Expected: Username not found after session deletion.
}
Output of Example 1:
Created Session ID: [a_unique_session_id]
Username: alice
Theme: dark
Cart not found for session.
Username not found after session deletion.
Example 2: Handling Non-existent Sessions
// Assume 'sessionManager' is an instance of your session management system
invalidSessionID := "non-existent-session-id-123"
// Attempt to set data to a non-existent session
err = sessionManager.Set(invalidSessionID, "user_id", "12345")
if err != nil {
fmt.Println("Error setting data to non-existent session:", err) // Expected: Error setting data to non-existent session: session not found
}
// Attempt to get data from a non-existent session
data, ok := sessionManager.Get(invalidSessionID, "any_key")
if !ok {
fmt.Println("Data retrieval from non-existent session failed as expected.") // Expected: Data retrieval from non-existent session failed as expected.
}
// Attempt to delete a non-existent session
err = sessionManager.DeleteSession(invalidSessionID)
if err != nil {
fmt.Println("Error deleting non-existent session:", err) // Expected: Error deleting non-existent session: session not found
}
Output of Example 2:
Error setting data to non-existent session: session not found
Data retrieval from non-existent session failed as expected.
Error deleting non-existent session: session not found
Example 3: Concurrency Test (Conceptual)
This example illustrates the concept of concurrency. You would need to write actual Go code to test this by launching multiple goroutines that perform operations on the sessionManager simultaneously.
// In a real test, you'd use Go's testing package and concurrency primitives.
// Imagine this is run by 100 goroutines concurrently:
sessionID, _ := sessionManager.CreateSession()
// Goroutine 1: Sets data
go func() {
sessionManager.Set(sessionID, "counter", 1)
}()
// Goroutine 2: Sets different data
go func() {
sessionManager.Set(sessionID, "status", "active")
}()
// Goroutine 3: Reads data
go func() {
value, ok := sessionManager.Get(sessionID, "counter")
if ok {
// Process value
}
}()
// Goroutine 4: Deletes session after a delay
go func() {
time.Sleep(1 * time.Second)
sessionManager.DeleteSession(sessionID)
}()
// The critical part is that all these operations must complete without data races
// and the final state of the session (or its absence) must be consistent.
Constraints
- Session IDs must be at least 32 characters long.
- Session data can be of any string type (key and value). For simplicity, assume values are strings.
- The primary storage mechanism for sessions should be in-memory using Go's built-in data structures.
- The solution should aim to handle at least 1000 concurrent operations per second on a single modern CPU core without significant performance degradation or errors.
- Implement session expiration with a configurable timeout (e.g., 30 minutes).
Notes
- Consider using
sync.Mutexorsync.RWMutexfor protecting shared session data from concurrent access. - For generating unique session IDs, the
crypto/randpackage is recommended. - Think about how you will store the session data. A
map[string]map[string]string(sessionID -> key -> value) might be a good starting point for in-memory storage. - For session expiration, you'll need a way to track the last access time of each session and a mechanism to periodically clean up expired sessions. A background goroutine could handle this.
- The
SetandGetoperations should be idempotent where appropriate. - Error handling is crucial. Define clear error messages for common issues (e.g., "session not found").