122 lines
2.7 KiB
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()
|
|
}
|