feat: 优化块生成
This commit is contained in:
parent
dc927febf0
commit
4559bb2cd6
@ -4,11 +4,11 @@ import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"plp-test/internal/config"
|
||||
@ -140,8 +140,12 @@ func (t *PowerLossTest) Run(ctx context.Context) (*model.TestResult, error) {
|
||||
startTime := time.Now()
|
||||
var totalBytesWritten int
|
||||
|
||||
// 第一阶段 - 持续写入数据,直到手动断电
|
||||
t.setMessage("写入数据 (请在适当时手动断电)")
|
||||
// 第一阶段 - 在内存中预先生成所有数据块
|
||||
t.setMessage("在内存中预生成数据块")
|
||||
|
||||
// 预先在内存中生成所有数据块
|
||||
blocksInMemory := make([]*model.TestBlock, t.totalBlocks)
|
||||
checksumMap := make(map[int]string)
|
||||
|
||||
for i := 0; i < t.totalBlocks; i++ {
|
||||
select {
|
||||
@ -159,6 +163,54 @@ func (t *PowerLossTest) Run(ctx context.Context) (*model.TestResult, error) {
|
||||
// 创建测试数据块
|
||||
block := model.NewTestBlock(data, i)
|
||||
|
||||
// 存储到内存中
|
||||
blocksInMemory[i] = block
|
||||
checksumMap[i] = block.Checksum
|
||||
|
||||
// 更新进度
|
||||
if i > 0 && i%100 == 0 {
|
||||
progress := float64(i+1) / float64(t.totalBlocks) * 30 // 前30%进度用于生成
|
||||
t.setProgress(progress)
|
||||
t.setMessage(fmt.Sprintf("已在内存中生成 %d/%d 个数据块", i, t.totalBlocks))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 将校验和映射持久化到文件
|
||||
t.setMessage("持久化校验和映射到文件")
|
||||
checksumFilePath := filepath.Join(t.testDir, "checksums.json")
|
||||
checksumData, err := json.Marshal(checksumMap)
|
||||
if err != nil {
|
||||
t.setStatus(StatusFailed)
|
||||
return nil, fmt.Errorf("序列化校验和映射失败: %v", err)
|
||||
}
|
||||
|
||||
err = os.WriteFile(checksumFilePath, checksumData, 0644)
|
||||
if err != nil {
|
||||
t.setStatus(StatusFailed)
|
||||
return nil, fmt.Errorf("保存校验和映射文件失败: %v", err)
|
||||
}
|
||||
|
||||
// 确保校验和映射已落盘
|
||||
t.setMessage("同步校验和映射到磁盘")
|
||||
_, err = utils.ExecuteCommand("sync")
|
||||
if err != nil {
|
||||
t.logger.Warnf("执行sync命令失败: %v", err)
|
||||
}
|
||||
|
||||
t.setProgress(35)
|
||||
t.setMessage("校验和映射已保存,准备断电测试,开始写入数据块...")
|
||||
time.Sleep(3 * time.Second) // 给用户一些时间准备
|
||||
|
||||
// 第二阶段 - 写入数据块到磁盘
|
||||
t.setMessage("写入数据块到磁盘 (请在适当时手动断电)")
|
||||
|
||||
for i, block := range blocksInMemory {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
t.setStatus(StatusAborted)
|
||||
return nil, ctx.Err()
|
||||
default:
|
||||
// 添加到数据块列表
|
||||
t.blocksMu.Lock()
|
||||
t.blocks = append(t.blocks, block)
|
||||
@ -175,22 +227,22 @@ func (t *PowerLossTest) Run(ctx context.Context) (*model.TestResult, error) {
|
||||
|
||||
// 写入文件
|
||||
filePath := filepath.Join(t.testDir, fmt.Sprintf("block_%d.dat", i))
|
||||
// direct IO 直接写入磁盘,必须使用syscall.O_DIRECT,Linux 2.6.29 及以上版本支持
|
||||
file, err := os.OpenFile(filePath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC|syscall.O_DIRECT, 0644)
|
||||
// direct IO 直接写入磁盘,使用跨平台的DirectIOFlag
|
||||
file, err := os.OpenFile(filePath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC|DirectIOFlag, 0644)
|
||||
if err != nil {
|
||||
t.setStatus(StatusFailed)
|
||||
return nil, fmt.Errorf("写入文件 %s 失败: %v", filePath, err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
_, err = file.Write(data)
|
||||
_, err = file.Write(block.Data)
|
||||
if err != nil {
|
||||
t.setStatus(StatusFailed)
|
||||
return nil, fmt.Errorf("写入文件 %s 失败: %v", filePath, err)
|
||||
}
|
||||
|
||||
t.writtenBlocks++
|
||||
totalBytesWritten += len(data)
|
||||
totalBytesWritten += len(block.Data)
|
||||
|
||||
t.setMessage(fmt.Sprintf("同步数据到磁盘 (已写入 %d/%d 块)", i, t.totalBlocks))
|
||||
_, err = utils.ExecuteCommand("sync")
|
||||
@ -199,13 +251,12 @@ func (t *PowerLossTest) Run(ctx context.Context) (*model.TestResult, error) {
|
||||
}
|
||||
|
||||
// 更新进度
|
||||
progress := float64(i+1) / float64(t.totalBlocks) * 100
|
||||
progress := 35 + float64(i+1)/float64(t.totalBlocks)*65 // 余下65%进度用于写入
|
||||
t.setProgress(progress)
|
||||
|
||||
// 每写入一定数量的打印输出下当前进度到日志
|
||||
if i > 0 && i%100 == 0 {
|
||||
t.setMessage(fmt.Sprintf("已写入 %d/%d 块数据, 共 %.2f MB", i, t.totalBlocks, float64(i*t.blockSize)/(1024*1024)))
|
||||
// time.Sleep(1 * time.Second)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -215,7 +266,7 @@ func (t *PowerLossTest) Run(ctx context.Context) (*model.TestResult, error) {
|
||||
|
||||
// 完成所有数据写入后,同步到磁盘
|
||||
t.setMessage("同步所有数据到磁盘")
|
||||
_, err := utils.ExecuteCommand("sync")
|
||||
_, err = utils.ExecuteCommand("sync")
|
||||
if err != nil {
|
||||
t.logger.Warnf("执行sync命令失败: %v", err)
|
||||
}
|
||||
@ -248,6 +299,26 @@ func (t *PowerLossTest) CheckIntegrity() *model.IntegrityInfo {
|
||||
t.integrityInfo.CorruptedBlocks = 0
|
||||
t.integrityInfo.MissingBlocks = 0
|
||||
|
||||
// 尝试从文件加载校验和映射
|
||||
checksumMap := make(map[int]string)
|
||||
checksumFilePath := filepath.Join(t.testDir, "checksums.json")
|
||||
if utils.FileExists(checksumFilePath) {
|
||||
t.setMessage("从文件加载校验和映射")
|
||||
data, err := os.ReadFile(checksumFilePath)
|
||||
if err == nil {
|
||||
err = json.Unmarshal(data, &checksumMap)
|
||||
if err != nil {
|
||||
t.logger.Warnf("解析校验和映射文件失败: %v,将使用内存中的校验和", err)
|
||||
} else {
|
||||
t.logger.Infof("从文件成功加载了 %d 个校验和", len(checksumMap))
|
||||
}
|
||||
} else {
|
||||
t.logger.Warnf("读取校验和映射文件失败: %v,将使用内存中的校验和", err)
|
||||
}
|
||||
} else {
|
||||
t.logger.Warnf("校验和映射文件不存在,将使用内存中的校验和")
|
||||
}
|
||||
|
||||
// 为所有块创建状态记录
|
||||
for i := 0; i < t.totalBlocks; i++ {
|
||||
filePath := filepath.Join(t.testDir, fmt.Sprintf("block_%d.dat", i))
|
||||
@ -280,14 +351,20 @@ func (t *PowerLossTest) CheckIntegrity() *model.IntegrityInfo {
|
||||
hash := sha256.Sum256(data)
|
||||
checksum := hex.EncodeToString(hash[:])
|
||||
|
||||
var blockChecksum string
|
||||
t.blocksMu.RLock()
|
||||
if i < len(t.blocks) && t.blocks[i] != nil {
|
||||
blockChecksum = t.blocks[i].Checksum
|
||||
// 获取期望的校验和 - 优先使用文件中的校验和映射
|
||||
var expectedChecksum string
|
||||
if storedChecksum, ok := checksumMap[i]; ok {
|
||||
expectedChecksum = storedChecksum
|
||||
} else {
|
||||
// 回退到内存中的校验和
|
||||
t.blocksMu.RLock()
|
||||
if i < len(t.blocks) && t.blocks[i] != nil {
|
||||
expectedChecksum = t.blocks[i].Checksum
|
||||
}
|
||||
t.blocksMu.RUnlock()
|
||||
}
|
||||
t.blocksMu.RUnlock()
|
||||
|
||||
if blockChecksum != "" && checksum != blockChecksum {
|
||||
if expectedChecksum != "" && checksum != expectedChecksum {
|
||||
// 数据损坏
|
||||
t.integrityInfo.CorruptedBlocks++
|
||||
t.integrityInfo.BlocksMap[i] = model.BlockStatus{
|
||||
|
4
internal/testcase/syscall_common.go
Normal file
4
internal/testcase/syscall_common.go
Normal file
@ -0,0 +1,4 @@
|
||||
package testcase
|
||||
|
||||
// DirectIOFlag 根据当前操作系统设置的直接IO标志
|
||||
var DirectIOFlag int
|
12
internal/testcase/syscall_darwin.go
Normal file
12
internal/testcase/syscall_darwin.go
Normal file
@ -0,0 +1,12 @@
|
||||
//go:build darwin
|
||||
// +build darwin
|
||||
|
||||
package testcase
|
||||
|
||||
// DarwinOpenFileFlag MacOS不支持O_DIRECT,使用0代替
|
||||
const DarwinOpenFileFlag = 0
|
||||
|
||||
func init() {
|
||||
// MacOS上设置为0(不支持O_DIRECT)
|
||||
DirectIOFlag = 0
|
||||
}
|
14
internal/testcase/syscall_linux.go
Normal file
14
internal/testcase/syscall_linux.go
Normal file
@ -0,0 +1,14 @@
|
||||
//go:build linux
|
||||
// +build linux
|
||||
|
||||
package testcase
|
||||
|
||||
import "syscall"
|
||||
|
||||
// LinuxOpenFileFlag 定义Linux特有的O_DIRECT标志
|
||||
const LinuxOpenFileFlag = syscall.O_DIRECT
|
||||
|
||||
func init() {
|
||||
// Linux上设置O_DIRECT标志
|
||||
DirectIOFlag = syscall.O_DIRECT
|
||||
}
|
Loading…
Reference in New Issue
Block a user