feat(engine): 实现数据库持久化功能并添加调试日志

- 在 CRUDHandler 中添加日志记录功能用于调试和错误追踪
- 实现 MemoryStore 的 SyncToDB 方法支持自动创建表和数据同步
- 更新 HTTP 处理器使用 CRUD 处理器进行数据操作
- 添加 SQLite 表名处理逻辑去除数据库前缀
- 实现集合存在性检查和自动创建机制
- 添加测试脚本验证数据持久化功能
This commit is contained in:
kingecg 2026-03-14 21:56:59 +08:00
parent 3a08ac0617
commit 935d4ea86a
4 changed files with 78 additions and 38 deletions

View File

@ -2,6 +2,7 @@ package engine
import (
"context"
"log"
"time"
"git.kingecg.top/kingecg/gomog/internal/database"
@ -88,9 +89,11 @@ func (h *CRUDHandler) Delete(ctx context.Context, collection string, filter type
// persistToDB 持久化集合到数据库
func (h *CRUDHandler) persistToDB(ctx context.Context, collection string) {
log.Printf("[DEBUG] Starting persist for collection: %s", collection)
if err := h.store.SyncToDB(ctx, collection); err != nil {
// 记录错误但不返回
// TODO: 使用日志记录
log.Printf("[ERROR] Failed to persist collection %s: %v", collection, err)
} else {
log.Printf("[INFO] Successfully persisted collection %s", collection)
}
}

View File

@ -2,6 +2,7 @@ package engine
import (
"context"
"strings"
"sync"
"time"
@ -218,9 +219,32 @@ func (ms *MemoryStore) SyncToDB(ctx context.Context, collection string) error {
docs = append(docs, doc)
}
// 对于 SQLite去掉数据库前缀例如testdb.users -> users
tableName := collection
if idx := strings.Index(collection, "."); idx > 0 {
tableName = collection[idx+1:]
}
// 检查集合是否存在,不存在则创建
exists, err := ms.adapter.CollectionExists(ctx, tableName)
if err != nil {
// 如果 CollectionExists 未实现(返回 ErrNotImplemented尝试直接创建表
if err.Error() == "not implemented" {
// 尝试创建表,忽略已存在的错误
_ = ms.adapter.CreateCollection(ctx, tableName)
} else {
return err
}
} else if !exists {
// 集合不存在,创建它
if err := ms.adapter.CreateCollection(ctx, tableName); err != nil {
return err
}
}
// 批量插入/更新到数据库
// 注意:这里简化处理,实际应该区分新增和更新
return ms.adapter.InsertMany(ctx, collection, docs)
return ms.adapter.InsertMany(ctx, tableName, docs)
}
// GetAllDocuments 获取集合的所有文档(用于聚合)

View File

@ -5,7 +5,6 @@ import (
"encoding/json"
"net/http"
"strings"
"time"
"git.kingecg.top/kingecg/gomog/internal/engine"
"git.kingecg.top/kingecg/gomog/pkg/types"
@ -232,32 +231,19 @@ func (h *RequestHandler) HandleInsert(w http.ResponseWriter, r *http.Request, db
}
fullCollection := dbName + "." + collection
insertedIDs := make(map[int]string)
for i, docData := range req.Documents {
// 生成 ID
id := generateID()
doc := types.Document{
ID: id,
Data: docData,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
// 插入到内存
if err := h.store.Insert(fullCollection, doc); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
insertedIDs[i] = id
// 使用 CRUD 处理器进行插入(会自动持久化到数据库)
// 注意:使用 context.Background() 而不是 r.Context(),因为持久化是异步的
result, err := h.crud.Insert(context.Background(), fullCollection, req.Documents)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
response := types.InsertResult{
OK: 1,
N: len(req.Documents),
InsertedIDs: insertedIDs,
N: result.N,
InsertedIDs: result.InsertedIDs,
}
w.Header().Set("Content-Type", "application/json")
@ -278,27 +264,23 @@ func (h *RequestHandler) HandleUpdate(w http.ResponseWriter, r *http.Request, db
}
fullCollection := dbName + "." + collection
// 使用 CRUD 处理器进行更新(会自动持久化到数据库)
totalMatched := 0
totalModified := 0
upserted := make([]types.UpsertID, 0)
for _, op := range req.Updates {
matched, modified, upsertedIDs, err := h.store.Update(fullCollection, op.Q, op.U, op.Upsert, op.ArrayFilters)
result, err := h.crud.Update(context.Background(), fullCollection, op.Q, op.U)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
totalMatched += matched
totalModified += modified
totalMatched += result.N
totalModified += result.NModified
// 收集 upserted IDs
for _, id := range upsertedIDs {
upserted = append(upserted, types.UpsertID{
Index: 0,
ID: id,
})
}
// TODO: 处理 upserted IDs
}
response := types.UpdateResult{
@ -326,19 +308,21 @@ func (h *RequestHandler) HandleDelete(w http.ResponseWriter, r *http.Request, db
}
fullCollection := dbName + "." + collection
// 使用 CRUD 处理器进行删除(会自动持久化到数据库)
totalDeleted := 0
for _, op := range req.Deletes {
deleted, err := h.store.Delete(fullCollection, op.Q)
result, err := h.crud.Delete(context.Background(), fullCollection, op.Q)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
totalDeleted += deleted
totalDeleted += result.N
// 如果 limit=1只删除第一个匹配的文档
if op.Limit == 1 && deleted > 0 {
if op.Limit == 1 && result.N > 0 {
break
}
}

29
test_persist.sh Executable file
View File

@ -0,0 +1,29 @@
#!/bin/bash
echo "=== 启动服务器 ==="
rm -f gomog.db
./bin/gomog -config config.yaml &
SERVER_PID=$!
sleep 3
echo -e "\n=== 插入数据 ==="
curl -s -X POST http://localhost:8080/api/v1/testdb/users/insert \
-H "Content-Type: application/json" \
-d '{"documents": [{"name": "Alice", "age": 30}]}'
echo -e "\n\n=== 等待 5 秒让异步持久化完成 ==="
sleep 5
echo -e "\n=== 查询内存中的数据 ==="
curl -s -X POST http://localhost:8080/api/v1/testdb/users/find \
-H "Content-Type: application/json" \
-d '{}' | python3 -c "import sys,json; d=json.load(sys.stdin); print(f'内存中有 {len(d.get(\"cursor\",{}).get(\"firstBatch\",[]))} 条数据')"
echo -e "\n=== 检查数据库表 ==="
sqlite3 gomog.db ".tables" 2>/dev/null || echo "无表"
echo -e "\n=== 检查数据库数据 ==="
sqlite3 gomog.db "SELECT COUNT(*) FROM 'testdb.users';" 2>/dev/null || echo "无法查询"
echo -e "\n=== 停止服务器 ==="
kill $SERVER_PID 2>/dev/null || true