From dfa34016e5559e5eaf08ce4edd20c5b09b15f5bb Mon Sep 17 00:00:00 2001 From: kingecg Date: Sun, 8 Jun 2025 10:31:34 +0800 Subject: [PATCH] complete task 17: add tidy mode --- document/document.go | 13 ++----- document/document_test.go | 27 ++++++++----- index/index.go | 15 ++------ storage/storage.go | 79 +++++++++++++++++++++++++++++++++++++++ utils/utils.go | 35 +++++++++++++++++ 5 files changed, 139 insertions(+), 30 deletions(-) create mode 100644 storage/storage.go create mode 100644 utils/utils.go diff --git a/document/document.go b/document/document.go index f7f4544..bf63d97 100644 --- a/document/document.go +++ b/document/document.go @@ -2,7 +2,6 @@ package document import ( "fmt" - "os" "strings" "sync" @@ -20,19 +19,15 @@ type DocumentStore struct { } // NewDocumentStore 创建新的文档存储实例 -func NewDocumentStore(path string) (*DocumentStore, error) { - // 确保目录存在 - if err := os.MkdirAll(path, 0755); err != nil { - return nil, fmt.Errorf("failed to create storage directory: %v", err) - } - - storage, err := storage.NewLevelDBStorage(path) +func NewDocumentStore(storeName string) (*DocumentStore, error) { + sm := storage.GetInstance() + storage, err := sm.GetStorage(storeName) if err != nil { return nil, fmt.Errorf("failed to create LevelDB storage: %v", err) } // 创建关联的索引存储 - is, err := index.NewIndexStore(path) + is, err := index.NewIndexStore(storeName) if err != nil { storage.Close() return nil, fmt.Errorf("failed to create index store: %v", err) diff --git a/document/document_test.go b/document/document_test.go index 10f79c4..fee3210 100644 --- a/document/document_test.go +++ b/document/document_test.go @@ -3,8 +3,12 @@ package document import ( "fmt" "os" + "path/filepath" "sync" "testing" + + "git.pyer.club/kingecg/godocdb/storage" + "git.pyer.club/kingecg/godocdb/utils" ) // 测试用结构体 @@ -14,13 +18,20 @@ type TestDoc struct { Age int `bson:"age"` } +func TestMain(m *testing.M) { + projectRoot := utils.GetProjectRootDir() + dir := filepath.Join(projectRoot, "test_data") + defer os.RemoveAll(dir) + storage.NewStorageManager(dir, 0) + m.Run() + storage.GetInstance().Close() +} + func TestDocumentStore(t *testing.T) { // 测试目录 - dir := "./testdb" - defer os.RemoveAll(dir) // 初始化文档存储 - ds, err := NewDocumentStore(dir) + ds, err := NewDocumentStore("test") if err != nil { t.Fatalf("Failed to create document store: %v", err) } @@ -57,11 +68,8 @@ func TestDocumentStore(t *testing.T) { } func TestErrorHandling(t *testing.T) { - // 测试无效文档(包含不支持的类型) - dir := "./testdb_error" - defer os.RemoveAll(dir) - ds, _ := NewDocumentStore(dir) + ds, _ := NewDocumentStore("test_error") // 测试存储非map文档 invalidDoc := struct { @@ -80,8 +88,7 @@ func TestErrorHandling(t *testing.T) { func TestConcurrentAccess(t *testing.T) { // 测试并发写入同一ID - dir := "./testdb_concurrent" - defer os.RemoveAll(dir) + dir := "testdb_concurrent" numGoroutines := 10 var wg sync.WaitGroup @@ -91,7 +98,7 @@ func TestConcurrentAccess(t *testing.T) { if err != nil { t.Fatalf("Failed to create document store: %v", err) } - defer ds.storage.Close() + // defer ds.storage.Close() wg.Add(numGoroutines) diff --git a/index/index.go b/index/index.go index 02a5b8b..60a0119 100644 --- a/index/index.go +++ b/index/index.go @@ -3,7 +3,6 @@ package index import ( "bytes" "fmt" - "os" "strings" "sync" @@ -44,20 +43,14 @@ type IndexStore struct { } // NewIndexStore 创建新的索引存储实例 -func NewIndexStore(path string) (*IndexStore, error) { +func NewIndexStore(storeName string) (*IndexStore, error) { // 确保目录存在 - if strings.HasPrefix(path, "/") { - path = strings.TrimSuffix(path, "/") - } - if !strings.HasSuffix(path, "_index") { + if !strings.HasSuffix(storeName, "_index") { // add _index suffix to avoid name confilict with document store, do remove this - path += "_index" - } - if err := os.MkdirAll(path, 0755); err != nil { - return nil, fmt.Errorf("failed to create storage directory: %v", err) + storeName += "_index" } - storage, err := storage.NewLevelDBStorage(path) + storage, err := storage.GetInstance().GetStorage(storeName) if err != nil { return nil, fmt.Errorf("failed to create LevelDB storage: %v", err) } diff --git a/storage/storage.go b/storage/storage.go new file mode 100644 index 0000000..3e7df24 --- /dev/null +++ b/storage/storage.go @@ -0,0 +1,79 @@ +package storage + +import ( + "fmt" + "sync" +) + +type DbPolicy int + +const ( + DbPolicy_Tidy DbPolicy = iota // all store in one db + DbPolicy_Loosy // store in different db +) + +const ( + DefaultDbName = "default" +) + +type StorageManager struct { + policy DbPolicy + dbrootDir string + db map[string]*LevelDBStorage +} + +var ( + instance *StorageManager + once sync.Once +) + +// 修改工厂函数实现单例模式 +func NewStorageManager(dbrootDir string, dbPolicy int) (*StorageManager, error) { + var initErr error + once.Do(func() { + instance = &StorageManager{ + policy: DbPolicy(dbPolicy), + dbrootDir: dbrootDir, + db: make(map[string]*LevelDBStorage), + } + + // 预初始化默认数据库 + if _, err := instance.GetStorage(DefaultDbName); err != nil { + initErr = err + } + }) + + if initErr != nil { + return nil, initErr + } + return instance, nil +} + +// 新增获取单例实例的方法 +func GetInstance() *StorageManager { + return instance +} + +func (sm *StorageManager) GetStorage(dbName string) (*LevelDBStorage, error) { + if dbName == "" { + return nil, fmt.Errorf("db name is empty") + } + if sm.policy == DbPolicy_Tidy { + dbName = DefaultDbName + } + _, exists := sm.db[dbName] + if !exists { + db, err := NewLevelDBStorage(sm.dbrootDir + "/" + dbName) + if err != nil { + return nil, err + } + sm.db[dbName] = db + } + return sm.db[dbName], nil +} + +func (sm *StorageManager) Close() { + for _, db := range sm.db { + db.Close() + } +} diff --git a/utils/utils.go b/utils/utils.go new file mode 100644 index 0000000..3234bba --- /dev/null +++ b/utils/utils.go @@ -0,0 +1,35 @@ +package utils + +import ( + "os" + "path/filepath" +) + +func GetRootDir() string { + dir, err := filepath.Abs(filepath.Dir(os.Args[0])) + + if err != nil { + panic(err) + } + rootDir := filepath.VolumeName(dir) + if rootDir == "" { + return "/" + } + return rootDir +} + +func GetProjectRootDir() string { + dir, err := filepath.Abs(filepath.Dir(os.Args[0])) + if err != nil { + panic(err) + } + rootDir := GetRootDir() + for dir != rootDir { + //check go.mod file under dir + if _, err := os.Stat(filepath.Join(dir, "go.mod")); err == nil { + return dir + } + dir = filepath.Dir(dir) + } + return "" +}