mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-09-29 14:06:36 +08:00
b6c4181c70
- Return AccessLogView.id as string to avoid JS Number precision loss - Store OpenFlareAccessLog IDs as uint64 with json id,string - Update frontend AccessLogItem.id type to string
509 lines
15 KiB
Go
509 lines
15 KiB
Go
// Copyright 2026 Arctel.net
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package model
|
|
|
|
import (
|
|
"context"
|
|
"sort"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/Rain-kl/Wavelet/internal/db/idgen"
|
|
)
|
|
|
|
type memoryObservabilityStore struct {
|
|
mu sync.RWMutex
|
|
metricSnapshots []*OpenFlareMetricSnapshot
|
|
requestReports []*OpenFlareRequestReport
|
|
openrestyObs []*OpenFlareNodeObservationOpenresty
|
|
frpsObs []*OpenFlareNodeObservationFrps
|
|
frpcObs []*OpenFlareNodeObservationFrpc
|
|
}
|
|
|
|
func (s *memoryObservabilityStore) InsertMetricSnapshot(_ context.Context, record *OpenFlareMetricSnapshot) error {
|
|
if record == nil {
|
|
return nil
|
|
}
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
copyRecord := cloneOpenFlareMetricSnapshot(record)
|
|
if memoryMetricSnapshotExists(s.metricSnapshots, copyRecord.NodeID, copyRecord.CapturedAt) {
|
|
return nil
|
|
}
|
|
s.metricSnapshots = append(s.metricSnapshots, copyRecord)
|
|
return nil
|
|
}
|
|
|
|
func (s *memoryObservabilityStore) ListMetricSnapshots(_ context.Context, nodeID string, since time.Time, limit int) ([]*OpenFlareMetricSnapshot, error) {
|
|
s.mu.RLock()
|
|
defer s.mu.RUnlock()
|
|
rows := memoryFilterMetricSnapshots(s.metricSnapshots, nodeID, since)
|
|
sortOpenFlareMetricSnapshots(rows)
|
|
return memoryLimitObservabilityRows(rows, limit), nil
|
|
}
|
|
|
|
func (s *memoryObservabilityStore) DeleteAllMetricSnapshots(_ context.Context) (int64, error) {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
count := int64(len(s.metricSnapshots))
|
|
s.metricSnapshots = nil
|
|
return count, nil
|
|
}
|
|
|
|
func (s *memoryObservabilityStore) DeleteMetricSnapshotsBefore(_ context.Context, cutoff time.Time) (int64, error) {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
cutoff = cutoff.UTC()
|
|
remaining := make([]*OpenFlareMetricSnapshot, 0, len(s.metricSnapshots))
|
|
var deleted int64
|
|
for _, row := range s.metricSnapshots {
|
|
if row.CapturedAt.Before(cutoff) {
|
|
deleted++
|
|
continue
|
|
}
|
|
remaining = append(remaining, row)
|
|
}
|
|
s.metricSnapshots = remaining
|
|
return deleted, nil
|
|
}
|
|
|
|
func (s *memoryObservabilityStore) InsertRequestReport(_ context.Context, record *OpenFlareRequestReport) error {
|
|
if record == nil {
|
|
return nil
|
|
}
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
copyRecord := cloneOpenFlareRequestReport(record)
|
|
if memoryRequestReportExists(s.requestReports, copyRecord.NodeID, copyRecord.WindowStartedAt, copyRecord.WindowEndedAt) {
|
|
return nil
|
|
}
|
|
s.requestReports = append(s.requestReports, copyRecord)
|
|
return nil
|
|
}
|
|
|
|
func (s *memoryObservabilityStore) ListRequestReports(_ context.Context, nodeID string, since time.Time, limit int) ([]*OpenFlareRequestReport, error) {
|
|
s.mu.RLock()
|
|
defer s.mu.RUnlock()
|
|
rows := memoryFilterRequestReports(s.requestReports, nodeID, since)
|
|
sortOpenFlareRequestReports(rows)
|
|
return memoryLimitObservabilityRows(rows, limit), nil
|
|
}
|
|
|
|
func (s *memoryObservabilityStore) DeleteAllRequestReports(_ context.Context) (int64, error) {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
count := int64(len(s.requestReports))
|
|
s.requestReports = nil
|
|
return count, nil
|
|
}
|
|
|
|
func (s *memoryObservabilityStore) DeleteRequestReportsBefore(_ context.Context, cutoff time.Time) (int64, error) {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
cutoff = cutoff.UTC()
|
|
remaining := make([]*OpenFlareRequestReport, 0, len(s.requestReports))
|
|
var deleted int64
|
|
for _, row := range s.requestReports {
|
|
if row.WindowEndedAt.Before(cutoff) {
|
|
deleted++
|
|
continue
|
|
}
|
|
remaining = append(remaining, row)
|
|
}
|
|
s.requestReports = remaining
|
|
return deleted, nil
|
|
}
|
|
|
|
func (s *memoryObservabilityStore) InsertNodeObservationOpenresty(_ context.Context, record *OpenFlareNodeObservationOpenresty) error {
|
|
if record == nil {
|
|
return nil
|
|
}
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
s.openrestyObs = append(s.openrestyObs, cloneOpenFlareNodeObservationOpenresty(record))
|
|
return nil
|
|
}
|
|
|
|
func (s *memoryObservabilityStore) ListNodeObservationOpenresty(_ context.Context, nodeID string, since time.Time, limit int) ([]*OpenFlareNodeObservationOpenresty, error) {
|
|
s.mu.RLock()
|
|
defer s.mu.RUnlock()
|
|
rows := memoryFilterOpenrestyObservations(s.openrestyObs, nodeID, since)
|
|
sortOpenFlareNodeObservationOpenresty(rows)
|
|
return memoryLimitObservabilityRows(rows, limit), nil
|
|
}
|
|
|
|
func (s *memoryObservabilityStore) DeleteAllNodeObservationOpenresty(_ context.Context) (int64, error) {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
count := int64(len(s.openrestyObs))
|
|
s.openrestyObs = nil
|
|
return count, nil
|
|
}
|
|
|
|
func (s *memoryObservabilityStore) DeleteNodeObservationOpenrestyBefore(_ context.Context, cutoff time.Time) (int64, error) {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
cutoff = cutoff.UTC()
|
|
remaining := make([]*OpenFlareNodeObservationOpenresty, 0, len(s.openrestyObs))
|
|
var deleted int64
|
|
for _, row := range s.openrestyObs {
|
|
if row.CapturedAt.Before(cutoff) {
|
|
deleted++
|
|
continue
|
|
}
|
|
remaining = append(remaining, row)
|
|
}
|
|
s.openrestyObs = remaining
|
|
return deleted, nil
|
|
}
|
|
|
|
func (s *memoryObservabilityStore) InsertNodeObservationFrps(_ context.Context, record *OpenFlareNodeObservationFrps) error {
|
|
if record == nil {
|
|
return nil
|
|
}
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
s.frpsObs = append(s.frpsObs, cloneOpenFlareNodeObservationFrps(record))
|
|
return nil
|
|
}
|
|
|
|
func (s *memoryObservabilityStore) ListNodeObservationFrps(_ context.Context, nodeID string, since time.Time, limit int) ([]*OpenFlareNodeObservationFrps, error) {
|
|
s.mu.RLock()
|
|
defer s.mu.RUnlock()
|
|
rows := memoryFilterFrpsObservations(s.frpsObs, nodeID, since)
|
|
sortOpenFlareNodeObservationFrps(rows)
|
|
return memoryLimitObservabilityRows(rows, limit), nil
|
|
}
|
|
|
|
func (s *memoryObservabilityStore) DeleteAllNodeObservationFrps(_ context.Context) (int64, error) {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
count := int64(len(s.frpsObs))
|
|
s.frpsObs = nil
|
|
return count, nil
|
|
}
|
|
|
|
func (s *memoryObservabilityStore) DeleteNodeObservationFrpsBefore(_ context.Context, cutoff time.Time) (int64, error) {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
cutoff = cutoff.UTC()
|
|
remaining := make([]*OpenFlareNodeObservationFrps, 0, len(s.frpsObs))
|
|
var deleted int64
|
|
for _, row := range s.frpsObs {
|
|
if row.CapturedAt.Before(cutoff) {
|
|
deleted++
|
|
continue
|
|
}
|
|
remaining = append(remaining, row)
|
|
}
|
|
s.frpsObs = remaining
|
|
return deleted, nil
|
|
}
|
|
|
|
func (s *memoryObservabilityStore) InsertNodeObservationFrpc(_ context.Context, record *OpenFlareNodeObservationFrpc) error {
|
|
if record == nil {
|
|
return nil
|
|
}
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
s.frpcObs = append(s.frpcObs, cloneOpenFlareNodeObservationFrpc(record))
|
|
return nil
|
|
}
|
|
|
|
func (s *memoryObservabilityStore) ListNodeObservationFrpc(_ context.Context, nodeID string, since time.Time, limit int) ([]*OpenFlareNodeObservationFrpc, error) {
|
|
s.mu.RLock()
|
|
defer s.mu.RUnlock()
|
|
rows := memoryFilterFrpcObservations(s.frpcObs, nodeID, since)
|
|
sortOpenFlareNodeObservationFrpc(rows)
|
|
return memoryLimitObservabilityRows(rows, limit), nil
|
|
}
|
|
|
|
func (s *memoryObservabilityStore) DeleteAllNodeObservationFrpc(_ context.Context) (int64, error) {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
count := int64(len(s.frpcObs))
|
|
s.frpcObs = nil
|
|
return count, nil
|
|
}
|
|
|
|
func (s *memoryObservabilityStore) DeleteNodeObservationFrpcBefore(_ context.Context, cutoff time.Time) (int64, error) {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
cutoff = cutoff.UTC()
|
|
remaining := make([]*OpenFlareNodeObservationFrpc, 0, len(s.frpcObs))
|
|
var deleted int64
|
|
for _, row := range s.frpcObs {
|
|
if row.CapturedAt.Before(cutoff) {
|
|
deleted++
|
|
continue
|
|
}
|
|
remaining = append(remaining, row)
|
|
}
|
|
s.frpcObs = remaining
|
|
return deleted, nil
|
|
}
|
|
|
|
func memoryFilterMetricSnapshots(rows []*OpenFlareMetricSnapshot, nodeID string, since time.Time) []*OpenFlareMetricSnapshot {
|
|
result := make([]*OpenFlareMetricSnapshot, 0, len(rows))
|
|
for _, row := range rows {
|
|
if !memoryObservabilityMatchesNodeID(row.NodeID, nodeID) {
|
|
continue
|
|
}
|
|
if !since.IsZero() && row.CapturedAt.Before(since) {
|
|
continue
|
|
}
|
|
result = append(result, row)
|
|
}
|
|
return result
|
|
}
|
|
|
|
func memoryFilterRequestReports(rows []*OpenFlareRequestReport, nodeID string, since time.Time) []*OpenFlareRequestReport {
|
|
result := make([]*OpenFlareRequestReport, 0, len(rows))
|
|
for _, row := range rows {
|
|
if !memoryObservabilityMatchesNodeID(row.NodeID, nodeID) {
|
|
continue
|
|
}
|
|
if !since.IsZero() && row.WindowEndedAt.Before(since) {
|
|
continue
|
|
}
|
|
result = append(result, row)
|
|
}
|
|
return result
|
|
}
|
|
|
|
func memoryFilterOpenrestyObservations(rows []*OpenFlareNodeObservationOpenresty, nodeID string, since time.Time) []*OpenFlareNodeObservationOpenresty {
|
|
result := make([]*OpenFlareNodeObservationOpenresty, 0, len(rows))
|
|
for _, row := range rows {
|
|
if !memoryObservabilityMatchesNodeID(row.NodeID, nodeID) {
|
|
continue
|
|
}
|
|
if !since.IsZero() && row.CapturedAt.Before(since) {
|
|
continue
|
|
}
|
|
result = append(result, row)
|
|
}
|
|
return result
|
|
}
|
|
|
|
func memoryFilterFrpsObservations(rows []*OpenFlareNodeObservationFrps, nodeID string, since time.Time) []*OpenFlareNodeObservationFrps {
|
|
result := make([]*OpenFlareNodeObservationFrps, 0, len(rows))
|
|
for _, row := range rows {
|
|
if !memoryObservabilityMatchesNodeID(row.NodeID, nodeID) {
|
|
continue
|
|
}
|
|
if !since.IsZero() && row.CapturedAt.Before(since) {
|
|
continue
|
|
}
|
|
result = append(result, row)
|
|
}
|
|
return result
|
|
}
|
|
|
|
func memoryFilterFrpcObservations(rows []*OpenFlareNodeObservationFrpc, nodeID string, since time.Time) []*OpenFlareNodeObservationFrpc {
|
|
result := make([]*OpenFlareNodeObservationFrpc, 0, len(rows))
|
|
for _, row := range rows {
|
|
if !memoryObservabilityMatchesNodeID(row.NodeID, nodeID) {
|
|
continue
|
|
}
|
|
if !since.IsZero() && row.CapturedAt.Before(since) {
|
|
continue
|
|
}
|
|
result = append(result, row)
|
|
}
|
|
return result
|
|
}
|
|
|
|
func memoryObservabilityMatchesNodeID(rowNodeID string, nodeID string) bool {
|
|
trimmed := strings.TrimSpace(nodeID)
|
|
if trimmed == "" {
|
|
return true
|
|
}
|
|
return rowNodeID == trimmed
|
|
}
|
|
|
|
func memoryMetricSnapshotExists(rows []*OpenFlareMetricSnapshot, nodeID string, capturedAt time.Time) bool {
|
|
capturedAt = capturedAt.UTC()
|
|
for _, row := range rows {
|
|
if row.NodeID == nodeID && row.CapturedAt.UTC().Equal(capturedAt) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func memoryRequestReportExists(rows []*OpenFlareRequestReport, nodeID string, windowStartedAt, windowEndedAt time.Time) bool {
|
|
windowStartedAt = windowStartedAt.UTC()
|
|
windowEndedAt = windowEndedAt.UTC()
|
|
for _, row := range rows {
|
|
if row.NodeID == nodeID &&
|
|
row.WindowStartedAt.UTC().Equal(windowStartedAt) &&
|
|
row.WindowEndedAt.UTC().Equal(windowEndedAt) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func sortOpenFlareMetricSnapshots(items []*OpenFlareMetricSnapshot) {
|
|
sort.Slice(items, func(i, j int) bool {
|
|
left := items[i]
|
|
right := items[j]
|
|
if left == nil || right == nil {
|
|
return left != nil
|
|
}
|
|
if compare := openFlareAccessLogCompareInt64(left.CapturedAt.Unix(), right.CapturedAt.Unix()); compare != 0 {
|
|
return compare > 0
|
|
}
|
|
return openFlareAccessLogCompareInt64(openFlareAccessLogUintToInt64(uint64(left.ID)), openFlareAccessLogUintToInt64(uint64(right.ID))) > 0
|
|
})
|
|
}
|
|
|
|
func sortOpenFlareRequestReports(items []*OpenFlareRequestReport) {
|
|
sort.Slice(items, func(i, j int) bool {
|
|
left := items[i]
|
|
right := items[j]
|
|
if left == nil || right == nil {
|
|
return left != nil
|
|
}
|
|
if compare := openFlareAccessLogCompareInt64(left.WindowEndedAt.Unix(), right.WindowEndedAt.Unix()); compare != 0 {
|
|
return compare > 0
|
|
}
|
|
return openFlareAccessLogCompareInt64(openFlareAccessLogUintToInt64(uint64(left.ID)), openFlareAccessLogUintToInt64(uint64(right.ID))) > 0
|
|
})
|
|
}
|
|
|
|
func sortOpenFlareNodeObservationOpenresty(items []*OpenFlareNodeObservationOpenresty) {
|
|
sort.Slice(items, func(i, j int) bool {
|
|
left := items[i]
|
|
right := items[j]
|
|
if left == nil || right == nil {
|
|
return left != nil
|
|
}
|
|
if compare := openFlareAccessLogCompareInt64(left.CapturedAt.Unix(), right.CapturedAt.Unix()); compare != 0 {
|
|
return compare > 0
|
|
}
|
|
return openFlareAccessLogCompareInt64(openFlareAccessLogUintToInt64(uint64(left.ID)), openFlareAccessLogUintToInt64(uint64(right.ID))) > 0
|
|
})
|
|
}
|
|
|
|
func sortOpenFlareNodeObservationFrps(items []*OpenFlareNodeObservationFrps) {
|
|
sort.Slice(items, func(i, j int) bool {
|
|
left := items[i]
|
|
right := items[j]
|
|
if left == nil || right == nil {
|
|
return left != nil
|
|
}
|
|
if compare := openFlareAccessLogCompareInt64(left.CapturedAt.Unix(), right.CapturedAt.Unix()); compare != 0 {
|
|
return compare > 0
|
|
}
|
|
return openFlareAccessLogCompareInt64(openFlareAccessLogUintToInt64(uint64(left.ID)), openFlareAccessLogUintToInt64(uint64(right.ID))) > 0
|
|
})
|
|
}
|
|
|
|
func sortOpenFlareNodeObservationFrpc(items []*OpenFlareNodeObservationFrpc) {
|
|
sort.Slice(items, func(i, j int) bool {
|
|
left := items[i]
|
|
right := items[j]
|
|
if left == nil || right == nil {
|
|
return left != nil
|
|
}
|
|
if compare := openFlareAccessLogCompareInt64(left.CapturedAt.Unix(), right.CapturedAt.Unix()); compare != 0 {
|
|
return compare > 0
|
|
}
|
|
return openFlareAccessLogCompareInt64(openFlareAccessLogUintToInt64(uint64(left.ID)), openFlareAccessLogUintToInt64(uint64(right.ID))) > 0
|
|
})
|
|
}
|
|
|
|
func memoryLimitObservabilityRows[T any](rows []T, limit int) []T {
|
|
if limit <= 0 || len(rows) <= limit {
|
|
result := make([]T, len(rows))
|
|
copy(result, rows)
|
|
return result
|
|
}
|
|
result := make([]T, limit)
|
|
copy(result, rows[:limit])
|
|
return result
|
|
}
|
|
|
|
func cloneOpenFlareMetricSnapshot(record *OpenFlareMetricSnapshot) *OpenFlareMetricSnapshot {
|
|
copyRecord := *record
|
|
if copyRecord.ID == 0 {
|
|
copyRecord.ID = uint(idgen.NextUint64ID())
|
|
}
|
|
now := time.Now().UTC()
|
|
if copyRecord.CreatedAt.IsZero() {
|
|
copyRecord.CreatedAt = now
|
|
}
|
|
copyRecord.CapturedAt = copyRecord.CapturedAt.UTC()
|
|
copyRecord.CreatedAt = copyRecord.CreatedAt.UTC()
|
|
return ©Record
|
|
}
|
|
|
|
func cloneOpenFlareRequestReport(record *OpenFlareRequestReport) *OpenFlareRequestReport {
|
|
copyRecord := *record
|
|
if copyRecord.ID == 0 {
|
|
copyRecord.ID = uint(idgen.NextUint64ID())
|
|
}
|
|
now := time.Now().UTC()
|
|
if copyRecord.CreatedAt.IsZero() {
|
|
copyRecord.CreatedAt = now
|
|
}
|
|
copyRecord.WindowStartedAt = copyRecord.WindowStartedAt.UTC()
|
|
copyRecord.WindowEndedAt = copyRecord.WindowEndedAt.UTC()
|
|
copyRecord.CreatedAt = copyRecord.CreatedAt.UTC()
|
|
return ©Record
|
|
}
|
|
|
|
func cloneOpenFlareNodeObservationOpenresty(record *OpenFlareNodeObservationOpenresty) *OpenFlareNodeObservationOpenresty {
|
|
copyRecord := *record
|
|
if copyRecord.ID == 0 {
|
|
copyRecord.ID = uint(idgen.NextUint64ID())
|
|
}
|
|
now := time.Now().UTC()
|
|
if copyRecord.CreatedAt.IsZero() {
|
|
copyRecord.CreatedAt = now
|
|
}
|
|
if copyRecord.CapturedAt.IsZero() {
|
|
copyRecord.CapturedAt = now
|
|
}
|
|
copyRecord.CapturedAt = copyRecord.CapturedAt.UTC()
|
|
copyRecord.CreatedAt = copyRecord.CreatedAt.UTC()
|
|
return ©Record
|
|
}
|
|
|
|
func cloneOpenFlareNodeObservationFrps(record *OpenFlareNodeObservationFrps) *OpenFlareNodeObservationFrps {
|
|
copyRecord := *record
|
|
if copyRecord.ID == 0 {
|
|
copyRecord.ID = uint(idgen.NextUint64ID())
|
|
}
|
|
now := time.Now().UTC()
|
|
if copyRecord.CreatedAt.IsZero() {
|
|
copyRecord.CreatedAt = now
|
|
}
|
|
if copyRecord.CapturedAt.IsZero() {
|
|
copyRecord.CapturedAt = now
|
|
}
|
|
copyRecord.CapturedAt = copyRecord.CapturedAt.UTC()
|
|
copyRecord.CreatedAt = copyRecord.CreatedAt.UTC()
|
|
return ©Record
|
|
}
|
|
|
|
func cloneOpenFlareNodeObservationFrpc(record *OpenFlareNodeObservationFrpc) *OpenFlareNodeObservationFrpc {
|
|
copyRecord := *record
|
|
if copyRecord.ID == 0 {
|
|
copyRecord.ID = uint(idgen.NextUint64ID())
|
|
}
|
|
now := time.Now().UTC()
|
|
if copyRecord.CreatedAt.IsZero() {
|
|
copyRecord.CreatedAt = now
|
|
}
|
|
if copyRecord.CapturedAt.IsZero() {
|
|
copyRecord.CapturedAt = now
|
|
}
|
|
copyRecord.CapturedAt = copyRecord.CapturedAt.UTC()
|
|
copyRecord.CreatedAt = copyRecord.CreatedAt.UTC()
|
|
return ©Record
|
|
}
|