kra-new/internal/data/data_scope_audit.go

122 lines
2.7 KiB
Go

package data
import (
"context"
"log/slog"
"sync"
"time"
"gorm.io/gorm"
)
const (
dataScopeAuditQueueSize = 1024
dataScopeAuditBatchSize = 100
dataScopeAuditInterval = 2 * time.Second
)
// dataScopeAuditWriter keeps row-scope auditing best-effort: callbacks only
// enqueue, while a background worker writes batches to the active database.
type dataScopeAuditWriter struct {
data *Data
logger *slog.Logger
queue chan dataAccessLogPO
batchSize int
interval time.Duration
stop chan struct{}
done chan struct{}
closeOnce sync.Once
}
func newDataScopeAuditWriter(data *Data, logger *slog.Logger) *dataScopeAuditWriter {
return newDataScopeAuditWriterWithOptions(data, logger, dataScopeAuditQueueSize, dataScopeAuditBatchSize, dataScopeAuditInterval)
}
func newDataScopeAuditWriterWithOptions(data *Data, logger *slog.Logger, queueSize, batchSize int, interval time.Duration) *dataScopeAuditWriter {
w := &dataScopeAuditWriter{
data: data,
logger: logger,
queue: make(chan dataAccessLogPO, queueSize),
batchSize: batchSize,
interval: interval,
stop: make(chan struct{}),
done: make(chan struct{}),
}
go w.run()
return w
}
func (d *Data) enqueueDataScopeAudit(record dataAccessLogPO) {
if d == nil || d.auditLog == nil {
return
}
d.auditLog.Enqueue(record)
}
func (w *dataScopeAuditWriter) Enqueue(record dataAccessLogPO) {
if w == nil {
return
}
select {
case w.queue <- record:
default:
w.log().Warn("数据权限审计缓冲已满, 事件被丢弃", "mod", "data-scope", "event_type", record.EventType, "table", record.TargetTable)
}
}
func (w *dataScopeAuditWriter) Close() {
if w == nil {
return
}
w.closeOnce.Do(func() { close(w.stop) })
<-w.done
}
func (w *dataScopeAuditWriter) run() {
defer close(w.done)
ticker := time.NewTicker(w.interval)
defer ticker.Stop()
batch := make([]dataAccessLogPO, 0, w.batchSize)
flush := func() {
if len(batch) == 0 || w.data == nil || w.data.gormDB == nil {
return
}
db := w.data.gormDB.DB()
if db == nil {
return
}
if err := db.WithContext(context.Background()).Session(&gorm.Session{NewDB: true, SkipHooks: true}).Create(&batch).Error; err != nil {
w.log().Warn("数据权限审计批量写入失败", "mod", "data-scope", "error", err)
}
batch = batch[:0]
}
for {
select {
case record := <-w.queue:
batch = append(batch, record)
if len(batch) >= w.batchSize {
flush()
}
case <-ticker.C:
flush()
case <-w.stop:
for {
select {
case record := <-w.queue:
batch = append(batch, record)
default:
flush()
return
}
}
}
}
}
func (w *dataScopeAuditWriter) log() *slog.Logger {
if w != nil && w.logger != nil {
return w.logger
}
return slog.Default()
}