Skip to content

Commit

Permalink
fix: Handle LastConnected on add/remove cache
Browse files Browse the repository at this point in the history
- Register/Unregister LastConnected on add/remove device cache.
- Update LastConnected for async reading

Signed-off-by: Lindsey Cheng <beckysocute@gmail.com>
  • Loading branch information
lindseysimple committed Sep 20, 2023
1 parent 99bffe5 commit af69577
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 21 deletions.
67 changes: 46 additions & 21 deletions internal/cache/devices.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@ import (
"sync"
"time"

"github.com/edgexfoundry/device-sdk-go/v3/internal/container"

bootstrapContainer "github.com/edgexfoundry/go-mod-bootstrap/v3/bootstrap/container"
bootstrapInterfaces "github.com/edgexfoundry/go-mod-bootstrap/v3/bootstrap/interfaces"
"github.com/edgexfoundry/go-mod-bootstrap/v3/di"
"github.com/edgexfoundry/go-mod-core-contracts/v3/clients/logger"
"github.com/edgexfoundry/go-mod-core-contracts/v3/common"
"github.com/edgexfoundry/go-mod-core-contracts/v3/errors"
"github.com/edgexfoundry/go-mod-core-contracts/v3/models"

Expand Down Expand Up @@ -45,6 +46,7 @@ type DeviceCache interface {
type deviceCache struct {
deviceMap map[string]*models.Device // key is Device name
mutex sync.RWMutex
dic *di.Container
lastConnected map[string]gometrics.Gauge
}

Expand All @@ -55,31 +57,44 @@ func newDeviceCache(devices []models.Device, dic *di.Container) DeviceCache {
dMap[d.Name] = &devices[i]
}

dc = &deviceCache{deviceMap: dMap}
metricsManager := bootstrapContainer.MetricsManagerFrom(dic.Get)
lc := bootstrapContainer.LoggingClientFrom(dic.Get)
lastConnectedMetrics := registerMetric(metricsManager, devices, lc)
dc = &deviceCache{deviceMap: dMap, dic: dic}
lastConnectedMetrics := make(map[string]gometrics.Gauge)
for _, d := range devices {
deviceMetric := gometrics.NewGauge()
registerMetric(d.Name, deviceMetric, dic)
lastConnectedMetrics[d.Name] = deviceMetric
}
dc.lastConnected = lastConnectedMetrics

return dc
}

func registerMetric(metricManager bootstrapInterfaces.MetricsManager, devices []models.Device, lc logger.LoggingClient) map[string]gometrics.Gauge {
lastConnected := make(map[string]gometrics.Gauge)
metricName := lastConnectedPrefix + ""
for _, d := range devices {
deviceMetric := gometrics.NewGauge()
registeredName := strings.Replace(metricName, deviceNameText, d.Name, 1)

err := metricManager.Register(registeredName, deviceMetric, nil)
if err != nil {
lc.Warnf("Unable to register %s metric. Metric will not be reported : %s", registeredName, err.Error())
} else {
lc.Infof("%s metric has been registered and will be reported (if enabled)", registeredName)
lastConnected[d.Name] = deviceMetric
}
func registerMetric(deviceName string, metric interface{}, dic *di.Container) {
metricsManager := bootstrapContainer.MetricsManagerFrom(dic.Get)
lc := bootstrapContainer.LoggingClientFrom(dic.Get)
configuration := container.ConfigurationFrom(dic.Get)
if configuration.Service.EnableNameFieldEscape {
deviceName = common.URLEncode(deviceName)
}
return lastConnected
registeredName := strings.Replace(lastConnectedPrefix, deviceNameText, deviceName, 1)

err := metricsManager.Register(registeredName, metric, map[string]string{"device": deviceName})
if err != nil {
lc.Warnf("Unable to register %s metric. Metric will not be reported : %s", registeredName, err.Error())
} else {
lc.Infof("%s metric has been registered and will be reported (if enabled)", registeredName)
}
}

func unregisterMetric(deviceName string, dic *di.Container) {
metricsManager := bootstrapContainer.MetricsManagerFrom(dic.Get)
configuration := container.ConfigurationFrom(dic.Get)
if configuration.Service.EnableNameFieldEscape {
deviceName = common.URLEncode(deviceName)
}
registeredName := strings.Replace(lastConnectedPrefix, deviceNameText, deviceName, 1)

metricsManager.Unregister(registeredName)
}

// ForName returns a Device with the given device name.
Expand Down Expand Up @@ -124,6 +139,11 @@ func (d *deviceCache) add(device models.Device) errors.EdgeX {
}

d.deviceMap[device.Name] = &device

// register the lastConnected metric for the new added device
deviceMetric := gometrics.NewGauge()
registerMetric(device.Name, deviceMetric, d.dic)
d.lastConnected[device.Name] = deviceMetric
return nil
}

Expand Down Expand Up @@ -154,6 +174,11 @@ func (d *deviceCache) removeByName(name string) errors.EdgeX {
}

delete(d.deviceMap, name)

// unregister the lastConnected metric for the removed device
unregisterMetric(name, d.dic)
delete(d.lastConnected, name)

return nil
}

Expand Down
14 changes: 14 additions & 0 deletions internal/cache/devices_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,12 @@ package cache
import (
"testing"

"github.com/edgexfoundry/device-sdk-go/v3/internal/config"
"github.com/edgexfoundry/device-sdk-go/v3/internal/container"

bootstrapContainer "github.com/edgexfoundry/go-mod-bootstrap/v3/bootstrap/container"
"github.com/edgexfoundry/go-mod-bootstrap/v3/bootstrap/interfaces/mocks"
bootstrapConfig "github.com/edgexfoundry/go-mod-bootstrap/v3/config"
"github.com/edgexfoundry/go-mod-bootstrap/v3/di"
"github.com/edgexfoundry/go-mod-core-contracts/v3/clients/logger"
"github.com/edgexfoundry/go-mod-core-contracts/v3/models"
Expand Down Expand Up @@ -42,6 +46,16 @@ func mockDic() *di.Container {
bootstrapContainer.LoggingClientInterfaceName: func(get di.Get) interface{} {
return logger.NewMockClient()
},
container.ConfigurationName: func(get di.Get) interface{} {
return &config.ConfigurationStruct{
Writable: config.WritableInfo{
LogLevel: "INFO",
},
Service: bootstrapConfig.ServiceInfo{
EnableNameFieldEscape: true,
},
}
},
})
}

Expand Down
3 changes: 3 additions & 0 deletions pkg/service/async.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ func (s *deviceService) sendAsyncValues(acv *sdkModels.AsyncValues, working chan
<-working
}()

// Update the LastConnected metric in deviceCache
cache.Devices().SetLastConnectedByName(acv.DeviceName)

if len(acv.CommandValues) == 0 {
s.lc.Error("Skip sending AsyncValues because the CommandValues is empty.")
return
Expand Down

0 comments on commit af69577

Please sign in to comment.