hotime/example/batch_cache_tester.go
hoteas 3d83c41905 feat(cache): 增加批量操作支持以提升性能
- 在 HoTimeCache 中新增 SessionsGet、SessionsSet 和 SessionsDelete 方法,支持批量获取、设置和删除 Session 缓存
- 优化缓存逻辑,减少数据库写入次数,提升性能
- 更新文档,详细说明批量操作的使用方法和性能对比
- 添加调试日志记录,便于追踪批量操作的执行情况
2026-01-30 17:51:43 +08:00

488 lines
15 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package main
import (
"encoding/json"
"fmt"
"os"
"time"
"code.hoteas.com/golang/hotime"
"code.hoteas.com/golang/hotime/cache"
. "code.hoteas.com/golang/hotime/common"
)
const debugLogPath = `d:\work\hotimev1.5\.cursor\debug.log`
// debugLog 写入调试日志
func debugLog(hypothesisId, location, message string, data map[string]interface{}) {
logFile, _ := os.OpenFile(debugLogPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if logFile != nil {
logEntry, _ := json.Marshal(map[string]interface{}{
"sessionId": "batch-cache-test",
"runId": "test-run",
"hypothesisId": hypothesisId,
"location": location,
"message": message,
"data": data,
"timestamp": time.Now().UnixMilli(),
})
logFile.Write(append(logEntry, '\n'))
logFile.Close()
}
}
// TestBatchCacheOperations 测试所有批量缓存操作
func TestBatchCacheOperations(app *hotime.Application) {
fmt.Println("\n========== 批量缓存操作测试开始 ==========")
// 测试1: 测试 CacheMemory 批量操作
fmt.Println("\n--- 测试1: CacheMemory 批量操作 ---")
testCacheMemoryBatch(app)
// 测试2: 测试 CacheDb 批量操作
fmt.Println("\n--- 测试2: CacheDb 批量操作 ---")
testCacheDbBatch(app)
// 测试3: 测试 HoTimeCache 三级缓存批量操作
fmt.Println("\n--- 测试3: HoTimeCache 三级缓存批量操作 ---")
testHoTimeCacheBatch(app)
// 测试4: 测试 SessionIns 批量操作
fmt.Println("\n--- 测试4: SessionIns 批量操作 ---")
testSessionInsBatch(app)
// 测试5: 测试缓存反哺机制
fmt.Println("\n--- 测试5: 缓存反哺机制测试 ---")
testCacheBackfill(app)
// 测试6: 测试批量操作效率(一次性写入验证)
fmt.Println("\n--- 测试6: 批量操作效率测试 ---")
testBatchEfficiency(app)
fmt.Println("\n========== 批量缓存操作测试完成 ==========")
}
// testCacheMemoryBatch 测试内存缓存批量操作
func testCacheMemoryBatch(app *hotime.Application) {
// #region agent log
debugLog("E", "batch_cache_test.go:testCacheMemoryBatch:start", "开始测试CacheMemory批量操作", nil)
// #endregion
memCache := &cache.CacheMemory{TimeOut: 3600, DbSet: true, SessionSet: true}
memCache.SetError(&Error{})
// 测试 CachesSet
testData := Map{
"mem_key1": "value1",
"mem_key2": "value2",
"mem_key3": "value3",
}
memCache.CachesSet(testData)
// #region agent log
debugLog("E", "batch_cache_test.go:testCacheMemoryBatch:afterSet", "CacheMemory.CachesSet完成", map[string]interface{}{"count": len(testData)})
// #endregion
// 测试 CachesGet
keys := []string{"mem_key1", "mem_key2", "mem_key3", "mem_key_not_exist"}
result := memCache.CachesGet(keys)
// #region agent log
debugLog("E", "batch_cache_test.go:testCacheMemoryBatch:afterGet", "CacheMemory.CachesGet完成", map[string]interface{}{
"requested_keys": keys,
"result_count": len(result),
"result_keys": getMapKeys(result),
})
// #endregion
if len(result) != 3 {
fmt.Printf(" [FAIL] CacheMemory.CachesGet: 期望3个结果实际%d个\n", len(result))
} else {
fmt.Println(" [PASS] CacheMemory.CachesGet: 批量获取正确")
}
// 测试 CachesDelete
memCache.CachesDelete([]string{"mem_key1", "mem_key2"})
result2 := memCache.CachesGet(keys)
// #region agent log
debugLog("E", "batch_cache_test.go:testCacheMemoryBatch:afterDelete", "CacheMemory.CachesDelete完成", map[string]interface{}{
"deleted_keys": []string{"mem_key1", "mem_key2"},
"remaining_count": len(result2),
"remaining_keys": getMapKeys(result2),
})
// #endregion
if len(result2) != 1 || result2["mem_key3"] == nil {
fmt.Printf(" [FAIL] CacheMemory.CachesDelete: 删除后期望1个结果实际%d个\n", len(result2))
} else {
fmt.Println(" [PASS] CacheMemory.CachesDelete: 批量删除正确")
}
}
// testCacheDbBatch 测试数据库缓存批量操作
func testCacheDbBatch(app *hotime.Application) {
// #region agent log
debugLog("E", "batch_cache_test.go:testCacheDbBatch:start", "开始测试CacheDb批量操作", nil)
// #endregion
// 使用应用的数据库连接
dbCache := &cache.CacheDb{
TimeOut: 3600,
DbSet: true,
SessionSet: true,
Mode: cache.CacheModeNew,
Db: &app.Db,
}
dbCache.SetError(&Error{})
// 清理测试数据
dbCache.CachesDelete([]string{"db_batch_key1", "db_batch_key2", "db_batch_key3"})
// 测试 CachesSet
testData := Map{
"db_batch_key1": "db_value1",
"db_batch_key2": "db_value2",
"db_batch_key3": "db_value3",
}
dbCache.CachesSet(testData)
// #region agent log
debugLog("E", "batch_cache_test.go:testCacheDbBatch:afterSet", "CacheDb.CachesSet完成", map[string]interface{}{"count": len(testData)})
// #endregion
// 测试 CachesGet
keys := []string{"db_batch_key1", "db_batch_key2", "db_batch_key3", "db_not_exist"}
result := dbCache.CachesGet(keys)
// #region agent log
debugLog("E", "batch_cache_test.go:testCacheDbBatch:afterGet", "CacheDb.CachesGet完成", map[string]interface{}{
"requested_keys": keys,
"result_count": len(result),
"result_keys": getMapKeys(result),
"result_values": result,
})
// #endregion
if len(result) != 3 {
fmt.Printf(" [FAIL] CacheDb.CachesGet: 期望3个结果实际%d个\n", len(result))
} else {
fmt.Println(" [PASS] CacheDb.CachesGet: 批量获取正确")
}
// 验证值正确性
if result["db_batch_key1"] != "db_value1" {
fmt.Printf(" [FAIL] CacheDb.CachesGet: db_batch_key1 值不正确,期望 db_value1实际 %v\n", result["db_batch_key1"])
} else {
fmt.Println(" [PASS] CacheDb.CachesGet: 值内容正确")
}
// 测试 CachesDelete
dbCache.CachesDelete([]string{"db_batch_key1", "db_batch_key2"})
result2 := dbCache.CachesGet(keys)
// #region agent log
debugLog("E", "batch_cache_test.go:testCacheDbBatch:afterDelete", "CacheDb.CachesDelete完成", map[string]interface{}{
"deleted_keys": []string{"db_batch_key1", "db_batch_key2"},
"remaining_count": len(result2),
"remaining_keys": getMapKeys(result2),
})
// #endregion
if len(result2) != 1 {
fmt.Printf(" [FAIL] CacheDb.CachesDelete: 删除后期望1个结果实际%d个\n", len(result2))
} else {
fmt.Println(" [PASS] CacheDb.CachesDelete: 批量删除正确")
}
// 清理
dbCache.CachesDelete([]string{"db_batch_key3"})
}
// testHoTimeCacheBatch 测试 HoTimeCache 三级缓存批量操作
func testHoTimeCacheBatch(app *hotime.Application) {
// #region agent log
debugLog("D", "batch_cache_test.go:testHoTimeCacheBatch:start", "开始测试HoTimeCache三级缓存批量操作", nil)
// #endregion
htCache := app.HoTimeCache
// 清理测试数据
htCache.SessionsDelete([]string{
hotime.HEAD_SESSION_ADD + "ht_batch_key1",
hotime.HEAD_SESSION_ADD + "ht_batch_key2",
hotime.HEAD_SESSION_ADD + "ht_batch_key3",
})
// 测试 SessionsSet
testData := Map{
hotime.HEAD_SESSION_ADD + "ht_batch_key1": Map{"user": "test1", "role": "admin"},
hotime.HEAD_SESSION_ADD + "ht_batch_key2": Map{"user": "test2", "role": "user"},
hotime.HEAD_SESSION_ADD + "ht_batch_key3": Map{"user": "test3", "role": "guest"},
}
htCache.SessionsSet(testData)
// #region agent log
debugLog("D", "batch_cache_test.go:testHoTimeCacheBatch:afterSet", "HoTimeCache.SessionsSet完成", map[string]interface{}{"count": len(testData)})
// #endregion
// 测试 SessionsGet
keys := []string{
hotime.HEAD_SESSION_ADD + "ht_batch_key1",
hotime.HEAD_SESSION_ADD + "ht_batch_key2",
hotime.HEAD_SESSION_ADD + "ht_batch_key3",
hotime.HEAD_SESSION_ADD + "ht_not_exist",
}
result := htCache.SessionsGet(keys)
// #region agent log
debugLog("D", "batch_cache_test.go:testHoTimeCacheBatch:afterGet", "HoTimeCache.SessionsGet完成", map[string]interface{}{
"requested_keys": len(keys),
"result_count": len(result),
"result_keys": getMapKeys(result),
})
// #endregion
if len(result) != 3 {
fmt.Printf(" [FAIL] HoTimeCache.SessionsGet: 期望3个结果实际%d个\n", len(result))
} else {
fmt.Println(" [PASS] HoTimeCache.SessionsGet: 批量获取正确")
}
// 测试 SessionsDelete
htCache.SessionsDelete([]string{
hotime.HEAD_SESSION_ADD + "ht_batch_key1",
hotime.HEAD_SESSION_ADD + "ht_batch_key2",
})
result2 := htCache.SessionsGet(keys)
// #region agent log
debugLog("D", "batch_cache_test.go:testHoTimeCacheBatch:afterDelete", "HoTimeCache.SessionsDelete完成", map[string]interface{}{
"remaining_count": len(result2),
})
// #endregion
if len(result2) != 1 {
fmt.Printf(" [FAIL] HoTimeCache.SessionsDelete: 删除后期望1个结果实际%d个\n", len(result2))
} else {
fmt.Println(" [PASS] HoTimeCache.SessionsDelete: 批量删除正确")
}
// 清理
htCache.SessionsDelete([]string{hotime.HEAD_SESSION_ADD + "ht_batch_key3"})
}
// testSessionInsBatch 测试 SessionIns 批量操作
func testSessionInsBatch(app *hotime.Application) {
// #region agent log
debugLog("B", "batch_cache_test.go:testSessionInsBatch:start", "开始测试SessionIns批量操作", nil)
// #endregion
// 创建一个模拟的 SessionIns
session := &hotime.SessionIns{
SessionId: "test_batch_session_" + ObjToStr(time.Now().UnixNano()),
}
session.Init(app.HoTimeCache)
// 测试 SessionsSet
testData := Map{
"field1": "value1",
"field2": 123,
"field3": Map{"nested": "data"},
}
session.SessionsSet(testData)
// #region agent log
debugLog("B", "batch_cache_test.go:testSessionInsBatch:afterSet", "SessionIns.SessionsSet完成", map[string]interface{}{
"session_id": session.SessionId,
"count": len(testData),
})
// #endregion
// 测试 SessionsGet
result := session.SessionsGet("field1", "field2", "field3", "not_exist")
// #region agent log
debugLog("B", "batch_cache_test.go:testSessionInsBatch:afterGet", "SessionIns.SessionsGet完成", map[string]interface{}{
"result_count": len(result),
"result_keys": getMapKeys(result),
"result": result,
})
// #endregion
if len(result) != 3 {
fmt.Printf(" [FAIL] SessionIns.SessionsGet: 期望3个结果实际%d个\n", len(result))
} else {
fmt.Println(" [PASS] SessionIns.SessionsGet: 批量获取正确")
}
// 验证值类型
if result["field1"] != "value1" {
fmt.Printf(" [FAIL] SessionIns.SessionsGet: field1 值不正确\n")
} else {
fmt.Println(" [PASS] SessionIns.SessionsGet: 字符串值正确")
}
var convErr Error
if ObjToInt(result["field2"], &convErr) != 123 {
fmt.Printf(" [FAIL] SessionIns.SessionsGet: field2 值不正确\n")
} else {
fmt.Println(" [PASS] SessionIns.SessionsGet: 数值类型正确")
}
// 测试 SessionsDelete
session.SessionsDelete("field1", "field2")
result2 := session.SessionsGet("field1", "field2", "field3")
// #region agent log
debugLog("B", "batch_cache_test.go:testSessionInsBatch:afterDelete", "SessionIns.SessionsDelete完成", map[string]interface{}{
"remaining_count": len(result2),
"remaining_keys": getMapKeys(result2),
})
// #endregion
if len(result2) != 1 {
fmt.Printf(" [FAIL] SessionIns.SessionsDelete: 删除后期望1个结果实际%d个\n", len(result2))
} else {
fmt.Println(" [PASS] SessionIns.SessionsDelete: 批量删除正确")
}
}
// testCacheBackfill 测试缓存反哺机制
func testCacheBackfill(app *hotime.Application) {
// #region agent log
debugLog("D", "batch_cache_test.go:testCacheBackfill:start", "开始测试缓存反哺机制", nil)
// #endregion
htCache := app.HoTimeCache
// 直接写入数据库缓存(绕过 memory模拟只有 db 有数据的情况
dbCache := &cache.CacheDb{
TimeOut: 3600,
DbSet: true,
SessionSet: true,
Mode: cache.CacheModeNew,
Db: &app.Db,
}
dbCache.SetError(&Error{})
testKey := "backfill_test_key_" + ObjToStr(time.Now().UnixNano())
testValue := Map{"backfill": "test_data"}
// 直接写入 db
dbCache.Cache(testKey, testValue)
// #region agent log
debugLog("D", "batch_cache_test.go:testCacheBackfill:dbWritten", "数据直接写入DB", map[string]interface{}{
"key": testKey,
"value": testValue,
})
// #endregion
// 通过 HoTimeCache 批量获取,应该触发反哺到 memory
keys := []string{testKey}
result := htCache.CachesGet(keys)
// #region agent log
debugLog("D", "batch_cache_test.go:testCacheBackfill:afterGet", "HoTimeCache.CachesGet完成", map[string]interface{}{
"result_count": len(result),
"has_key": result[testKey] != nil,
})
// #endregion
if len(result) != 1 || result[testKey] == nil {
fmt.Println(" [FAIL] 缓存反哺: 从 DB 读取失败")
} else {
fmt.Println(" [PASS] 缓存反哺: 从 DB 读取成功")
}
// 清理
htCache.CachesDelete(keys)
}
// testBatchEfficiency 测试批量操作效率
func testBatchEfficiency(app *hotime.Application) {
// #region agent log
debugLog("A", "batch_cache_test.go:testBatchEfficiency:start", "开始测试批量操作效率", nil)
// #endregion
session := &hotime.SessionIns{
SessionId: "efficiency_test_" + ObjToStr(time.Now().UnixNano()),
}
session.Init(app.HoTimeCache)
// 记录批量设置开始时间
startTime := time.Now()
// 设置10个字段
testData := Map{}
for i := 0; i < 10; i++ {
testData[fmt.Sprintf("eff_field_%d", i)] = fmt.Sprintf("value_%d", i)
}
session.SessionsSet(testData)
batchDuration := time.Since(startTime)
// #region agent log
debugLog("A", "batch_cache_test.go:testBatchEfficiency:batchSet", "批量设置完成", map[string]interface{}{
"count": len(testData),
"duration_ms": batchDuration.Milliseconds(),
})
// #endregion
// 对比单个设置
session2 := &hotime.SessionIns{
SessionId: "efficiency_test_single_" + ObjToStr(time.Now().UnixNano()),
}
session2.Init(app.HoTimeCache)
startTime2 := time.Now()
for i := 0; i < 10; i++ {
session2.Session(fmt.Sprintf("single_field_%d", i), fmt.Sprintf("value_%d", i))
}
singleDuration := time.Since(startTime2)
// #region agent log
debugLog("A", "batch_cache_test.go:testBatchEfficiency:singleSet", "单个设置完成", map[string]interface{}{
"count": 10,
"duration_ms": singleDuration.Milliseconds(),
})
// #endregion
fmt.Printf(" 批量设置10个字段耗时: %v\n", batchDuration)
fmt.Printf(" 单个设置10个字段耗时: %v\n", singleDuration)
if batchDuration < singleDuration {
fmt.Println(" [PASS] 批量操作效率: 批量操作更快")
} else {
fmt.Println(" [WARN] 批量操作效率: 批量操作未体现优势(可能数据量太小)")
}
// 批量获取测试
startTime3 := time.Now()
keys := make([]string, 10)
for i := 0; i < 10; i++ {
keys[i] = fmt.Sprintf("eff_field_%d", i)
}
session.SessionsGet(keys...)
batchGetDuration := time.Since(startTime3)
// #region agent log
debugLog("A", "batch_cache_test.go:testBatchEfficiency:batchGet", "批量获取完成", map[string]interface{}{
"count": 10,
"duration_ms": batchGetDuration.Milliseconds(),
})
// #endregion
fmt.Printf(" 批量获取10个字段耗时: %v\n", batchGetDuration)
}
// getMapKeys 获取 Map 的所有键
func getMapKeys(m Map) []string {
keys := make([]string, 0, len(m))
for k := range m {
keys = append(keys, k)
}
return keys
}