148 lines
3.8 KiB
Go
148 lines
3.8 KiB
Go
package linedb
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"time"
|
|
)
|
|
|
|
// Transaction представляет транзакцию
|
|
type Transaction struct {
|
|
transactionMode string
|
|
transactionID string
|
|
timeoutMs int
|
|
timeoutID *time.Timer
|
|
rollback bool
|
|
backupFile string
|
|
doNotDeleteBackupFile bool
|
|
// mutex sync.RWMutex
|
|
active bool
|
|
}
|
|
|
|
// NewTransaction создает новую транзакцию
|
|
func NewTransaction(mode string, id string, timeout int, rollback bool) *Transaction {
|
|
tx := &Transaction{
|
|
transactionMode: mode,
|
|
transactionID: id,
|
|
timeoutMs: timeout,
|
|
rollback: rollback,
|
|
active: true,
|
|
}
|
|
|
|
if timeout > 0 {
|
|
tx.timeoutID = time.AfterFunc(time.Duration(timeout)*time.Millisecond, func() {
|
|
tx.active = false
|
|
})
|
|
}
|
|
|
|
return tx
|
|
}
|
|
|
|
// ClearTimeout очищает таймаут транзакции
|
|
func (t *Transaction) ClearTimeout() {
|
|
if t.timeoutID != nil {
|
|
t.timeoutID.Stop()
|
|
}
|
|
}
|
|
|
|
// IsActive проверяет, является ли транзакция активной
|
|
func (t *Transaction) IsActive() bool {
|
|
return t.active
|
|
}
|
|
|
|
// IsReadMode проверяет, является ли транзакция режимом чтения
|
|
func (t *Transaction) IsReadMode() bool {
|
|
return t.transactionMode == "read"
|
|
}
|
|
|
|
// IsWriteMode проверяет, является ли транзакция режимом записи
|
|
func (t *Transaction) IsWriteMode() bool {
|
|
return t.transactionMode == "write"
|
|
}
|
|
|
|
// ShouldRollback проверяет, требуется ли откат транзакции при ошибке
|
|
func (t *Transaction) ShouldRollback() bool {
|
|
return t.rollback
|
|
}
|
|
|
|
// ShouldKeepBackup проверяет, нужно ли сохранять резервную копию
|
|
func (t *Transaction) ShouldKeepBackup() bool {
|
|
return !t.doNotDeleteBackupFile
|
|
}
|
|
|
|
// GetBackupFile получает путь к файлу резервной копии
|
|
func (t *Transaction) GetBackupFile() string {
|
|
return t.backupFile
|
|
}
|
|
|
|
// SetBackupFile устанавливает путь к файлу резервной копии
|
|
func (t *Transaction) SetBackupFile(path string) {
|
|
t.backupFile = path
|
|
}
|
|
|
|
// CreateBackup создает резервную копию файла
|
|
func (t *Transaction) CreateBackup(filename string) error {
|
|
if filename == "" {
|
|
return fmt.Errorf("filename is required")
|
|
}
|
|
|
|
// Создаем резервную копию
|
|
backupFile := filename + ".backup"
|
|
|
|
// Копируем файл
|
|
src, err := os.Open(filename)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to open source file: %w", err)
|
|
}
|
|
defer src.Close()
|
|
|
|
dst, err := os.Create(backupFile)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create backup file: %w", err)
|
|
}
|
|
defer dst.Close()
|
|
|
|
_, err = dst.ReadFrom(src)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to copy file: %w", err)
|
|
}
|
|
|
|
t.backupFile = backupFile
|
|
return nil
|
|
}
|
|
|
|
// RestoreFromBackup восстанавливает из резервной копии
|
|
func (t *Transaction) RestoreFromBackup(filename string) error {
|
|
if t.backupFile == "" {
|
|
return fmt.Errorf("no backup file available")
|
|
}
|
|
|
|
// Копируем резервную копию обратно
|
|
src, err := os.Open(t.backupFile)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to open backup file: %w", err)
|
|
}
|
|
defer src.Close()
|
|
|
|
dst, err := os.Create(filename)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create target file: %w", err)
|
|
}
|
|
defer dst.Close()
|
|
|
|
_, err = dst.ReadFrom(src)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to copy backup: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// CleanupBackup удаляет резервную копию
|
|
func (t *Transaction) CleanupBackup() error {
|
|
if t.backupFile != "" && !t.doNotDeleteBackupFile {
|
|
return os.Remove(t.backupFile)
|
|
}
|
|
return nil
|
|
}
|