package hotime import ( "encoding/json" "os" "sync" "time" . "code.hoteas.com/golang/hotime/cache" . "code.hoteas.com/golang/hotime/common" ) // session对象 type SessionIns struct { *HoTimeCache SessionId string Map ContextBase mutex sync.RWMutex } // set 保存 session 到缓存,必须在锁内调用或传入深拷贝的 map func (that *SessionIns) setWithCopy() { // #region agent log logFile, _ := os.OpenFile(`d:\work\hotimev1.5\.cursor\debug.log`, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) if logFile != nil { logEntry, _ := json.Marshal(map[string]interface{}{"sessionId": "debug-session", "runId": "run1", "hypothesisId": "A", "location": "session.go:setWithCopy", "message": "Session写入数据库触发", "data": map[string]interface{}{"session_id": that.SessionId, "map_size": len(that.Map)}, "timestamp": time.Now().UnixMilli()}) logFile.Write(append(logEntry, '\n')) logFile.Close() } // #endregion // 深拷贝 Map 防止并发修改 that.mutex.RLock() copyMap := make(Map, len(that.Map)) for k, v := range that.Map { copyMap[k] = v } that.mutex.RUnlock() that.HoTimeCache.Session(HEAD_SESSION_ADD+that.SessionId, copyMap) } func (that *SessionIns) Session(key string, data ...interface{}) *Obj { that.mutex.Lock() if that.Map == nil { that.getWithoutLock() } that.mutex.Unlock() if len(data) != 0 { // #region agent log logFile, _ := os.OpenFile(`d:\work\hotimev1.5\.cursor\debug.log`, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) if logFile != nil { logEntry, _ := json.Marshal(map[string]interface{}{"sessionId": "debug-session", "runId": "run1", "hypothesisId": "B", "location": "session.go:Session", "message": "Session.Set调用", "data": map[string]interface{}{"key": key, "is_delete": data[0] == nil}, "timestamp": time.Now().UnixMilli()}) logFile.Write(append(logEntry, '\n')) logFile.Close() } // #endregion that.mutex.Lock() if data[0] == nil { delete(that.Map, key) } else { that.Map[key] = data[0] } that.mutex.Unlock() // 使用深拷贝版本保存,避免并发问题 that.setWithCopy() return &Obj{Data: nil} } that.mutex.RLock() result := &Obj{Data: that.Map.Get(key)} that.mutex.RUnlock() return result } // SessionsSet 批量设置session字段,只触发一次数据库写入 // 用法:that.SessionsSet(Map{"key1": value1, "key2": value2, ...}) // 性能优化:设置N个字段只触发1次数据库写入(而非N次) func (that *SessionIns) SessionsSet(data Map) { if len(data) == 0 { return } // #region agent log logFile, _ := os.OpenFile(`d:\work\hotimev1.5\.cursor\debug.log`, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) if logFile != nil { keys := make([]string, 0, len(data)) for k := range data { keys = append(keys, k) } logEntry, _ := json.Marshal(map[string]interface{}{"sessionId": "debug-session", "runId": "run1", "hypothesisId": "C", "location": "session.go:SessionsSet", "message": "SessionsSet批量设置", "data": map[string]interface{}{"keys": keys, "count": len(data)}, "timestamp": time.Now().UnixMilli()}) logFile.Write(append(logEntry, '\n')) logFile.Close() } // #endregion that.mutex.Lock() if that.Map == nil { that.getWithoutLock() } // 批量设置所有字段 for key, value := range data { if value == nil { delete(that.Map, key) } else { that.Map[key] = value } } that.mutex.Unlock() // 只触发一次数据库写入 that.setWithCopy() } // SessionsDelete 批量删除session字段,只触发一次数据库写入 // 用法:that.SessionsDelete("key1", "key2", ...) func (that *SessionIns) SessionsDelete(keys ...string) { if len(keys) == 0 { return } // #region agent log logFile, _ := os.OpenFile(`d:\work\hotimev1.5\.cursor\debug.log`, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) if logFile != nil { logEntry, _ := json.Marshal(map[string]interface{}{"sessionId": "debug-session", "runId": "run1", "hypothesisId": "C", "location": "session.go:SessionsDelete", "message": "SessionsDelete批量删除", "data": map[string]interface{}{"keys": keys, "count": len(keys)}, "timestamp": time.Now().UnixMilli()}) logFile.Write(append(logEntry, '\n')) logFile.Close() } // #endregion that.mutex.Lock() if that.Map == nil { that.getWithoutLock() } // 批量删除所有字段 for _, key := range keys { delete(that.Map, key) } that.mutex.Unlock() // 只触发一次数据库写入 that.setWithCopy() } // SessionsGet 批量获取session字段 // 用法:result := that.SessionsGet("key1", "key2", ...) // 返回 Map,key 为字段名,value 为字段值(不存在的 key 不包含在结果中) func (that *SessionIns) SessionsGet(keys ...string) Map { if len(keys) == 0 { return Map{} } that.mutex.Lock() if that.Map == nil { that.getWithoutLock() } that.mutex.Unlock() result := make(Map, len(keys)) that.mutex.RLock() for _, key := range keys { if value, exists := that.Map[key]; exists { result[key] = value } } that.mutex.RUnlock() return result } // getWithoutLock 内部使用,调用前需要已持有锁 func (that *SessionIns) getWithoutLock() { that.Map = that.HoTimeCache.Session(HEAD_SESSION_ADD + that.SessionId).ToMap() if that.Map != nil { return } that.Map = Map{} // 保存时也需要深拷贝 copyMap := make(Map, len(that.Map)) for k, v := range that.Map { copyMap[k] = v } that.HoTimeCache.Session(HEAD_SESSION_ADD+that.SessionId, copyMap) } func (that *SessionIns) get() { that.mutex.Lock() defer that.mutex.Unlock() that.getWithoutLock() } func (that *SessionIns) Init(cache *HoTimeCache) { that.mutex = sync.RWMutex{} that.HoTimeCache = cache }