242 lines
4.9 KiB
Go
242 lines
4.9 KiB
Go
package gocache
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"os"
|
|
"sync"
|
|
)
|
|
|
|
// KVStore represents a simple in-memory key/value store.
|
|
type KVStore struct {
|
|
mu sync.RWMutex
|
|
store map[string]string
|
|
file string
|
|
transaction *Transaction
|
|
logFile string // 添加日志文件路径
|
|
}
|
|
|
|
// Transaction represents an ongoing transaction
|
|
type Transaction struct {
|
|
store map[string]string
|
|
}
|
|
|
|
// NewKVStore creates a new instance of KVStore.
|
|
func NewKVStore(file string) *KVStore {
|
|
store := &KVStore{
|
|
store: make(map[string]string),
|
|
file: file,
|
|
logFile: file + ".log",
|
|
}
|
|
// 启动时自动恢复日志
|
|
if err := store.RecoverFromLog(); err != nil {
|
|
panic(err)
|
|
}
|
|
return store
|
|
}
|
|
|
|
// RecoverFromLog recovers data from log file
|
|
func (k *KVStore) RecoverFromLog() error {
|
|
k.mu.Lock()
|
|
defer k.mu.Unlock()
|
|
|
|
// 检查日志文件是否存在
|
|
if _, err := os.Stat(k.logFile); os.IsNotExist(err) {
|
|
return nil
|
|
}
|
|
|
|
// 读取日志文件
|
|
data, err := os.ReadFile(k.logFile)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// 逐行处理日志
|
|
lines := bytes.Split(data, []byte{'\n'})
|
|
for _, line := range lines {
|
|
if len(line) == {
|
|
continue
|
|
}
|
|
var logEntry map[string]string
|
|
if err := json.Unmarshal(line, &logEntry); err != nil {
|
|
return err
|
|
}
|
|
switch logEntry["op"] {
|
|
case "put":
|
|
k.store[logEntry["key"]] = logEntry["value"]
|
|
case "delete":
|
|
delete(k.store, logEntry["key"])
|
|
}
|
|
}
|
|
|
|
// 清空日志文件
|
|
return os.Truncate(k.logFile, 0)
|
|
}
|
|
|
|
// SaveToFile saves the current store to a file.
|
|
func (k *KVStore) SaveToFile() error {
|
|
k.mu.RLock()
|
|
defer k.mu.RUnlock()
|
|
data, err := json.Marshal(k.store)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return os.WriteFile(k.file, data, 0644)
|
|
}
|
|
|
|
// LogOperation logs a key/value operation to the log file
|
|
func (k *KVStore) LogOperation(op string, key, value string) error {
|
|
logEntry := map[string]string{
|
|
"op": op,
|
|
"key": key,
|
|
"value": value,
|
|
}
|
|
data, err := json.Marshal(logEntry)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
data = append(data, '\n')
|
|
f, err := os.OpenFile(k.logFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer f.Close()
|
|
_, err = f.Write(data)
|
|
return err
|
|
}
|
|
|
|
// LoadFromFile loads the store from a file with partial loading support
|
|
func (k *KVStore) LoadFromFile(keys ...string) error {
|
|
k.mu.Lock()
|
|
defer k.mu.Unlock()
|
|
|
|
// 先加载主数据文件
|
|
if _, err := os.Stat(k.file); err == nil {
|
|
data, err := os.ReadFile(k.file)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var tempStore map[string]string
|
|
if err := json.Unmarshal(data, &tempStore); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Partial loading
|
|
if len(keys) > 0 {
|
|
for _, key := range keys {
|
|
if value, exists := tempStore[key]; exists {
|
|
k.store[key] = value
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 然后应用日志文件中的操作
|
|
if _, err := os.Stat(k.logFile); err == nil {
|
|
data, err := os.ReadFile(k.logFile)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
lines := bytes.Split(data, []byte{'\n'})
|
|
for _, line := range lines {
|
|
if len(line) == 0 {
|
|
continue
|
|
}
|
|
var logEntry map[string]string
|
|
if err := json.Unmarshal(line, &logEntry); err != nil {
|
|
return err
|
|
}
|
|
switch logEntry["op"] {
|
|
case "put":
|
|
k.store[logEntry["key"]] = logEntry["value"]
|
|
case "delete":
|
|
delete(k.store, logEntry["key"])
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Put adds a key/value pair to the store.
|
|
func (k *KVStore) Put(key, value string) {
|
|
k.mu.Lock()
|
|
defer k.mu.Unlock()
|
|
k.store[key] = value
|
|
go func() {
|
|
k.mu.Lock()
|
|
defer k.mu.Unlock()
|
|
k.LogOperation("put", key, value) // 异步记录操作日志
|
|
}()
|
|
}
|
|
|
|
// Get retrieves the value associated with the given key.
|
|
func (k *KVStore) Get(key string) (string, bool) {
|
|
k.mu.RLock()
|
|
defer k.mu.RUnlock()
|
|
if k.transaction != nil {
|
|
value, exists := k.transaction.store[key]
|
|
if exists {
|
|
return value, true
|
|
}
|
|
}
|
|
value, exists := k.store[key]
|
|
return value, exists
|
|
}
|
|
|
|
// Delete removes the key/value pair associated with the given key.
|
|
func (k *KVStore) Delete(key string) {
|
|
k.mu.Lock()
|
|
defer k.mu.Unlock()
|
|
delete(k.store, key)
|
|
go func() {
|
|
k.mu.Lock()
|
|
defer k.mu.Unlock()
|
|
k.LogOperation("delete", key, "") // 异步记录操作日志
|
|
}()
|
|
}
|
|
|
|
// BeginTransaction starts a new transaction
|
|
func (k *KVStore) BeginTransaction() {
|
|
k.mu.Lock()
|
|
defer k.mu.Unlock()
|
|
k.transaction = &Transaction{
|
|
store: make(map[string]string),
|
|
}
|
|
}
|
|
|
|
// Commit commits the current transaction
|
|
func (k *KVStore) Commit() error {
|
|
k.mu.Lock()
|
|
defer k.mu.Unlock()
|
|
if k.transaction == nil {
|
|
return nil
|
|
}
|
|
|
|
for key, value := range k.transaction.store {
|
|
k.store[key] = value
|
|
}
|
|
k.transaction = nil
|
|
return nil
|
|
}
|
|
|
|
// Rollback rolls back the current transaction
|
|
func (k *KVStore) Rollback() {
|
|
k.mu.Lock()
|
|
defer k.mu.Unlock()
|
|
k.transaction = nil
|
|
}
|
|
|
|
// PutInTransaction puts a key/value pair in the current transaction
|
|
func (k *KVStore) PutInTransaction(key, value string) {
|
|
k.mu.Lock()
|
|
defer k.mu.Unlock()
|
|
if k.transaction != nil {
|
|
k.transaction.store[key] = value
|
|
} else {
|
|
k.store[key] = value
|
|
}
|
|
}
|