complete task 17: add tidy mode
This commit is contained in:
parent
b70a38e4b1
commit
dfa34016e5
|
@ -2,7 +2,6 @@ package document
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
@ -20,19 +19,15 @@ type DocumentStore struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDocumentStore 创建新的文档存储实例
|
// NewDocumentStore 创建新的文档存储实例
|
||||||
func NewDocumentStore(path string) (*DocumentStore, error) {
|
func NewDocumentStore(storeName string) (*DocumentStore, error) {
|
||||||
// 确保目录存在
|
sm := storage.GetInstance()
|
||||||
if err := os.MkdirAll(path, 0755); err != nil {
|
storage, err := sm.GetStorage(storeName)
|
||||||
return nil, fmt.Errorf("failed to create storage directory: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
storage, err := storage.NewLevelDBStorage(path)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to create LevelDB storage: %v", err)
|
return nil, fmt.Errorf("failed to create LevelDB storage: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建关联的索引存储
|
// 创建关联的索引存储
|
||||||
is, err := index.NewIndexStore(path)
|
is, err := index.NewIndexStore(storeName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
storage.Close()
|
storage.Close()
|
||||||
return nil, fmt.Errorf("failed to create index store: %v", err)
|
return nil, fmt.Errorf("failed to create index store: %v", err)
|
||||||
|
|
|
@ -3,8 +3,12 @@ package document
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"git.pyer.club/kingecg/godocdb/storage"
|
||||||
|
"git.pyer.club/kingecg/godocdb/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 测试用结构体
|
// 测试用结构体
|
||||||
|
@ -14,13 +18,20 @@ type TestDoc struct {
|
||||||
Age int `bson:"age"`
|
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) {
|
func TestDocumentStore(t *testing.T) {
|
||||||
// 测试目录
|
// 测试目录
|
||||||
dir := "./testdb"
|
|
||||||
defer os.RemoveAll(dir)
|
|
||||||
|
|
||||||
// 初始化文档存储
|
// 初始化文档存储
|
||||||
ds, err := NewDocumentStore(dir)
|
ds, err := NewDocumentStore("test")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create document store: %v", err)
|
t.Fatalf("Failed to create document store: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -57,11 +68,8 @@ func TestDocumentStore(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestErrorHandling(t *testing.T) {
|
func TestErrorHandling(t *testing.T) {
|
||||||
// 测试无效文档(包含不支持的类型)
|
|
||||||
dir := "./testdb_error"
|
|
||||||
defer os.RemoveAll(dir)
|
|
||||||
|
|
||||||
ds, _ := NewDocumentStore(dir)
|
ds, _ := NewDocumentStore("test_error")
|
||||||
|
|
||||||
// 测试存储非map文档
|
// 测试存储非map文档
|
||||||
invalidDoc := struct {
|
invalidDoc := struct {
|
||||||
|
@ -80,8 +88,7 @@ func TestErrorHandling(t *testing.T) {
|
||||||
|
|
||||||
func TestConcurrentAccess(t *testing.T) {
|
func TestConcurrentAccess(t *testing.T) {
|
||||||
// 测试并发写入同一ID
|
// 测试并发写入同一ID
|
||||||
dir := "./testdb_concurrent"
|
dir := "testdb_concurrent"
|
||||||
defer os.RemoveAll(dir)
|
|
||||||
|
|
||||||
numGoroutines := 10
|
numGoroutines := 10
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
|
@ -91,7 +98,7 @@ func TestConcurrentAccess(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create document store: %v", err)
|
t.Fatalf("Failed to create document store: %v", err)
|
||||||
}
|
}
|
||||||
defer ds.storage.Close()
|
// defer ds.storage.Close()
|
||||||
|
|
||||||
wg.Add(numGoroutines)
|
wg.Add(numGoroutines)
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,6 @@ package index
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
@ -44,20 +43,14 @@ type IndexStore struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewIndexStore 创建新的索引存储实例
|
// NewIndexStore 创建新的索引存储实例
|
||||||
func NewIndexStore(path string) (*IndexStore, error) {
|
func NewIndexStore(storeName string) (*IndexStore, error) {
|
||||||
// 确保目录存在
|
// 确保目录存在
|
||||||
if strings.HasPrefix(path, "/") {
|
if !strings.HasSuffix(storeName, "_index") {
|
||||||
path = strings.TrimSuffix(path, "/")
|
|
||||||
}
|
|
||||||
if !strings.HasSuffix(path, "_index") {
|
|
||||||
// add _index suffix to avoid name confilict with document store, do remove this
|
// add _index suffix to avoid name confilict with document store, do remove this
|
||||||
path += "_index"
|
storeName += "_index"
|
||||||
}
|
|
||||||
if err := os.MkdirAll(path, 0755); err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to create storage directory: %v", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
storage, err := storage.NewLevelDBStorage(path)
|
storage, err := storage.GetInstance().GetStorage(storeName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to create LevelDB storage: %v", err)
|
return nil, fmt.Errorf("failed to create LevelDB storage: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 ""
|
||||||
|
}
|
Loading…
Reference in New Issue