optimize struct of real time data subscription api and fix bug of real time data pull api
This commit is contained in:
parent
b6e47177fb
commit
6f3134b5e9
|
|
@ -44,3 +44,8 @@ const (
|
||||||
// OpUpdate define update exist target from the subscription list
|
// OpUpdate define update exist target from the subscription list
|
||||||
OpUpdate
|
OpUpdate
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// NoticeChanCap define real time data notice channel capacity
|
||||||
|
NoticeChanCap = 10000
|
||||||
|
)
|
||||||
|
|
|
||||||
|
|
@ -611,7 +611,7 @@ func generateRandomData(baseValue float64, changes []float64, size int) []float6
|
||||||
// simulateDataWrite 定时生成并写入模拟数据到 Redis ZSet
|
// simulateDataWrite 定时生成并写入模拟数据到 Redis ZSet
|
||||||
func simulateDataWrite(ctx context.Context, rdb *redis.Client, redisKey string, config outlierConfig, measInfo calculationResult) {
|
func simulateDataWrite(ctx context.Context, rdb *redis.Client, redisKey string, config outlierConfig, measInfo calculationResult) {
|
||||||
log.Printf("启动数据写入程序, Redis Key: %s, 基准值: %.4f, 变化范围: %+v\n", redisKey, measInfo.BaseValue, measInfo.Changes)
|
log.Printf("启动数据写入程序, Redis Key: %s, 基准值: %.4f, 变化范围: %+v\n", redisKey, measInfo.BaseValue, measInfo.Changes)
|
||||||
ticker := time.NewTicker(1 * time.Second)
|
ticker := time.NewTicker(3 * time.Second)
|
||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
|
|
||||||
pipe := rdb.Pipeline()
|
pipe := rdb.Pipeline()
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,7 @@ func PullRealTimeDataHandler(c *gin.Context) {
|
||||||
if len(buffer) >= bufferMaxSize {
|
if len(buffer) >= bufferMaxSize {
|
||||||
// buffer is full, send immediately
|
// buffer is full, send immediately
|
||||||
if err := sendAggregateRealTimeDataStream(conn, buffer); err != nil {
|
if err := sendAggregateRealTimeDataStream(conn, buffer); err != nil {
|
||||||
logger.Error(nil, "when buffer is full, send the real time aggregate data failed", "client_id", clientID, "buffer", buffer, "error", err)
|
logger.Error(ctx, "when buffer is full, send the real time aggregate data failed", "client_id", clientID, "buffer", buffer, "error", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// reset buffer
|
// reset buffer
|
||||||
|
|
@ -94,7 +94,7 @@ func PullRealTimeDataHandler(c *gin.Context) {
|
||||||
if len(buffer) > 0 {
|
if len(buffer) > 0 {
|
||||||
// when the ticker is triggered, all data in the send buffer is sent
|
// when the ticker is triggered, all data in the send buffer is sent
|
||||||
if err := sendAggregateRealTimeDataStream(conn, buffer); err != nil {
|
if err := sendAggregateRealTimeDataStream(conn, buffer); err != nil {
|
||||||
logger.Error(nil, "when the ticker is triggered, send the real time aggregate data failed", "client_id", clientID, "buffer", buffer, "error", err)
|
logger.Error(ctx, "when the ticker is triggered, send the real time aggregate data failed", "client_id", clientID, "buffer", buffer, "error", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// reset buffer
|
// reset buffer
|
||||||
|
|
@ -103,7 +103,7 @@ func PullRealTimeDataHandler(c *gin.Context) {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
// send the last remaining data
|
// send the last remaining data
|
||||||
if err := sendAggregateRealTimeDataStream(conn, buffer); err != nil {
|
if err := sendAggregateRealTimeDataStream(conn, buffer); err != nil {
|
||||||
logger.Error(nil, "send the last remaining data failed", "client_id", clientID, "buffer", buffer, "error", err)
|
logger.Error(ctx, "send the last remaining data failed", "client_id", clientID, "buffer", buffer, "error", err)
|
||||||
}
|
}
|
||||||
logger.Info(ctx, "PullRealTimeDataHandler exiting as context is done.", "client_id", clientID)
|
logger.Info(ctx, "PullRealTimeDataHandler exiting as context is done.", "client_id", clientID)
|
||||||
return
|
return
|
||||||
|
|
@ -171,27 +171,27 @@ func processTargetPolling(ctx context.Context, s *SharedSubState, clientID strin
|
||||||
logger.Info(ctx, fmt.Sprintf("found subscription config for clientID:%s, start initial polling goroutines", clientID), "components len", config.components)
|
logger.Info(ctx, fmt.Sprintf("found subscription config for clientID:%s, start initial polling goroutines", clientID), "components len", config.components)
|
||||||
|
|
||||||
config.mutex.RLock()
|
config.mutex.RLock()
|
||||||
for interval, componentItems := range config.components {
|
for interval, measurementTargets := range config.measurements {
|
||||||
logger.Info(ctx, fmt.Sprintf("interval %s len of componentItems:%d\n", interval, len(componentItems.targets)))
|
for _, target := range measurementTargets {
|
||||||
for _, target := range componentItems.targets {
|
|
||||||
// add a secondary check to prevent the target from already existing in the stopChanMap
|
// add a secondary check to prevent the target from already existing in the stopChanMap
|
||||||
if _, exists := stopChanMap[target]; exists {
|
if _, exists := stopChanMap[target]; exists {
|
||||||
logger.Warn(ctx, "target already exists in polling map, skipping start-up", "target", target)
|
logger.Warn(ctx, "target already exists in polling map, skipping start-up", "target", target)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
measurement, exist := componentItems.targetParam[target]
|
targetContext, exist := config.targetContext[target]
|
||||||
if !exist {
|
if !exist {
|
||||||
logger.Error(ctx, "can not found subscription node param into param map", "target", target)
|
logger.Error(ctx, "can not found subscription node param into param map", "target", target)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
measurementInfo := targetContext.measurement
|
||||||
|
|
||||||
queryGStopChan := make(chan struct{})
|
queryGStopChan := make(chan struct{})
|
||||||
// store stop channel with target into map
|
// store stop channel with target into map
|
||||||
stopChanMap[target] = queryGStopChan
|
stopChanMap[target] = queryGStopChan
|
||||||
queryKey, err := model.GenerateMeasureIdentifier(measurement.DataSource)
|
queryKey, err := model.GenerateMeasureIdentifier(measurementInfo.DataSource)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error(ctx, "generate measurement indentifier by data_source field failed", "data_source", measurement.DataSource, "error", err)
|
logger.Error(ctx, "generate measurement indentifier by data_source field failed", "data_source", measurementInfo.DataSource, "error", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -199,7 +199,7 @@ func processTargetPolling(ctx context.Context, s *SharedSubState, clientID strin
|
||||||
targetID: target,
|
targetID: target,
|
||||||
queryKey: queryKey,
|
queryKey: queryKey,
|
||||||
interval: interval,
|
interval: interval,
|
||||||
dataSize: int64(measurement.Size),
|
dataSize: int64(measurementInfo.Size),
|
||||||
}
|
}
|
||||||
go realTimeDataQueryFromRedis(ctx, pollingConfig, fanInChan, queryGStopChan)
|
go realTimeDataQueryFromRedis(ctx, pollingConfig, fanInChan, queryGStopChan)
|
||||||
}
|
}
|
||||||
|
|
@ -219,7 +219,7 @@ func processTargetPolling(ctx context.Context, s *SharedSubState, clientID strin
|
||||||
case constants.OpAppend:
|
case constants.OpAppend:
|
||||||
appendTargets(ctx, config, stopChanMap, fanInChan, transportTargets.Targets)
|
appendTargets(ctx, config, stopChanMap, fanInChan, transportTargets.Targets)
|
||||||
case constants.OpRemove:
|
case constants.OpRemove:
|
||||||
removeTargets(ctx, stopChanMap, transportTargets.Targets)
|
removeTargets(ctx, config, stopChanMap, transportTargets.Targets)
|
||||||
}
|
}
|
||||||
config.mutex.Unlock()
|
config.mutex.Unlock()
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
|
|
@ -232,68 +232,87 @@ func processTargetPolling(ctx context.Context, s *SharedSubState, clientID strin
|
||||||
|
|
||||||
// appendTargets starts new polling goroutines for targets that were just added
|
// appendTargets starts new polling goroutines for targets that were just added
|
||||||
func appendTargets(ctx context.Context, config *RealTimeSubConfig, stopChanMap map[string]chan struct{}, fanInChan chan network.RealTimePullTarget, appendTargets []string) {
|
func appendTargets(ctx context.Context, config *RealTimeSubConfig, stopChanMap map[string]chan struct{}, fanInChan chan network.RealTimePullTarget, appendTargets []string) {
|
||||||
targetSet := make(map[string]struct{}, len(appendTargets))
|
appendTargetsSet := make(map[string]struct{}, len(appendTargets))
|
||||||
for _, target := range appendTargets {
|
for _, target := range appendTargets {
|
||||||
targetSet[target] = struct{}{}
|
appendTargetsSet[target] = struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
for interval, componentItems := range config.components {
|
for _, target := range appendTargets {
|
||||||
for _, target := range componentItems.targets {
|
targetContext, exists := config.targetContext[target]
|
||||||
if _, needsToAdd := targetSet[target]; !needsToAdd {
|
if exists {
|
||||||
continue
|
logger.Warn(ctx, "the append target already exists in the real time data fetch process,skipping the startup step", "target", target)
|
||||||
}
|
continue
|
||||||
|
|
||||||
if _, exists := stopChanMap[target]; exists {
|
|
||||||
logger.Warn(ctx, "append target already running, skipping", "target", target)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
measurement, exist := componentItems.targetParam[target]
|
|
||||||
if !exist {
|
|
||||||
logger.Error(ctx, "append target can not find measurement params for new target", "target", target)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
queryKey, err := model.GenerateMeasureIdentifier(measurement.DataSource)
|
|
||||||
if err != nil {
|
|
||||||
logger.Error(ctx, "append target generate measurement identifier failed", "target", target, "error", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
pollingConfig := redisPollingConfig{
|
|
||||||
targetID: target,
|
|
||||||
queryKey: queryKey,
|
|
||||||
interval: interval,
|
|
||||||
dataSize: int64(measurement.Size),
|
|
||||||
}
|
|
||||||
|
|
||||||
queryGStopChan := make(chan struct{})
|
|
||||||
stopChanMap[target] = queryGStopChan
|
|
||||||
|
|
||||||
go realTimeDataQueryFromRedis(ctx, pollingConfig, fanInChan, queryGStopChan)
|
|
||||||
|
|
||||||
logger.Info(ctx, "started new polling goroutine for appended target", "target", target, "interval", interval)
|
|
||||||
|
|
||||||
delete(targetSet, target)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, exists := stopChanMap[target]; exists {
|
||||||
|
logger.Warn(ctx, "the append target already has a stop channel, skipping the startup step", "target", target)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
queryGStopChan := make(chan struct{})
|
||||||
|
stopChanMap[target] = queryGStopChan
|
||||||
|
|
||||||
|
interval := targetContext.interval
|
||||||
|
measurementTargets, ok := config.measurements[interval]
|
||||||
|
if !ok {
|
||||||
|
logger.Error(ctx, "targetContext exists but measurements is missing, cannot update config", "target", target, "interval", interval)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
measurementTargets = append(measurementTargets, target)
|
||||||
|
config.targetContext[target] = targetContext
|
||||||
|
delete(appendTargetsSet, target)
|
||||||
|
|
||||||
|
queryKey, err := model.GenerateMeasureIdentifier(targetContext.measurement.DataSource)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error(ctx, "the append target generate redis query key identifier failed", "target", target, "error", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
pollingConfig := redisPollingConfig{
|
||||||
|
targetID: target,
|
||||||
|
queryKey: queryKey,
|
||||||
|
interval: targetContext.interval,
|
||||||
|
dataSize: int64(targetContext.measurement.Size),
|
||||||
|
}
|
||||||
|
go realTimeDataQueryFromRedis(ctx, pollingConfig, fanInChan, queryGStopChan)
|
||||||
|
|
||||||
|
logger.Info(ctx, "started new polling goroutine for appended target", "target", target, "interval", targetContext.interval)
|
||||||
}
|
}
|
||||||
|
|
||||||
for target := range targetSet {
|
allKeys := util.GetKeysFromSet(appendTargetsSet)
|
||||||
logger.Error(ctx, "append target: failed to find config for target, goroutine not started", "target", target)
|
if len(allKeys) > 0 {
|
||||||
|
logger.Warn(ctx, fmt.Sprintf("the following targets:%v start up fetch real time data process goroutine not started", allKeys))
|
||||||
|
clear(appendTargetsSet)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// removeTargets define func to stops running polling goroutines for targets that were removed
|
// removeTargets define func to stops running polling goroutines for targets that were removed
|
||||||
func removeTargets(ctx context.Context, stopChanMap map[string]chan struct{}, targetsToRemove []string) {
|
func removeTargets(ctx context.Context, config *RealTimeSubConfig, stopChanMap map[string]chan struct{}, removeTargets []string) {
|
||||||
for _, target := range targetsToRemove {
|
for _, target := range removeTargets {
|
||||||
|
targetContext, exist := config.targetContext[target]
|
||||||
|
if !exist {
|
||||||
|
logger.Warn(ctx, "removeTarget does not exist in targetContext map, skipping remove operation", "target", target)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
stopChan, exists := stopChanMap[target]
|
stopChan, exists := stopChanMap[target]
|
||||||
if !exists {
|
if !exists {
|
||||||
logger.Warn(ctx, "removeTarget was not running, skipping", "target", target)
|
logger.Warn(ctx, "removeTarget was not running, skipping remove operation", "target", target)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
close(stopChan)
|
close(stopChan)
|
||||||
delete(stopChanMap, target)
|
delete(stopChanMap, target)
|
||||||
|
delete(config.targetContext, target)
|
||||||
|
|
||||||
|
interval := targetContext.interval
|
||||||
|
measurementTargets, mesExist := config.measurements[interval]
|
||||||
|
if !mesExist {
|
||||||
|
logger.Warn(ctx, "targetContext exists but measurements is missing, cannot perform remove operation", "interval", interval, "target", target)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
measurementTargets = util.RemoveTargetsFromSliceSimple(measurementTargets, []string{target})
|
||||||
|
config.measurements[interval] = measurementTargets
|
||||||
|
|
||||||
logger.Info(ctx, "stopped polling goroutine for removed target", "target", target)
|
logger.Info(ctx, "stopped polling goroutine for removed target", "target", target)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -90,6 +90,8 @@ func RealTimeSubHandler(c *gin.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if request.Action == constants.SubStartAction && request.ClientID == "" {
|
if request.Action == constants.SubStartAction && request.ClientID == "" {
|
||||||
|
// TODO 调试输出,待删除
|
||||||
|
fmt.Println("00000")
|
||||||
subAction = request.Action
|
subAction = request.Action
|
||||||
id, err := uuid.NewV4()
|
id, err := uuid.NewV4()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -102,6 +104,8 @@ func RealTimeSubHandler(c *gin.Context) {
|
||||||
}
|
}
|
||||||
clientID = id.String()
|
clientID = id.String()
|
||||||
} else if request.Action == constants.SubStartAction && request.ClientID != "" {
|
} else if request.Action == constants.SubStartAction && request.ClientID != "" {
|
||||||
|
// TODO 调试输出,待删除
|
||||||
|
fmt.Println("11111")
|
||||||
subAction = constants.SubAppendAction
|
subAction = constants.SubAppendAction
|
||||||
clientID = request.ClientID
|
clientID = request.ClientID
|
||||||
} else if request.Action == constants.SubStopAction && request.ClientID != "" {
|
} else if request.Action == constants.SubStopAction && request.ClientID != "" {
|
||||||
|
|
@ -116,7 +120,7 @@ func RealTimeSubHandler(c *gin.Context) {
|
||||||
|
|
||||||
switch subAction {
|
switch subAction {
|
||||||
case constants.SubStartAction:
|
case constants.SubStartAction:
|
||||||
results, err := globalSubState.CreateConfig(c, tx, clientID, request.Components)
|
results, err := globalSubState.CreateConfig(c, tx, clientID, request.Measurements)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error(c, "create real time data subscription config failed", "error", err)
|
logger.Error(c, "create real time data subscription config failed", "error", err)
|
||||||
c.JSON(http.StatusOK, network.FailureResponse{
|
c.JSON(http.StatusOK, network.FailureResponse{
|
||||||
|
|
@ -140,7 +144,7 @@ func RealTimeSubHandler(c *gin.Context) {
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
case constants.SubStopAction:
|
case constants.SubStopAction:
|
||||||
results, err := globalSubState.RemoveTargets(c, clientID, request.Components)
|
results, err := globalSubState.RemoveTargets(c, clientID, request.Measurements)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error(c, "remove target to real time data subscription config failed", "error", err)
|
logger.Error(c, "remove target to real time data subscription config failed", "error", err)
|
||||||
c.JSON(http.StatusOK, network.FailureResponse{
|
c.JSON(http.StatusOK, network.FailureResponse{
|
||||||
|
|
@ -164,7 +168,9 @@ func RealTimeSubHandler(c *gin.Context) {
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
case constants.SubAppendAction:
|
case constants.SubAppendAction:
|
||||||
results, err := globalSubState.AppendTargets(c, tx, clientID, request.Components)
|
// TODO 调试输出,待删除
|
||||||
|
fmt.Println("22222")
|
||||||
|
results, err := globalSubState.AppendTargets(c, tx, clientID, request.Measurements)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error(c, "append target to real time data subscription config failed", "error", err)
|
logger.Error(c, "append target to real time data subscription config failed", "error", err)
|
||||||
c.JSON(http.StatusOK, network.FailureResponse{
|
c.JSON(http.StatusOK, network.FailureResponse{
|
||||||
|
|
@ -190,8 +196,8 @@ func RealTimeSubHandler(c *gin.Context) {
|
||||||
default:
|
default:
|
||||||
err := fmt.Errorf("%w: request action is %s", constants.ErrUnsupportedAction, request.Action)
|
err := fmt.Errorf("%w: request action is %s", constants.ErrUnsupportedAction, request.Action)
|
||||||
logger.Error(c, "unsupported action of real time data subscription request", "error", err)
|
logger.Error(c, "unsupported action of real time data subscription request", "error", err)
|
||||||
requestTargetsCount := processRealTimeRequestCount(request.Components)
|
requestTargetsCount := processRealTimeRequestCount(request.Measurements)
|
||||||
results := processRealTimeRequestTargets(request.Components, requestTargetsCount, err)
|
results := processRealTimeRequestTargets(request.Measurements, requestTargetsCount, err)
|
||||||
c.JSON(http.StatusOK, network.FailureResponse{
|
c.JSON(http.StatusOK, network.FailureResponse{
|
||||||
Code: http.StatusBadRequest,
|
Code: http.StatusBadRequest,
|
||||||
Msg: err.Error(),
|
Msg: err.Error(),
|
||||||
|
|
@ -204,17 +210,23 @@ func RealTimeSubHandler(c *gin.Context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// RealTimeSubComponent define struct of real time subscription component
|
// RealTimeSubMeasurement define struct of real time subscription measurement
|
||||||
type RealTimeSubComponent struct {
|
type RealTimeSubMeasurement struct {
|
||||||
targets []string
|
targets []string
|
||||||
targetParam map[string]*orm.Measurement
|
}
|
||||||
|
|
||||||
|
// TargetPollingContext define struct of real time pulling reverse context
|
||||||
|
type TargetPollingContext struct {
|
||||||
|
interval string
|
||||||
|
measurement *orm.Measurement
|
||||||
}
|
}
|
||||||
|
|
||||||
// RealTimeSubConfig define struct of real time subscription config
|
// RealTimeSubConfig define struct of real time subscription config
|
||||||
type RealTimeSubConfig struct {
|
type RealTimeSubConfig struct {
|
||||||
noticeChan chan *transportTargets
|
noticeChan chan *transportTargets
|
||||||
mutex sync.RWMutex
|
mutex sync.RWMutex
|
||||||
components map[string]*RealTimeSubComponent
|
measurements map[string][]string
|
||||||
|
targetContext map[string]*TargetPollingContext
|
||||||
}
|
}
|
||||||
|
|
||||||
// SharedSubState define struct of shared subscription state with mutex
|
// SharedSubState define struct of shared subscription state with mutex
|
||||||
|
|
@ -231,19 +243,19 @@ func NewSharedSubState() *SharedSubState {
|
||||||
}
|
}
|
||||||
|
|
||||||
// processAndValidateTargets define func to perform all database I/O operations in a lock-free state (eg,ParseDataIdentifierToken)
|
// processAndValidateTargets define func to perform all database I/O operations in a lock-free state (eg,ParseDataIdentifierToken)
|
||||||
func processAndValidateTargets(ctx context.Context, tx *gorm.DB, components []network.RealTimeComponentItem, allReqTargetNum int) (
|
func processAndValidateTargets(ctx context.Context, tx *gorm.DB, measurements []network.RealTimeMeasurementItem, allReqTargetNum int) (
|
||||||
[]network.TargetResult,
|
[]network.TargetResult, []string,
|
||||||
map[string]*RealTimeSubComponent,
|
map[string][]string,
|
||||||
[]string,
|
map[string]*TargetPollingContext,
|
||||||
) {
|
) {
|
||||||
targetProcessResults := make([]network.TargetResult, 0, allReqTargetNum)
|
targetProcessResults := make([]network.TargetResult, 0, allReqTargetNum)
|
||||||
newComponentsMap := make(map[string]*RealTimeSubComponent)
|
newMeasMap := make(map[string][]string)
|
||||||
successfulTargets := make([]string, 0, allReqTargetNum)
|
successfulTargets := make([]string, 0, allReqTargetNum)
|
||||||
|
newMeasContextMap := make(map[string]*TargetPollingContext)
|
||||||
|
|
||||||
for _, componentItem := range components {
|
for _, measurementItem := range measurements {
|
||||||
interval := componentItem.Interval
|
interval := measurementItem.Interval
|
||||||
|
for _, target := range measurementItem.Targets {
|
||||||
for _, target := range componentItem.Targets {
|
|
||||||
var targetResult network.TargetResult
|
var targetResult network.TargetResult
|
||||||
targetResult.ID = target
|
targetResult.ID = target
|
||||||
targetModel, err := database.ParseDataIdentifierToken(ctx, tx, target)
|
targetModel, err := database.ParseDataIdentifierToken(ctx, tx, target)
|
||||||
|
|
@ -259,38 +271,39 @@ func processAndValidateTargets(ctx context.Context, tx *gorm.DB, components []ne
|
||||||
targetProcessResults = append(targetProcessResults, targetResult)
|
targetProcessResults = append(targetProcessResults, targetResult)
|
||||||
successfulTargets = append(successfulTargets, target)
|
successfulTargets = append(successfulTargets, target)
|
||||||
|
|
||||||
if _, ok := newComponentsMap[interval]; !ok {
|
if _, ok := newMeasMap[interval]; !ok {
|
||||||
newComponentsMap[interval] = &RealTimeSubComponent{
|
newMeasMap[interval] = make([]string, 0, len(measurementItem.Targets))
|
||||||
targets: make([]string, 0, len(componentItem.Targets)),
|
|
||||||
targetParam: make(map[string]*orm.Measurement),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
comp := newComponentsMap[interval]
|
meas := newMeasMap[interval]
|
||||||
comp.targets = append(comp.targets, target)
|
meas = append(meas, target)
|
||||||
comp.targetParam[target] = targetModel.GetMeasurementInfo()
|
newMeasContextMap[target] = &TargetPollingContext{
|
||||||
|
interval: interval,
|
||||||
|
measurement: targetModel.GetMeasurementInfo(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return targetProcessResults, newComponentsMap, successfulTargets
|
return targetProcessResults, successfulTargets, newMeasMap, newMeasContextMap
|
||||||
}
|
}
|
||||||
|
|
||||||
// mergeComponents define func to merge newComponentsMap into existingComponentsMap
|
// mergeMeasurements define func to merge newMeasurementsMap into existingMeasurementsMap
|
||||||
func mergeComponents(existingComponents map[string]*RealTimeSubComponent, newComponents map[string]*RealTimeSubComponent) {
|
func mergeMeasurements(config *RealTimeSubConfig, newMeasurements map[string][]string, newMeasurementsContextMap map[string]*TargetPollingContext) {
|
||||||
for interval, newComp := range newComponents {
|
for interval, newMeas := range newMeasurements {
|
||||||
if existingComp, ok := existingComponents[interval]; ok {
|
if existingMeas, ok := config.measurements[interval]; ok {
|
||||||
existingComp.targets = append(existingComp.targets, newComp.targets...)
|
existingMeas = append(existingMeas, newMeas...)
|
||||||
maps.Copy(existingComp.targetParam, newComp.targetParam)
|
config.measurements[interval] = existingMeas
|
||||||
|
maps.Copy(config.targetContext, newMeasurementsContextMap)
|
||||||
} else {
|
} else {
|
||||||
existingComponents[interval] = newComp
|
config.measurements[interval] = newMeas
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateConfig define function to create config in SharedSubState
|
// CreateConfig define function to create config in SharedSubState
|
||||||
func (s *SharedSubState) CreateConfig(ctx context.Context, tx *gorm.DB, clientID string, components []network.RealTimeComponentItem) ([]network.TargetResult, error) {
|
func (s *SharedSubState) CreateConfig(ctx context.Context, tx *gorm.DB, clientID string, components []network.RealTimeMeasurementItem) ([]network.TargetResult, error) {
|
||||||
requestTargetsCount := processRealTimeRequestCount(components)
|
requestTargetsCount := processRealTimeRequestCount(components)
|
||||||
targetProcessResults, newComponentsMap, _ := processAndValidateTargets(ctx, tx, components, requestTargetsCount)
|
targetProcessResults, _, newMeasurementsMap, MeasurementInfos := processAndValidateTargets(ctx, tx, components, requestTargetsCount)
|
||||||
|
fmt.Println(MeasurementInfos)
|
||||||
s.globalMutex.Lock()
|
s.globalMutex.Lock()
|
||||||
if _, exist := s.subMap[clientID]; exist {
|
if _, exist := s.subMap[clientID]; exist {
|
||||||
s.globalMutex.Unlock()
|
s.globalMutex.Unlock()
|
||||||
|
|
@ -300,8 +313,9 @@ func (s *SharedSubState) CreateConfig(ctx context.Context, tx *gorm.DB, clientID
|
||||||
}
|
}
|
||||||
|
|
||||||
config := &RealTimeSubConfig{
|
config := &RealTimeSubConfig{
|
||||||
noticeChan: make(chan *transportTargets),
|
noticeChan: make(chan *transportTargets, constants.NoticeChanCap),
|
||||||
components: newComponentsMap, // 直接使用预构建的 Map
|
measurements: newMeasurementsMap,
|
||||||
|
targetContext: MeasurementInfos,
|
||||||
}
|
}
|
||||||
s.subMap[clientID] = config
|
s.subMap[clientID] = config
|
||||||
s.globalMutex.Unlock()
|
s.globalMutex.Unlock()
|
||||||
|
|
@ -309,24 +323,23 @@ func (s *SharedSubState) CreateConfig(ctx context.Context, tx *gorm.DB, clientID
|
||||||
}
|
}
|
||||||
|
|
||||||
// AppendTargets define function to append targets in SharedSubState
|
// AppendTargets define function to append targets in SharedSubState
|
||||||
func (s *SharedSubState) AppendTargets(ctx context.Context, tx *gorm.DB, clientID string, components []network.RealTimeComponentItem) ([]network.TargetResult, error) {
|
func (s *SharedSubState) AppendTargets(ctx context.Context, tx *gorm.DB, clientID string, components []network.RealTimeMeasurementItem) ([]network.TargetResult, error) {
|
||||||
requestTargetsCount := processRealTimeRequestCount(components)
|
requestTargetsCount := processRealTimeRequestCount(components)
|
||||||
targetProcessResults := make([]network.TargetResult, 0, requestTargetsCount)
|
|
||||||
|
|
||||||
s.globalMutex.RLock()
|
s.globalMutex.RLock()
|
||||||
config, exist := s.subMap[clientID]
|
config, exist := s.subMap[clientID]
|
||||||
|
s.globalMutex.RUnlock()
|
||||||
|
|
||||||
if !exist {
|
if !exist {
|
||||||
s.globalMutex.RUnlock()
|
|
||||||
err := fmt.Errorf("clientID %s not found. use CreateConfig to start a new config", clientID)
|
err := fmt.Errorf("clientID %s not found. use CreateConfig to start a new config", clientID)
|
||||||
logger.Error(ctx, "clientID not found. use CreateConfig to start a new config", "error", err)
|
logger.Error(ctx, "clientID not found. use CreateConfig to start a new config", "error", err)
|
||||||
return processRealTimeRequestTargets(components, requestTargetsCount, err), err
|
return processRealTimeRequestTargets(components, requestTargetsCount, err), err
|
||||||
}
|
}
|
||||||
s.globalMutex.RUnlock()
|
|
||||||
|
|
||||||
targetProcessResults, newComponentsMap, successfulTargets := processAndValidateTargets(ctx, tx, components, requestTargetsCount)
|
targetProcessResults, successfulTargets, newMeasMap, newMeasContextMap := processAndValidateTargets(ctx, tx, components, requestTargetsCount)
|
||||||
|
|
||||||
config.mutex.Lock()
|
config.mutex.Lock()
|
||||||
mergeComponents(config.components, newComponentsMap)
|
mergeMeasurements(config, newMeasMap, newMeasContextMap)
|
||||||
config.mutex.Unlock()
|
config.mutex.Unlock()
|
||||||
|
|
||||||
if len(successfulTargets) > 0 {
|
if len(successfulTargets) > 0 {
|
||||||
|
|
@ -337,53 +350,13 @@ func (s *SharedSubState) AppendTargets(ctx context.Context, tx *gorm.DB, clientI
|
||||||
config.noticeChan <- transportTargets
|
config.noticeChan <- transportTargets
|
||||||
}
|
}
|
||||||
|
|
||||||
transportTargets := &transportTargets{
|
|
||||||
OperationType: constants.OpAppend,
|
|
||||||
Targets: make([]string, requestTargetsCount),
|
|
||||||
}
|
|
||||||
config.mutex.Lock()
|
|
||||||
for _, componentItem := range components {
|
|
||||||
interval := componentItem.Interval
|
|
||||||
for _, target := range componentItem.Targets {
|
|
||||||
var targetResult network.TargetResult
|
|
||||||
targetResult.ID = target
|
|
||||||
|
|
||||||
targetModel, err := database.ParseDataIdentifierToken(ctx, tx, 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
|
|
||||||
targetProcessResults = append(targetProcessResults, targetResult)
|
|
||||||
if comp, ok := config.components[interval]; !ok {
|
|
||||||
targets := make([]string, 0, len(componentItem.Targets))
|
|
||||||
targetParam := make(map[string]*orm.Measurement)
|
|
||||||
targetParam[target] = targetModel.GetMeasurementInfo()
|
|
||||||
config.components[interval] = &RealTimeSubComponent{
|
|
||||||
targets: append(targets, target),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
comp.targets = append(comp.targets, target)
|
|
||||||
comp.targetParam[target] = targetModel.GetMeasurementInfo()
|
|
||||||
}
|
|
||||||
transportTargets.Targets = append(transportTargets.Targets, target)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
config.mutex.Unlock()
|
|
||||||
|
|
||||||
config.noticeChan <- transportTargets
|
|
||||||
return targetProcessResults, nil
|
return targetProcessResults, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpsertTargets define function to upsert targets in SharedSubState
|
// UpsertTargets define function to upsert targets in SharedSubState
|
||||||
func (s *SharedSubState) UpsertTargets(ctx context.Context, tx *gorm.DB, clientID string, components []network.RealTimeComponentItem) ([]network.TargetResult, error) {
|
func (s *SharedSubState) UpsertTargets(ctx context.Context, tx *gorm.DB, clientID string, measurements []network.RealTimeMeasurementItem) ([]network.TargetResult, error) {
|
||||||
requestTargetsCount := processRealTimeRequestCount(components)
|
requestTargetsCount := processRealTimeRequestCount(measurements)
|
||||||
targetProcessResults, newComponentsMap, successfulTargets := processAndValidateTargets(ctx, tx, components, requestTargetsCount)
|
targetProcessResults, successfulTargets, newMeasMap, newMeasContextMap := processAndValidateTargets(ctx, tx, measurements, requestTargetsCount)
|
||||||
|
|
||||||
s.globalMutex.RLock()
|
s.globalMutex.RLock()
|
||||||
config, exist := s.subMap[clientID]
|
config, exist := s.subMap[clientID]
|
||||||
|
|
@ -393,21 +366,21 @@ func (s *SharedSubState) UpsertTargets(ctx context.Context, tx *gorm.DB, clientI
|
||||||
if exist {
|
if exist {
|
||||||
opType = constants.OpUpdate
|
opType = constants.OpUpdate
|
||||||
config.mutex.Lock()
|
config.mutex.Lock()
|
||||||
mergeComponents(config.components, newComponentsMap)
|
mergeMeasurements(config, newMeasMap, newMeasContextMap)
|
||||||
config.mutex.Unlock()
|
config.mutex.Unlock()
|
||||||
} else {
|
} else {
|
||||||
opType = constants.OpAppend
|
opType = constants.OpAppend
|
||||||
s.globalMutex.Lock()
|
s.globalMutex.Lock()
|
||||||
if config, exist = s.subMap[clientID]; !exist {
|
if config, exist = s.subMap[clientID]; !exist {
|
||||||
config = &RealTimeSubConfig{
|
config = &RealTimeSubConfig{
|
||||||
noticeChan: make(chan *transportTargets),
|
noticeChan: make(chan *transportTargets, constants.NoticeChanCap),
|
||||||
components: newComponentsMap,
|
measurements: newMeasMap,
|
||||||
}
|
}
|
||||||
s.subMap[clientID] = config
|
s.subMap[clientID] = config
|
||||||
} else {
|
} else {
|
||||||
s.globalMutex.Unlock()
|
s.globalMutex.Unlock()
|
||||||
config.mutex.Lock()
|
config.mutex.Lock()
|
||||||
mergeComponents(config.components, newComponentsMap)
|
mergeMeasurements(config, newMeasMap, newMeasContextMap)
|
||||||
config.mutex.Unlock()
|
config.mutex.Unlock()
|
||||||
}
|
}
|
||||||
s.globalMutex.Unlock()
|
s.globalMutex.Unlock()
|
||||||
|
|
@ -424,8 +397,8 @@ func (s *SharedSubState) UpsertTargets(ctx context.Context, tx *gorm.DB, clientI
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveTargets define function to remove targets in SharedSubState
|
// RemoveTargets define function to remove targets in SharedSubState
|
||||||
func (s *SharedSubState) RemoveTargets(ctx context.Context, clientID string, components []network.RealTimeComponentItem) ([]network.TargetResult, error) {
|
func (s *SharedSubState) RemoveTargets(ctx context.Context, clientID string, measurements []network.RealTimeMeasurementItem) ([]network.TargetResult, error) {
|
||||||
requestTargetsCount := processRealTimeRequestCount(components)
|
requestTargetsCount := processRealTimeRequestCount(measurements)
|
||||||
targetProcessResults := make([]network.TargetResult, 0, requestTargetsCount)
|
targetProcessResults := make([]network.TargetResult, 0, requestTargetsCount)
|
||||||
|
|
||||||
s.globalMutex.RLock()
|
s.globalMutex.RLock()
|
||||||
|
|
@ -434,24 +407,24 @@ func (s *SharedSubState) RemoveTargets(ctx context.Context, clientID string, com
|
||||||
s.globalMutex.RUnlock()
|
s.globalMutex.RUnlock()
|
||||||
err := fmt.Errorf("clientID %s not found", clientID)
|
err := fmt.Errorf("clientID %s not found", clientID)
|
||||||
logger.Error(ctx, "clientID not found in remove targets operation", "error", err)
|
logger.Error(ctx, "clientID not found in remove targets operation", "error", err)
|
||||||
return processRealTimeRequestTargets(components, requestTargetsCount, err), err
|
return processRealTimeRequestTargets(measurements, requestTargetsCount, err), err
|
||||||
}
|
}
|
||||||
s.globalMutex.RUnlock()
|
s.globalMutex.RUnlock()
|
||||||
|
|
||||||
var shouldRemoveClient bool
|
var shouldRemoveClient bool
|
||||||
// components is the list of items to be removed passed in the request
|
// measurements is the list of items to be removed passed in the request
|
||||||
transportTargets := &transportTargets{
|
transportTargets := &transportTargets{
|
||||||
OperationType: constants.OpRemove,
|
OperationType: constants.OpRemove,
|
||||||
Targets: make([]string, 0, requestTargetsCount),
|
Targets: make([]string, 0, requestTargetsCount),
|
||||||
}
|
}
|
||||||
config.mutex.Lock()
|
config.mutex.Lock()
|
||||||
for _, compent := range components {
|
for _, measurement := range measurements {
|
||||||
interval := compent.Interval
|
interval := measurement.Interval
|
||||||
// comp is the locally running listener configuration
|
// meas is the locally running listener configuration
|
||||||
comp, compExist := config.components[interval]
|
measTargets, measExist := config.measurements[interval]
|
||||||
if !compExist {
|
if !measExist {
|
||||||
logger.Error(ctx, fmt.Sprintf("component with interval %s not found under clientID %s", interval, clientID), "clientID", clientID, "interval", interval)
|
logger.Error(ctx, fmt.Sprintf("measurement with interval %s not found under clientID %s", interval, clientID), "clientID", clientID, "interval", interval)
|
||||||
for _, target := range compent.Targets {
|
for _, target := range measTargets {
|
||||||
targetResult := network.TargetResult{
|
targetResult := network.TargetResult{
|
||||||
ID: target,
|
ID: target,
|
||||||
Code: constants.CancelSubFailedCode,
|
Code: constants.CancelSubFailedCode,
|
||||||
|
|
@ -463,12 +436,12 @@ func (s *SharedSubState) RemoveTargets(ctx context.Context, clientID string, com
|
||||||
}
|
}
|
||||||
|
|
||||||
targetsToRemoveMap := make(map[string]struct{})
|
targetsToRemoveMap := make(map[string]struct{})
|
||||||
for _, target := range compent.Targets {
|
for _, target := range measurement.Targets {
|
||||||
targetsToRemoveMap[target] = struct{}{}
|
targetsToRemoveMap[target] = struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
var newTargets []string
|
var newTargets []string
|
||||||
for _, existingTarget := range comp.targets {
|
for _, existingTarget := range measTargets {
|
||||||
if _, found := targetsToRemoveMap[existingTarget]; !found {
|
if _, found := targetsToRemoveMap[existingTarget]; !found {
|
||||||
newTargets = append(newTargets, existingTarget)
|
newTargets = append(newTargets, existingTarget)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -480,16 +453,16 @@ func (s *SharedSubState) RemoveTargets(ctx context.Context, clientID string, com
|
||||||
}
|
}
|
||||||
targetProcessResults = append(targetProcessResults, targetResult)
|
targetProcessResults = append(targetProcessResults, targetResult)
|
||||||
delete(targetsToRemoveMap, existingTarget)
|
delete(targetsToRemoveMap, existingTarget)
|
||||||
delete(comp.targetParam, existingTarget)
|
delete(config.targetContext, existingTarget)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
comp.targets = newTargets
|
measTargets = newTargets
|
||||||
|
|
||||||
if len(comp.targets) == 0 {
|
if len(measTargets) == 0 {
|
||||||
delete(config.components, interval)
|
delete(config.measurements, interval)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(config.components) == 0 {
|
if len(config.measurements) == 0 {
|
||||||
shouldRemoveClient = true
|
shouldRemoveClient = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -511,7 +484,7 @@ func (s *SharedSubState) RemoveTargets(ctx context.Context, clientID string, com
|
||||||
|
|
||||||
if shouldRemoveClient {
|
if shouldRemoveClient {
|
||||||
s.globalMutex.Lock()
|
s.globalMutex.Lock()
|
||||||
if currentConfig, exist := s.subMap[clientID]; exist && len(currentConfig.components) == 0 {
|
if currentConfig, exist := s.subMap[clientID]; exist && len(currentConfig.measurements) == 0 {
|
||||||
delete(s.subMap, clientID)
|
delete(s.subMap, clientID)
|
||||||
}
|
}
|
||||||
s.globalMutex.Unlock()
|
s.globalMutex.Unlock()
|
||||||
|
|
@ -533,7 +506,7 @@ func (s *SharedSubState) Get(clientID string) (*RealTimeSubConfig, bool) {
|
||||||
|
|
||||||
// TODO 增加一个update 函数用来更新 interval
|
// TODO 增加一个update 函数用来更新 interval
|
||||||
|
|
||||||
func processRealTimeRequestCount(components []network.RealTimeComponentItem) int {
|
func processRealTimeRequestCount(components []network.RealTimeMeasurementItem) int {
|
||||||
totalTargetsCount := 0
|
totalTargetsCount := 0
|
||||||
for _, compItem := range components {
|
for _, compItem := range components {
|
||||||
totalTargetsCount += len(compItem.Targets)
|
totalTargetsCount += len(compItem.Targets)
|
||||||
|
|
@ -541,7 +514,7 @@ func processRealTimeRequestCount(components []network.RealTimeComponentItem) int
|
||||||
return totalTargetsCount
|
return totalTargetsCount
|
||||||
}
|
}
|
||||||
|
|
||||||
func processRealTimeRequestTargets(components []network.RealTimeComponentItem, targetCount int, err error) []network.TargetResult {
|
func processRealTimeRequestTargets(components []network.RealTimeMeasurementItem, targetCount int, err error) []network.TargetResult {
|
||||||
targetProcessResults := make([]network.TargetResult, 0, targetCount)
|
targetProcessResults := make([]network.TargetResult, 0, targetCount)
|
||||||
for _, componentItem := range components {
|
for _, componentItem := range components {
|
||||||
for _, target := range componentItem.Targets {
|
for _, target := range componentItem.Targets {
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,10 @@ type RealTimeQueryRequest struct {
|
||||||
// enum: [start, stop]
|
// enum: [start, stop]
|
||||||
Action string `json:"action" example:"start" description:"请求的操作,例如 start/stop"`
|
Action string `json:"action" example:"start" description:"请求的操作,例如 start/stop"`
|
||||||
// TODO 增加monitorID的example值说明
|
// TODO 增加monitorID的example值说明
|
||||||
MonitorID string `json:"monitor_id" example:"xxxx" description:"用于标识不同client的监控请求ID"`
|
ClientID string `json:"client_id" example:"xxxx" description:"用于标识不同client的监控请求ID"`
|
||||||
|
|
||||||
// required: true
|
// required: true
|
||||||
Components []RealTimeComponentItem `json:"components" description:"定义不同的数据采集策略和目标"`
|
Measurements []RealTimeMeasurementItem `json:"measurements" description:"定义不同的数据采集策略和目标"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// RealTimeSubRequest define struct of real time data subscription request
|
// RealTimeSubRequest define struct of real time data subscription request
|
||||||
|
|
@ -20,11 +20,11 @@ type RealTimeSubRequest struct {
|
||||||
Action string `json:"action" example:"start" description:"请求的操作,例如 start/stop"`
|
Action string `json:"action" example:"start" description:"请求的操作,例如 start/stop"`
|
||||||
ClientID string `json:"client_id" example:"5d72f2d9-e33a-4f1b-9c76-88a44b9a953e" description:"用于标识不同client的监控请求ID"`
|
ClientID string `json:"client_id" example:"5d72f2d9-e33a-4f1b-9c76-88a44b9a953e" description:"用于标识不同client的监控请求ID"`
|
||||||
// required: true
|
// required: true
|
||||||
Components []RealTimeComponentItem `json:"components" description:"定义不同的数据采集策略和目标"`
|
Measurements []RealTimeMeasurementItem `json:"measurements" description:"定义不同的数据采集策略和目标"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// RealTimeComponentItem define struct of real time component item
|
// RealTimeMeasurementItem define struct of real time measurement item
|
||||||
type RealTimeComponentItem struct {
|
type RealTimeMeasurementItem struct {
|
||||||
Interval string `json:"interval" example:"1" description:"数据采集的时间间隔(秒)"`
|
Interval string `json:"interval" example:"1" description:"数据采集的时间间隔(秒)"`
|
||||||
Targets []string `json:"targets" example:"[\"grid1.zone1.station1.ns1.tag1.transformfeeder1_220.I_A_rms\",\"grid1.zone1.station1.ns1.tag1.transformfeeder1_220.I_B_rms\"]" description:"需要采集数据的测点或标签名称列表"`
|
Targets []string `json:"targets" example:"[\"grid1.zone1.station1.ns1.tag1.transformfeeder1_220.I_A_rms\",\"grid1.zone1.station1.ns1.tag1.transformfeeder1_220.I_B_rms\"]" description:"需要采集数据的测点或标签名称列表"`
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
// Package util provide some utility functions
|
||||||
|
package util
|
||||||
|
|
||||||
|
// GetKeysFromSet define func to get all keys from a map[string]struct{}
|
||||||
|
func GetKeysFromSet(set map[string]struct{}) []string {
|
||||||
|
keys := make([]string, 0, len(set))
|
||||||
|
for key := range set {
|
||||||
|
keys = append(keys, key)
|
||||||
|
}
|
||||||
|
return keys
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
// Package util provide some utility functions
|
||||||
|
package util
|
||||||
|
|
||||||
|
// RemoveTargetsFromSliceSimple define func to remove targets from a slice of strings
|
||||||
|
func RemoveTargetsFromSliceSimple(targetsSlice []string, targetsToRemove []string) []string {
|
||||||
|
targetsToRemoveSet := make(map[string]struct{}, len(targetsToRemove))
|
||||||
|
for _, target := range targetsToRemove {
|
||||||
|
targetsToRemoveSet[target] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := len(targetsSlice) - 1; i >= 0; i-- {
|
||||||
|
if _, found := targetsToRemoveSet[targetsSlice[i]]; found {
|
||||||
|
targetsSlice[i] = targetsSlice[len(targetsSlice)-1]
|
||||||
|
targetsSlice = targetsSlice[:len(targetsSlice)-1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return targetsSlice
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue