optimize first create monitor config func of real time data query api

This commit is contained in:
douxu 2025-11-05 18:20:54 +08:00
parent f5ea909120
commit b75358e676
10 changed files with 322 additions and 96 deletions

View File

@ -9,3 +9,18 @@ const (
// MonitorAppendAction define the real time monitor append action
MonitorAppendAction string = "append"
)
// 定义状态常量
const (
// SubSuccessCode define subscription success code
SubSuccessCode = "1001"
// SubSuccessCode define subscription failed code
SubFailedCode = "1002"
)
const (
// SubSuccessMsg define subscription success message
SubSuccessMsg = "subscription success"
// SubFailedMsg define subscription failed message
SubFailedMsg = "subscription failed"
)

View File

@ -27,9 +27,9 @@ func CreateComponentIntoDB(ctx context.Context, tx *gorm.DB, componentInfo netwo
component := orm.Component{
GlobalUUID: globalUUID,
GridID: strconv.FormatInt(componentInfo.GridID, 10),
ZoneID: strconv.FormatInt(componentInfo.ZoneID, 10),
StationID: strconv.FormatInt(componentInfo.StationID, 10),
GridTag: strconv.FormatInt(componentInfo.GridID, 10),
ZoneTag: strconv.FormatInt(componentInfo.ZoneID, 10),
StationTag: strconv.FormatInt(componentInfo.StationID, 10),
Tag: componentInfo.Tag,
Name: componentInfo.Name,
Context: componentInfo.Context,

View File

@ -3,64 +3,86 @@ package database
import (
"context"
"fmt"
"strings"
"modelRT/logger"
"modelRT/model"
"modelRT/orm"
"gorm.io/gorm"
)
// FillingShortTokenModel define filling short token model info
func FillingShortTokenModel(ctx context.Context, tx *gorm.DB, attrItems []string, attrModel *model.ShortAttrInfo) error {
// TODO 重新创建tokenModel 及相关sql查询函数
// component, err := QueryComponentByLongToken(ctx, tx, attrItems[0])
// if err != nil {
// return err
// }
// attrModel.ComponentInfo = &component
func FillingShortTokenModel(ctx context.Context, tx *gorm.DB, identModel *model.ShortIdentityTokenModel) error {
filterComponent := &orm.Component{
GridTag: identModel.GetGridTag(),
ZoneTag: identModel.GetZoneTag(),
StationTag: identModel.GetStationTag(),
}
component, measurement, err := QueryLongIdentModelInfoByToken(ctx, tx, identModel.MeasurementTag, filterComponent)
if err != nil {
logger.Error(ctx, "query long identity token model info failed", "error", err)
return err
}
identModel.ComponentInfo = component
identModel.MeasurementInfo = measurement
return nil
}
// FillingLongTokenModel define filling long token model info
func FillingLongTokenModel(ctx context.Context, tx *gorm.DB, attrItems []string, attrModel *model.LongAttrInfo) error {
// TODO 重新创建tokenModel 及相关sql查询函数
// component, err := QueryComponentByShortToken(ctx, tx, attrItems[3])
// if err != nil {
// return err
// }
// attrModel.ComponentInfo = &component
func FillingLongTokenModel(ctx context.Context, tx *gorm.DB, identModel *model.LongIdentityTokenModel) error {
filterComponent := &orm.Component{
GridTag: identModel.GetGridTag(),
ZoneTag: identModel.GetZoneTag(),
StationTag: identModel.GetStationTag(),
Tag: identModel.GetComponentTag(),
}
component, measurement, err := QueryLongIdentModelInfoByToken(ctx, tx, identModel.MeasurementTag, filterComponent)
if err != nil {
logger.Error(ctx, "query long identity token model info failed", "error", err)
return err
}
identModel.ComponentInfo = component
identModel.MeasurementInfo = measurement
return nil
}
// ParseDataIdentifierToken define function to parse data identifier token function
func ParseDataIdentifierToken(ctx context.Context, tx *gorm.DB, identToken string) (model.AttrModelInterface, error) {
// TODO 使用identityToken代替ShortAttrInfo等
attrSlice := strings.Split(identToken, ".")
attrLen := len(attrSlice)
if attrLen == 4 {
short := &model.ShortAttrInfo{
AttrGroupName: attrSlice[2],
AttrKey: attrSlice[3],
func ParseDataIdentifierToken(ctx context.Context, tx *gorm.DB, identToken string) (model.IndentityTokenModelInterface, error) {
identSlice := strings.Split(identToken, ".")
identSliceLen := len(identSlice)
if identSliceLen == 4 {
// token1.token2.token3.token4.token7
shortIndentModel := &model.ShortIdentityTokenModel{
GridTag: identSlice[0],
ZoneTag: identSlice[1],
StationTag: identSlice[2],
NamespacePath: identSlice[3],
MeasurementTag: identSlice[6],
}
err := FillingShortTokenModel(ctx, tx, attrSlice, short)
err := FillingShortTokenModel(ctx, tx, shortIndentModel)
if err != nil {
return nil, err
}
// short.AttrValue = attrValue
return short, nil
} else if attrLen == 7 {
long := &model.LongAttrInfo{
AttrGroupName: attrSlice[5],
AttrKey: attrSlice[6],
return shortIndentModel, nil
} else if identSliceLen == 7 {
// token1.token2.token3.token4.token5.token6.token7
longIndentModel := &model.LongIdentityTokenModel{
GridTag: identSlice[0],
ZoneTag: identSlice[1],
StationTag: identSlice[2],
NamespacePath: identSlice[3],
ComponentTag: identSlice[4],
AttributeGroup: identSlice[5],
MeasurementTag: identSlice[6],
}
err := FillingLongTokenModel(ctx, tx, attrSlice, long)
err := FillingLongTokenModel(ctx, tx, longIndentModel)
if err != nil {
return nil, err
}
// long.AttrValue = attrValue
return long, nil
return longIndentModel, nil
}
return nil, nil
return nil, fmt.Errorf("invalid identity token format: %s", identToken)
}

View File

@ -3,6 +3,7 @@ package database
import (
"context"
"fmt"
"time"
"modelRT/orm"
@ -81,3 +82,58 @@ func QueryComponentByNsPath(ctx context.Context, tx *gorm.DB, nsPath string) (or
}
return component, nil
}
// QueryLongIdentModelInfoByToken define func to query long identity model info by long token
func QueryLongIdentModelInfoByToken(ctx context.Context, tx *gorm.DB, measTag string, condition *orm.Component) (*orm.Component, *orm.Measurement, error) {
var resultComp orm.Component
var meauserment orm.Measurement
// ctx timeout judgment
cancelCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
result := tx.WithContext(cancelCtx).Clauses(clause.Locking{Strength: "UPDATE"}).First(&resultComp, &condition)
if result.Error != nil {
if result.Error == gorm.ErrRecordNotFound {
return nil, nil, fmt.Errorf("component record not found by %v:%w", condition, result.Error)
}
return nil, nil, result.Error
}
filterMap := map[string]any{"COMPONENT_UUID": resultComp.GlobalUUID, "TAG": measTag}
result = tx.WithContext(cancelCtx).Where(filterMap).Clauses(clause.Locking{Strength: "UPDATE"}).First(&meauserment)
if result.Error != nil {
if result.Error == gorm.ErrRecordNotFound {
return nil, nil, fmt.Errorf("measurement record not found by %v:%w", filterMap, result.Error)
}
return nil, nil, result.Error
}
return &resultComp, &meauserment, nil
}
// QueryShortIdentModelInfoByToken define func to query short identity model info by short token
func QueryShortIdentModelInfoByToken(ctx context.Context, tx *gorm.DB, measTag string, condition *orm.Component) (*orm.Component, *orm.Measurement, error) {
var resultComp orm.Component
var meauserment orm.Measurement
// ctx timeout judgment
cancelCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
result := tx.WithContext(cancelCtx).Clauses(clause.Locking{Strength: "UPDATE"}).First(&resultComp, &condition)
if result.Error != nil {
if result.Error == gorm.ErrRecordNotFound {
return nil, nil, fmt.Errorf("component record not found by %v:%w", condition, result.Error)
}
return nil, nil, result.Error
}
filterMap := map[string]any{"COMPONENT_UUID": resultComp.GlobalUUID, "TAG": measTag}
result = tx.WithContext(cancelCtx).Where(filterMap).Clauses(clause.Locking{Strength: "UPDATE"}).First(&meauserment)
if result.Error != nil {
if result.Error == gorm.ErrRecordNotFound {
return nil, nil, fmt.Errorf("measurement record not found by %v:%w", filterMap, result.Error)
}
return nil, nil, result.Error
}
return &resultComp, &meauserment, nil
}

View File

@ -37,9 +37,9 @@ func UpdateComponentIntoDB(ctx context.Context, tx *gorm.DB, componentInfo netwo
updateParams := orm.Component{
GlobalUUID: globalUUID,
GridID: strconv.FormatInt(componentInfo.GridID, 10),
ZoneID: strconv.FormatInt(componentInfo.ZoneID, 10),
StationID: strconv.FormatInt(componentInfo.StationID, 10),
GridTag: strconv.FormatInt(componentInfo.GridID, 10),
ZoneTag: strconv.FormatInt(componentInfo.ZoneID, 10),
StationTag: strconv.FormatInt(componentInfo.StationID, 10),
Tag: componentInfo.Tag,
Name: componentInfo.Name,
Context: componentInfo.Context,

View File

@ -8,11 +8,13 @@ import (
"sync"
"modelRT/constants"
"modelRT/database"
"modelRT/logger"
"modelRT/network"
"github.com/gin-gonic/gin"
"github.com/gofrs/uuid"
"gorm.io/gorm"
)
var globalMonitorState *SharedMonitorState
@ -62,9 +64,14 @@ func RealTimeMonitorHandler(c *gin.Context) {
monitorID = request.MonitorID
}
pgClient := database.GetPostgresDBClient()
// open transaction
tx := pgClient.Begin()
defer tx.Commit()
switch monitorAction {
case constants.MonitorStartAction:
err := globalMonitorState.CreateConfig(monitorID, request.Components)
results, err := globalMonitorState.CreateConfig(c, tx, monitorID, request.Components)
if err != nil {
logger.Error(c, "create real time data monitor config failed", "error", err)
c.JSON(http.StatusOK, network.FailureResponse{
@ -77,8 +84,8 @@ func RealTimeMonitorHandler(c *gin.Context) {
c.JSON(http.StatusOK, network.SuccessResponse{
Code: http.StatusOK,
Msg: "success",
PayLoad: map[string]interface{}{
"monitor_id": monitorID,
PayLoad: network.RealTimeQueryPayload{
TargetResults: results,
},
})
return
@ -130,12 +137,12 @@ func NewSharedMonitorState() *SharedMonitorState {
}
// CreateConfig define function to create config in SharedMonitorState
func (s *SharedMonitorState) CreateConfig(monitorID string, components []network.RealTimeComponentItem) error {
func (s *SharedMonitorState) CreateConfig(ctx context.Context, tx *gorm.DB, monitorID string, components []network.RealTimeComponentItem) ([]network.TargetResult, error) {
s.mutex.Lock()
defer s.mutex.Unlock()
if _, exist := s.monitorMap[monitorID]; exist {
return fmt.Errorf("monitorID %s already exists. Use AppendTargets to modify existing config", monitorID)
return nil, fmt.Errorf("monitorID %s already exists. Use AppendTargets to modify existing config", monitorID)
}
config := &RealTimeMonitorConfig{
@ -143,17 +150,44 @@ func (s *SharedMonitorState) CreateConfig(monitorID string, components []network
components: make(map[string]*RealTimeMonitorComponent),
}
for _, compent := range components {
config.components[compent.Interval] = &RealTimeMonitorComponent{
targets: compent.Targets,
targetProcessResults := make([]network.TargetResult, 0, processRealTimeRequestCount(components))
for _, componentItem := range components {
interval := componentItem.Interval
for _, target := range componentItem.Targets {
targetModel, err := database.ParseDataIdentifierToken(ctx, tx, target)
var targetResult network.TargetResult
targetResult.ID = target
if err != nil {
logger.Error(ctx, "parse data indentity token failed", "error", err, "identity_token", target)
targetResult.Code = constants.SubFailedCode
targetResult.Msg = fmt.Sprintf("%s: %s", constants.SubFailedMsg, err.Error())
targetProcessResults = append(targetProcessResults, targetResult)
continue
}
targetResult.Code = constants.SubSuccessCode
targetResult.Msg = constants.SubSuccessMsg
if _, ok := config.components[componentItem.Interval]; !ok {
targets := make([]string, 0, len(componentItem.Targets))
config.components[interval] = &RealTimeMonitorComponent{
targets: append(targets, target),
}
} else {
component := config.components[interval]
component.targets = append(component.targets, target)
}
fmt.Println(targetModel.GetMeasurementInfo().Size)
}
}
s.monitorMap[monitorID] = config
return nil
return targetProcessResults, nil
}
// AppendTargets define function to append targets in SharedMonitorState
// TODO 增加targetsResults的返回
func (s *SharedMonitorState) AppendTargets(monitorID string, components []network.RealTimeComponentItem) error {
s.mutex.Lock()
defer s.mutex.Unlock()
@ -168,12 +202,11 @@ func (s *SharedMonitorState) AppendTargets(monitorID string, components []networ
comp, compExist := config.components[interval]
if !compExist {
comp = &RealTimeMonitorComponent{
targets: comp.targets,
targets: compent.Targets,
}
config.components[interval] = comp
} else {
comp.targets = append(comp.targets, comp.targets...)
comp.targets = append(comp.targets, compent.Targets...)
}
}
@ -182,6 +215,7 @@ func (s *SharedMonitorState) AppendTargets(monitorID string, components []networ
}
// UpsertTargets define function to upsert targets in SharedMonitorState
// TODO 增加targetsResults的返回
func (s *SharedMonitorState) UpsertTargets(monitorID string, interval string, newTargets []string) (isNewMonitor bool, err error) {
s.mutex.Lock()
defer s.mutex.Unlock()
@ -229,6 +263,7 @@ func (s *SharedMonitorState) Get(monitorID, interval string) ([]string, bool) {
}
// RemoveTargets define function to remove targets in SharedMonitorState
// TODO 增加targetsResults的返回
func (s *SharedMonitorState) RemoveTargets(ctx context.Context, monitorID string, components []network.RealTimeComponentItem) error {
s.mutex.Lock()
defer s.mutex.Unlock()
@ -271,3 +306,11 @@ func (s *SharedMonitorState) RemoveTargets(ctx context.Context, monitorID string
config.noticeChan <- struct{}{}
return nil
}
func processRealTimeRequestCount(components []network.RealTimeComponentItem) int {
totalTargetsCount := 0
for _, compItem := range components {
totalTargetsCount += len(compItem.Targets)
}
return totalTargetsCount
}

View File

@ -0,0 +1,47 @@
package middleware
import (
"net"
"net/http"
"net/http/httputil"
"os"
"runtime/debug"
"strings"
"modelRT/logger"
"github.com/gin-gonic/gin"
)
// GinPanicRecovery define func of customizing gin recover output
func GinPanicRecovery() gin.HandlerFunc {
return func(c *gin.Context) {
defer func() {
if err := recover(); err != nil {
// Check for a broken connection, as it is not really a
// condition that warrants a panic stack trace.
var brokenPipe bool
if ne, ok := err.(*net.OpError); ok {
if se, ok := ne.Err.(*os.SyscallError); ok {
if strings.Contains(strings.ToLower(se.Error()), "broken pipe") || strings.Contains(strings.ToLower(se.Error()), "connection reset by peer") {
brokenPipe = true
}
}
}
httpRequest, _ := httputil.DumpRequest(c.Request, false)
if brokenPipe {
logger.Error(c, "http request broken pipe", "path", c.Request.URL.Path, "error", err, "request", string(httpRequest))
// If the connection is dead, we can't write a status to it.
c.Error(err.(error))
c.Abort()
return
}
logger.Error(c, "http_request_panic", "path", c.Request.URL.Path, "error", err, "request", string(httpRequest), "stack", string(debug.Stack()))
c.AbortWithError(http.StatusInternalServerError, err.(error))
}
}()
c.Next()
}
}

View File

@ -5,34 +5,29 @@ import "modelRT/orm"
// IndentityTokenModelInterface define basic identity token model type interface
type IndentityTokenModelInterface interface {
GetGridInfo() *orm.Grid
GetZoneInfo() *orm.Zone
GetStationInfo() *orm.Station
GetComponentInfo() *orm.Component
GetMeasurementInfo() *orm.Measurement
GetGridTag() string // token1
GetZoneTag() string // token2
GetStationTag() string // token3
GetNamespacePath() string // token4(COMPONENT TABLE NSPATH)
GetComponentTag() string // token5(COMPONENT TABLE TAG)
GetAttributeGroup() string // token6(component attribute group information)
GetMeasurementTag() string // token7(measurement value or attribute field)
IsLocal() bool
}
// LongIdentityTokenModel define struct to long identity token info
type LongIdentityTokenModel struct {
GridInfo *orm.Grid
ZoneInfo *orm.Zone
StationInfo *orm.Station
ComponentInfo *orm.Component
}
// GetGridInfo define return the grid information in the long identity token
func (l *LongIdentityTokenModel) GetGridInfo() *orm.Grid {
return l.GridInfo
}
// GetZoneInfo define return the zone information in the long identity token
func (l *LongIdentityTokenModel) GetZoneInfo() *orm.Zone {
return l.ZoneInfo
}
// GetStationInfo define return the station information in the long identity token
func (l *LongIdentityTokenModel) GetStationInfo() *orm.Station {
return l.StationInfo
ComponentInfo *orm.Component
MeasurementInfo *orm.Measurement
GridTag string
ZoneTag string
StationTag string
NamespacePath string
ComponentTag string
AttributeGroup string
MeasurementTag string
}
// GetComponentInfo define return the component information in the long identity token
@ -40,6 +35,34 @@ func (l *LongIdentityTokenModel) GetComponentInfo() *orm.Component {
return l.ComponentInfo
}
// GetMeasurementInfo define return the measurement information in the long identity token
func (l *LongIdentityTokenModel) GetMeasurementInfo() *orm.Measurement {
return l.MeasurementInfo
}
// GetGridTag define function to return the grid tag information in the long identity token
func (l *LongIdentityTokenModel) GetGridTag() string { return l.GridTag }
// GetZoneTag define function to return the zone tag information in the long identity token
func (l *LongIdentityTokenModel) GetZoneTag() string { return l.ZoneTag }
// GetStationTag define function to return the station tag information in the long identity token
func (l *LongIdentityTokenModel) GetStationTag() string { return l.StationTag }
// GetNamespacePath define function to return the namespace path information in the long identity token
func (l *LongIdentityTokenModel) GetNamespacePath() string { return l.NamespacePath }
// GetComponentTag define function to return the component tag information in the long identity token
func (l *LongIdentityTokenModel) GetComponentTag() string { return l.ComponentTag }
// GetAttributeGroup define function to return the attribute group information in the long identity token
func (l *LongIdentityTokenModel) GetAttributeGroup() string { return l.AttributeGroup }
// GetMeasurementTag define function to return the measurement tag information in the long identity token
func (l *LongIdentityTokenModel) GetMeasurementTag() string {
return l.MeasurementTag
}
// IsLocal define return the is_local information in the long identity token
func (l *LongIdentityTokenModel) IsLocal() bool {
return false
@ -47,22 +70,14 @@ func (l *LongIdentityTokenModel) IsLocal() bool {
// ShortIdentityTokenModel define struct to short identity token info
type ShortIdentityTokenModel struct {
ComponentInfo *orm.Component
}
ComponentInfo *orm.Component
MeasurementInfo *orm.Measurement
// GetGridInfo define return the grid information in the short identity token
func (s *ShortIdentityTokenModel) GetGridInfo() *orm.Grid {
return nil
}
// GetZoneInfo define return the zone information in the short identity token
func (s *ShortIdentityTokenModel) GetZoneInfo() *orm.Zone {
return nil
}
// GetStationInfo define return the station information in the short identity token
func (s *ShortIdentityTokenModel) GetStationInfo() *orm.Station {
return nil
GridTag string // token1
ZoneTag string // token2
StationTag string // token3
NamespacePath string // token4
MeasurementTag string // token7
}
// GetComponentInfo define return the component information in the short identity token
@ -70,6 +85,34 @@ func (s *ShortIdentityTokenModel) GetComponentInfo() *orm.Component {
return s.ComponentInfo
}
// GetMeasurementInfo define return the measurement information in the long identity token
func (s *ShortIdentityTokenModel) GetMeasurementInfo() *orm.Measurement {
return s.MeasurementInfo
}
// GetGridTag define function to return the grid tag information in the short identity token
func (s *ShortIdentityTokenModel) GetGridTag() string { return "" }
// GetZoneTag define function to return the zone tag information in the short identity token
func (s *ShortIdentityTokenModel) GetZoneTag() string { return "" }
// GetStationTag define function to return the station tag information in the short identity token
func (s *ShortIdentityTokenModel) GetStationTag() string { return "" }
// GetNamespacePath define function to return the namespace path information in the short identity token
func (s *ShortIdentityTokenModel) GetNamespacePath() string { return s.NamespacePath }
// GetComponentTag define function to return the component tag information in the short identity token
func (s *ShortIdentityTokenModel) GetComponentTag() string { return "" }
// GetAttributeGroup define function to return the attribute group information in the short identity token
func (s *ShortIdentityTokenModel) GetAttributeGroup() string { return "" }
// GetMeasurementTag define function to return the measurement tag information in the short identity token
func (s *ShortIdentityTokenModel) GetMeasurementTag() string {
return ""
}
// IsLocal define return the is_local information in the short identity token
func (s *ShortIdentityTokenModel) IsLocal() bool {
return true

View File

@ -22,8 +22,8 @@ type MeasurementRecommendPayload struct {
RecommendedList []string `json:"recommended_list" example:"[\"I_A_rms\", \"I_B_rms\",\"I_C_rms\"]"`
}
// Target define struct of target item in real time data query response payload
type Target struct {
// TargetResult define struct of target item in real time data query response payload
type TargetResult struct {
ID string `json:"id" example:"grid1.zone1.station1.ns1.tag1.transformfeeder1_220.I_A_rms"`
Code string `json:"code" example:"1001"`
Msg string `json:"msg" example:"subscription success"`
@ -31,5 +31,5 @@ type Target struct {
// RealTimeQueryPayload define struct of real time data query request
type RealTimeQueryPayload struct {
Targets []Target `json:"targets"`
TargetResults []TargetResult `json:"targets"`
}

View File

@ -15,9 +15,9 @@ type Component struct {
Name string `gorm:"column:NAME"`
ModelName string `gorm:"column:MODEL_NAME"`
Description string `gorm:"column:DESCRIPTION"`
GridID string `gorm:"column:GRID"`
ZoneID string `gorm:"column:ZONE"`
StationID string `gorm:"column:STATION"`
GridTag string `gorm:"column:GRID"`
ZoneTag string `gorm:"column:ZONE"`
StationTag string `gorm:"column:STATION"`
Type int `gorm:"column:TYPE"`
InService bool `gorm:"column:IN_SERVICE"`
State int `gorm:"column:STATE"`