I’m writing my own session handler
session.go
var (
p *pool
)
type (
Session struct {
sid string
lock sync.Mutex
closed bool
expires int64
data session_data
}
sessions map[string]*Session
session_data map[string]any
)
func init(){
p = &pool{
sessions: sessions{},
}
}
// Start session and lock for other concurrent requests to read data from the same session
func Start(w http.ResponseWriter, r *http.Request) *Session {
s, expired := p.get(sid);
if expired {
p.delete(sid)
return nil
}
if s != nil {
return s
}
}
// Get session data
func (s *Session) Data() session_data {
return s.data
}
// Close session for further writes and release read lock
func (s *Session) Close(){
if s.Closed() {
return false
}
s.closed = true;
/*
How to make this object a copy and detach it from the "pool" ?
*/
s.lock.Unlock()
return true
}
pool.go
type pool struct {
lock sync.RWMutex
sessions sessions
}
func (p *pool) get(sid string) (*Session, bool){
p.lock.RLock()
defer p.lock.RUnlock()
s, ok := p.sessions[sid]
if !ok {
return nil, false
}
s.lock.Lock()
// Check if session is expired
if time_unix() > s.expires {
s.lock.Unlock()
return nil, true
}
return s, false
}
func (p *pool) set(sid string, s *Session){
p.lock.Lock()
defer p.lock.Unlock()
p.sessions[sid] = s
}
Current design:
There is a mutex on both *pool
and *Session
. The mutex on the *Session
ensures the same session ID can not be fetched from the pool as long it’s already opened.
After the session is closed and the mutex is unlocked the session data is still available.
Problem:
For concurrency performance you want to close the session as quick as possible. After the session is closed the user might still want to fetch the session data. If the user in the meantime has opened the same session in a new request and writes updates then the data in the first request will have inconsistent data.
How is it possible to copy the *Session
struct after the session is closed to ensure the data always is consistent and not will be affected from other concurrency requests?
The design (as far I have tested with -race
) there should not be any issues. It’s only if the session data is fetched for read-only purposes after it is closed and the session has been opened and modified from another request.
Or is my design “bad” design?
Example:
- Request A: Starts and closes session
- Request B: Opens the same session id and writes to the session
- Request A: Fetches the data with
s.Data()
but now the data is inconsistent
How to detach the *Session
struct from the pool by making the pointer a copy of the one in the session pool?