v0.19.0 use updated config and toml packages, dep update, minor fixes
Test / test (push) Canceled after 0s

This commit is contained in:
lixen committed 2026-09-29 00:20:53 -04:00
1 parent b8982e4e44
commit cb0da23070
22 files changed
+692 -232

No files matched your search

+3 -5
View File
@@ -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
}
}
+20 -6
View File
@@ -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)
}
}
+45
View File
@@ -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")
}
}