chore. fix memory leak with Cacheable objects (#5782)

This commit is contained in:
nename0 2023-01-14 15:16:53 +01:00 • committed by GitHub
parent 9cc6d3f3bd
commit 6d7006fe65
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 63 additions and 64 deletions

View file

@ -22,8 +22,8 @@ type DaheimLaden struct {
idTag string
token string
transactionID int32
statusG func() (daheimladen.GetLatestStatus, error)
meterG func() (daheimladen.GetLatestMeterValueResponse, error)
statusCache provider.Cacheable[daheimladen.GetLatestStatus]
meterCache provider.Cacheable[daheimladen.GetLatestMeterValueResponse]
cache time.Duration
}
@ -67,29 +67,30 @@ func NewDaheimLaden(token, stationID string, cache time.Duration) (*DaheimLaden,
Base: c.Client.Transport,
}
c.reset()
c.statusCache = provider.ResettableCached(func() (daheimladen.GetLatestStatus, error) {
var res daheimladen.GetLatestStatus
err := c.GetJSON(fmt.Sprintf("%s/cs/%s/status", daheimladen.BASE_URL, c.stationID), &res)
return res, err
}, c.cache)
c.meterCache = provider.ResettableCached(func() (daheimladen.GetLatestMeterValueResponse, error) {
var res daheimladen.GetLatestMeterValueResponse
err := c.GetJSON(fmt.Sprintf("%s/cs/%s/metervalue", daheimladen.BASE_URL, c.stationID), &res)
return res, err
}, c.cache)
return c, nil
}
// reset cache
func (c *DaheimLaden) reset() {
c.statusG = provider.Cached(func() (daheimladen.GetLatestStatus, error) {
var res daheimladen.GetLatestStatus
err := c.GetJSON(fmt.Sprintf("%s/cs/%s/status", daheimladen.BASE_URL, c.stationID), &res)
return res, err
}, c.cache)
c.meterG = provider.Cached(func() (daheimladen.GetLatestMeterValueResponse, error) {
var res daheimladen.GetLatestMeterValueResponse
err := c.GetJSON(fmt.Sprintf("%s/cs/%s/metervalue", daheimladen.BASE_URL, c.stationID), &res)
return res, err
}, c.cache)
c.statusCache.Reset()
c.meterCache.Reset()
}
// Status implements the api.Charger interface
func (c *DaheimLaden) Status() (api.ChargeStatus, error) {
res, err := c.statusG()
res, err := c.statusCache.Get()
if err != nil {
return api.StatusNone, err
}
@ -111,7 +112,7 @@ func (c *DaheimLaden) Status() (api.ChargeStatus, error) {
// Enabled implements the api.Charger interface
func (c *DaheimLaden) Enabled() (bool, error) {
res, err := c.statusG()
res, err := c.statusCache.Get()
return res.Status == string(daheimladen.CHARGING), err
}
@ -192,7 +193,7 @@ var _ api.Meter = (*DaheimLaden)(nil)
// CurrentPower implements the api.Meter interface
func (c *DaheimLaden) CurrentPower() (float64, error) {
res, err := c.meterG()
res, err := c.meterCache.Get()
return float64(res.ActivePowerImport * 1e3), err
}
@ -200,7 +201,7 @@ var _ api.MeterEnergy = (*DaheimLaden)(nil)
// TotalEnergy implements the api.MeterMeterEnergy interface
func (c *DaheimLaden) TotalEnergy() (float64, error) {
res, err := c.meterG()
res, err := c.meterCache.Get()
return float64(res.EnergyActiveImportRegister), err
}
@ -208,6 +209,6 @@ var _ api.PhaseCurrents = (*DaheimLaden)(nil)
// Currents implements the api.PhaseCurrents interface
func (c *DaheimLaden) Currents() (float64, float64, float64, error) {
res, err := c.meterG()
res, err := c.meterCache.Get()
return float64(res.CurrentImportPhaseL1), float64(res.CurrentImportPhaseL2), float64(res.CurrentImportPhaseL3), err
}

View file

@ -21,8 +21,8 @@ func init() {
type MyStrom struct {
conn *mystrom.Connection
*switchSocket
cache time.Duration
reportG func() (mystrom.Report, error)
cache time.Duration
reportCache provider.Cacheable[mystrom.Report]
}
// NewMyStromFromConfig creates a myStrom charger from generic config
@ -45,21 +45,21 @@ func NewMyStromFromConfig(other map[string]interface{}) (api.Charger, error) {
}
c.switchSocket = NewSwitchSocket(c.Enabled, c.conn.CurrentPower, cc.StandbyPower)
c.reportG = provider.Cached(c.conn.Report, c.cache)
c.reportCache = provider.ResettableCached(c.conn.Report, c.cache)
return c, nil
}
// Enabled implements the api.Charger interface
func (c *MyStrom) Enabled() (bool, error) {
res, err := c.reportG()
res, err := c.reportCache.Get()
return res.Relay, err
}
// Enable implements the api.Charger interface
func (c *MyStrom) Enable(enable bool) error {
// reset cache
c.reportG = provider.Cached(c.conn.Report, c.cache)
c.reportCache.Reset()
onoff := map[bool]int{false: 0, true: 1}
return c.conn.Request(fmt.Sprintf("relay?state=%d", onoff[enable]))

View file

@ -37,12 +37,12 @@ import (
// smaevchager charger implementation
type Smaevcharger struct {
*request.Helper
log *util.Logger
uri string // 192.168.XXX.XXX
cache time.Duration
oldstate float64
measurementG func() ([]smaevcharger.Measurements, error)
parameterG func() ([]smaevcharger.Parameters, error)
log *util.Logger
uri string // 192.168.XXX.XXX
cache time.Duration
oldstate float64
measurementCache provider.Cacheable[[]smaevcharger.Measurements]
parameterCache provider.Cacheable[[]smaevcharger.Parameters]
}
func init() {
@ -95,7 +95,8 @@ func NewSmaevcharger(uri, user, password string, cache time.Duration) (api.Charg
}
// setup cached values
wb.reset()
wb.measurementCache = provider.ResettableCached(wb._measurementData, wb.cache)
wb.parameterCache = provider.ResettableCached(wb._parameterData, wb.cache)
ts, err := smaevcharger.TokenSource(log, wb.uri, user, password)
if err != nil {
@ -267,8 +268,8 @@ func (wb *Smaevcharger) Currents() (float64, float64, float64, error) {
// reset cache
func (wb *Smaevcharger) reset() {
wb.measurementG = provider.Cached(wb._measurementData, wb.cache)
wb.parameterG = provider.Cached(wb._parameterData, wb.cache)
wb.measurementCache.Reset()
wb.parameterCache.Reset()
}
func (wb *Smaevcharger) _measurementData() ([]smaevcharger.Measurements, error) {
@ -298,7 +299,7 @@ func (wb *Smaevcharger) _parameterData() ([]smaevcharger.Parameters, error) {
}
func (wb *Smaevcharger) getMeasurement(id string) (float64, error) {
res, err := wb.measurementG()
res, err := wb.measurementCache.Get()
if err != nil {
return 0, err
}
@ -313,7 +314,7 @@ func (wb *Smaevcharger) getMeasurement(id string) (float64, error) {
}
func (wb *Smaevcharger) getParameter(id string) (string, error) {
res, err := wb.parameterG()
res, err := wb.parameterCache.Get()
if err != nil {
return "", err
}

View file

@ -41,11 +41,11 @@ import (
// Zaptec charger implementation
type Zaptec struct {
*request.Helper
log *util.Logger
statusG func() (zaptec.StateResponse, error)
id string
priority bool
cache time.Duration
log *util.Logger
statusCache provider.Cacheable[zaptec.StateResponse]
id string
priority bool
cache time.Duration
}
func init() {
@ -91,7 +91,14 @@ func NewZaptec(user, password, id string, priority bool, cache time.Duration) (a
}
// setup cached values
c.reset()
c.statusCache = provider.ResettableCached(func() (zaptec.StateResponse, error) {
var res zaptec.StateResponse
uri := fmt.Sprintf("%s/api/chargers/%s/state", zaptec.ApiURL, c.id)
err := c.GetJSON(uri, &res)
return res, err
}, c.cache)
data := url.Values{
"grant_type": {"password"},
@ -118,17 +125,6 @@ func NewZaptec(user, password, id string, priority bool, cache time.Duration) (a
return c, err
}
func (c *Zaptec) reset() {
c.statusG = provider.Cached(func() (zaptec.StateResponse, error) {
var res zaptec.StateResponse
uri := fmt.Sprintf("%s/api/chargers/%s/state", zaptec.ApiURL, c.id)
err := c.GetJSON(uri, &res)
return res, err
}, c.cache)
}
func (c *Zaptec) chargers() ([]string, error) {
var res zaptec.ChargersResponse
@ -145,7 +141,7 @@ func (c *Zaptec) chargers() ([]string, error) {
// Status implements the api.Charger interface
func (c *Zaptec) Status() (api.ChargeStatus, error) {
res, err := c.statusG()
res, err := c.statusCache.Get()
if err != nil {
return api.StatusA, err
}
@ -167,7 +163,7 @@ func (c *Zaptec) Status() (api.ChargeStatus, error) {
// Enabled implements the api.Charger interface
func (c *Zaptec) Enabled() (bool, error) {
res, err := c.statusG()
res, err := c.statusCache.Get()
return res.ObservationByID(zaptec.IsEnabled).Bool() && !res.ObservationByID(zaptec.FinalStopActive).Bool(), err
}
@ -183,7 +179,7 @@ func (c *Zaptec) Enable(enable bool) error {
req, err := request.New(http.MethodPost, uri, nil, request.JSONEncoding)
if err == nil {
_, err = c.DoBody(req)
c.reset()
c.statusCache.Reset()
}
return err
@ -195,7 +191,7 @@ func (c *Zaptec) chargerUpdate(data zaptec.Update) error {
req, err := request.New(http.MethodPost, uri, request.MarshalJSON(data), request.JSONEncoding)
if err == nil {
_, err = c.DoBody(req)
c.reset()
c.statusCache.Reset()
}
return err
@ -207,7 +203,7 @@ func (c *Zaptec) sessionPriority(session string, data zaptec.SessionPriority) er
req, err := request.New(http.MethodPost, uri, request.MarshalJSON(data), request.JSONEncoding)
if err == nil {
_, err = c.DoBody(req)
c.reset()
c.statusCache.Reset()
}
return err
@ -227,7 +223,7 @@ var _ api.Meter = (*Zaptec)(nil)
// CurrentPower implements the api.Meter interface
func (c *Zaptec) CurrentPower() (float64, error) {
res, err := c.statusG()
res, err := c.statusCache.Get()
if err != nil {
return 0, err
}
@ -239,7 +235,7 @@ var _ api.ChargeRater = (*Zaptec)(nil)
// ChargedEnergy implements the api.ChargeRater interface
func (c *Zaptec) ChargedEnergy() (float64, error) {
res, err := c.statusG()
res, err := c.statusCache.Get()
if err != nil {
return 0, err
}
@ -251,7 +247,7 @@ var _ api.PhaseCurrents = (*Zaptec)(nil)
// Currents implements the api.PhaseCurrents interface
func (c *Zaptec) Currents() (float64, float64, float64, error) {
res, err := c.statusG()
res, err := c.statusCache.Get()
if err != nil {
return 0, 0, 0, err
}
@ -270,7 +266,7 @@ var _ api.PhaseSwitcher = (*Zaptec)(nil)
// Phases1p3p implements the api.ChargePhases interface
func (c *Zaptec) Phases1p3p(phases int) error {
res, err := c.statusG()
res, err := c.statusCache.Get()
if err == nil {
data := zaptec.Update{
@ -299,7 +295,7 @@ var _ api.Diagnosis = (*Zaptec)(nil)
// Diagnosis implements the api.ChargePhases interface
func (c *Zaptec) Diagnose() {
res, _ := c.statusG()
res, _ := c.statusCache.Get()
// sort for printing
sort.Slice(res, func(i, j int) bool {

View file

@ -36,7 +36,6 @@ type cached[T any] struct {
// Cached wraps a getter with a cache
func Cached[T any](g func() (T, error), cache time.Duration) func() (T, error) {
c := ResettableCached(g, cache)
_ = bus.Subscribe(reset, c.Reset)
return c.Get
}
@ -51,11 +50,13 @@ var _ Cacheable[int64] = (*cached[int64])(nil)
// ResettableCached wraps a getter with a cache. It returns a `Cacheable`.
// Instead of the cached getter, the `Get()` and `Reset()` methods are exposed.
func ResettableCached[T any](g func() (T, error), cache time.Duration) *cached[T] {
return &cached[T]{
c := &cached[T]{
clock: clock.New(),
cache: cache,
g: g,
}
_ = bus.Subscribe(reset, c.Reset)
return c
}
func (c *cached[T]) Get() (T, error) {