v0.19.0 use updated config and toml packages, dep update, minor fixes
Test / test (push) Canceled after 0s
Test / test (push) Canceled after 0s
This commit is contained in:
1 parent
b8982e4e44
commit
cb0da23070
22 files changed
+692
-232
No files matched your search
@@ -29,10 +29,8 @@ func (p *Proxy) CreateSession(remoteAddr string, metadata map[string]any) *Sessi
|
||||
metadata["instance_id"] = p.instanceID
|
||||
|
||||
// Create session with instance-scoped source
|
||||
session := p.manager.CreateSession(remoteAddr, p.instanceID, metadata)
|
||||
session.InstanceID = p.instanceID
|
||||
|
||||
return session
|
||||
// Set identity before publishing the session to concurrent snapshot readers.
|
||||
return p.manager.createSession(remoteAddr, p.instanceID, p.instanceID, metadata)
|
||||
}
|
||||
|
||||
// GetSession retrieves a session if it belongs to this instance
|
||||
@@ -79,4 +77,4 @@ func (p *Proxy) UpdateActivity(sessionID string) bool {
|
||||
// GetInstanceID returns the instance ID this proxy is bound to
|
||||
func (p *Proxy) GetInstanceID() string {
|
||||
return p.instanceID
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"maps"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -59,7 +60,12 @@ func NewManager(maxIdleTime time.Duration) *Manager {
|
||||
|
||||
// CreateSession creates and stores a new session for a connection
|
||||
func (m *Manager) CreateSession(remoteAddr string, source string, metadata map[string]any) *Session {
|
||||
return m.createSession(remoteAddr, source, "", metadata)
|
||||
}
|
||||
|
||||
func (m *Manager) createSession(remoteAddr, source, instanceID string, metadata map[string]any) *Session {
|
||||
session := &Session{
|
||||
InstanceID: instanceID,
|
||||
ID: generateSessionID(),
|
||||
RemoteAddr: remoteAddr,
|
||||
CreatedAt: time.Now(),
|
||||
@@ -127,7 +133,7 @@ func (m *Manager) GetActiveSessions() []*Session {
|
||||
|
||||
sessions := make([]*Session, 0, len(m.sessions))
|
||||
for _, session := range m.sessions {
|
||||
sessions = append(sessions, session)
|
||||
sessions = append(sessions, snapshotSession(session))
|
||||
}
|
||||
return sessions
|
||||
}
|
||||
@@ -139,7 +145,7 @@ func (m *Manager) GetSessionCount() int {
|
||||
return len(m.sessions)
|
||||
}
|
||||
|
||||
// GetSessionsBySource returns all sessions matching a specific source type
|
||||
// GetSessionsBySource returns snapshots of sessions matching a specific source type.
|
||||
func (m *Manager) GetSessionsBySource(source string) []*Session {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
@@ -147,13 +153,13 @@ func (m *Manager) GetSessionsBySource(source string) []*Session {
|
||||
var sessions []*Session
|
||||
for _, session := range m.sessions {
|
||||
if session.Source == source {
|
||||
sessions = append(sessions, session)
|
||||
sessions = append(sessions, snapshotSession(session))
|
||||
}
|
||||
}
|
||||
return sessions
|
||||
}
|
||||
|
||||
// GetActiveSessionsBySource returns all active sessions for a given source
|
||||
// GetActiveSessionsBySource returns snapshots of active sessions for a given source.
|
||||
func (m *Manager) GetActiveSessionsBySource(source string) []*Session {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
@@ -163,12 +169,20 @@ func (m *Manager) GetActiveSessionsBySource(source string) []*Session {
|
||||
|
||||
for _, session := range m.sessions {
|
||||
if session.Source == source && now.Sub(session.LastActivity) < m.maxIdleTime {
|
||||
sessions = append(sessions, session)
|
||||
sessions = append(sessions, snapshotSession(session))
|
||||
}
|
||||
}
|
||||
return sessions
|
||||
}
|
||||
|
||||
// Call under m.mu. Metadata values are immutable after session creation; copy
|
||||
// the top-level map so readers cannot add or remove entries in stored metadata.
|
||||
func snapshotSession(session *Session) *Session {
|
||||
snapshot := *session
|
||||
snapshot.Metadata = maps.Clone(session.Metadata)
|
||||
return &snapshot
|
||||
}
|
||||
|
||||
// GetStats returns statistics about the session manager
|
||||
func (m *Manager) GetStats() map[string]any {
|
||||
m.mu.RLock()
|
||||
@@ -291,4 +305,4 @@ func generateSessionID() string {
|
||||
return fmt.Sprintf("session_%d", time.Now().UnixNano())
|
||||
}
|
||||
return base64.URLEncoding.WithPadding(base64.NoPadding).EncodeToString(b)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package session
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestSessionSnapshotsDoNotRaceActivityUpdates(t *testing.T) {
|
||||
m := NewManager(time.Minute)
|
||||
defer m.Stop()
|
||||
proxy := NewProxy(m, "source")
|
||||
stored := proxy.CreateSession("remote", map[string]any{"label": "original"})
|
||||
readers := []func() []*Session{
|
||||
m.GetActiveSessions,
|
||||
func() []*Session { return m.GetSessionsBySource("source") },
|
||||
func() []*Session { return m.GetActiveSessionsBySource("source") },
|
||||
}
|
||||
var wg sync.WaitGroup
|
||||
wg.Go(func() {
|
||||
for range 1000 {
|
||||
proxy.UpdateActivity(stored.ID)
|
||||
}
|
||||
})
|
||||
for _, read := range readers {
|
||||
for range 100 {
|
||||
snapshots := read()
|
||||
if len(snapshots) != 1 || snapshots[0] == stored || snapshots[0].InstanceID != "source" {
|
||||
t.Errorf("invalid snapshot: %+v", snapshots)
|
||||
break
|
||||
}
|
||||
snapshot := snapshots[0]
|
||||
before := snapshot.LastActivity
|
||||
proxy.UpdateActivity(stored.ID)
|
||||
if !snapshot.LastActivity.Equal(before) {
|
||||
t.Error("activity update changed an earlier snapshot")
|
||||
}
|
||||
snapshot.Metadata["label"] = "changed"
|
||||
}
|
||||
}
|
||||
wg.Wait()
|
||||
if stored.Metadata["label"] != "original" {
|
||||
t.Fatal("snapshot metadata aliases the stored map")
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user