fix: 初验针对修改
This commit is contained in:
151
internal/jobs/integrity.go
Normal file
151
internal/jobs/integrity.go
Normal file
@@ -0,0 +1,151 @@
|
||||
package jobs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"git.apinb.com/bsm-sdk/core/logger"
|
||||
"git.apinb.com/ops/files/internal/config"
|
||||
"git.apinb.com/ops/files/internal/impl"
|
||||
"git.apinb.com/ops/files/internal/models"
|
||||
"git.apinb.com/ops/files/internal/storage"
|
||||
)
|
||||
|
||||
type IntegrityStatus struct {
|
||||
Running bool `json:"running"`
|
||||
LastStarted time.Time `json:"last_started,omitempty"`
|
||||
LastSuccess time.Time `json:"last_success,omitempty"`
|
||||
LastError string `json:"last_error,omitempty"`
|
||||
Checked int64 `json:"checked"`
|
||||
Missing int64 `json:"missing"`
|
||||
SizeMismatch int64 `json:"size_mismatch"`
|
||||
ETagMismatch int64 `json:"etag_mismatch"`
|
||||
}
|
||||
|
||||
var integrityRuntime struct {
|
||||
sync.RWMutex
|
||||
IntegrityStatus
|
||||
wg sync.WaitGroup
|
||||
}
|
||||
|
||||
func StartIntegrity(ctx context.Context) error {
|
||||
integrityRuntime.Lock()
|
||||
if integrityRuntime.Running {
|
||||
integrityRuntime.Unlock()
|
||||
return fmt.Errorf("文件完整性任务已启动")
|
||||
}
|
||||
integrityRuntime.Unlock()
|
||||
if err := runIntegrity(ctx); err != nil {
|
||||
return fmt.Errorf("首次核对文件完整性失败: %w", err)
|
||||
}
|
||||
integrityRuntime.Lock()
|
||||
integrityRuntime.Running = true
|
||||
integrityRuntime.Unlock()
|
||||
integrityRuntime.wg.Add(1)
|
||||
go func() {
|
||||
defer integrityRuntime.wg.Done()
|
||||
defer func() {
|
||||
integrityRuntime.Lock()
|
||||
integrityRuntime.Running = false
|
||||
integrityRuntime.Unlock()
|
||||
}()
|
||||
ticker := time.NewTicker(time.Duration(config.Spec.Cleanup.IntegrityIntervalSeconds) * time.Second)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-ticker.C:
|
||||
if err := runIntegrity(ctx); err != nil && !errors.Is(err, context.Canceled) {
|
||||
logger.Errorf("stage=files_integrity error=%v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func IntegrityStatusSnapshot() IntegrityStatus {
|
||||
integrityRuntime.RLock()
|
||||
defer integrityRuntime.RUnlock()
|
||||
return integrityRuntime.IntegrityStatus
|
||||
}
|
||||
|
||||
func waitIntegrity(ctx context.Context) error {
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
integrityRuntime.wg.Wait()
|
||||
close(done)
|
||||
}()
|
||||
select {
|
||||
case <-done:
|
||||
return nil
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
}
|
||||
}
|
||||
|
||||
func runIntegrity(ctx context.Context) error {
|
||||
startedAt := time.Now().UTC()
|
||||
status := IntegrityStatus{Running: true, LastStarted: startedAt}
|
||||
integrityRuntime.Lock()
|
||||
status.LastSuccess = integrityRuntime.LastSuccess
|
||||
integrityRuntime.IntegrityStatus = status
|
||||
integrityRuntime.Unlock()
|
||||
|
||||
var lastID uint
|
||||
for {
|
||||
var fileObjects []models.FileObject
|
||||
result := impl.DBService.WithContext(ctx).
|
||||
Where("status = ? AND id > ?", models.FileStatusReady, lastID).
|
||||
Order("id ASC").Limit(config.Spec.Cleanup.IntegrityBatchSize).Find(&fileObjects)
|
||||
if result.Error != nil {
|
||||
return finishIntegrity(status, result.Error)
|
||||
}
|
||||
if len(fileObjects) == 0 {
|
||||
break
|
||||
}
|
||||
for _, fileObject := range fileObjects {
|
||||
if err := ctx.Err(); err != nil {
|
||||
return finishIntegrity(status, err)
|
||||
}
|
||||
status.Checked++
|
||||
objectInfo, err := impl.StorageService.Stat(ctx, fileObject.ObjectKey)
|
||||
if err != nil {
|
||||
if errors.Is(err, storage.ErrObjectNotFound) {
|
||||
status.Missing++
|
||||
continue
|
||||
}
|
||||
return finishIntegrity(status, err)
|
||||
}
|
||||
if objectInfo.Size != fileObject.ActualSize {
|
||||
status.SizeMismatch++
|
||||
}
|
||||
if objectInfo.StorageETag != fileObject.StorageETag {
|
||||
status.ETagMismatch++
|
||||
}
|
||||
}
|
||||
lastID = fileObjects[len(fileObjects)-1].ID
|
||||
if len(fileObjects) < config.Spec.Cleanup.IntegrityBatchSize {
|
||||
break
|
||||
}
|
||||
}
|
||||
status.LastSuccess = time.Now().UTC()
|
||||
status.Running = integrityRuntime.Running
|
||||
integrityRuntime.Lock()
|
||||
integrityRuntime.IntegrityStatus = status
|
||||
integrityRuntime.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
func finishIntegrity(status IntegrityStatus, err error) error {
|
||||
status.LastError = err.Error()
|
||||
status.Running = integrityRuntime.Running
|
||||
integrityRuntime.Lock()
|
||||
integrityRuntime.IntegrityStatus = status
|
||||
integrityRuntime.Unlock()
|
||||
return err
|
||||
}
|
||||
Reference in New Issue
Block a user