diff --git a/client/client.go b/client/client.go index 7404b00a..1bfa52aa 100644 --- a/client/client.go +++ b/client/client.go @@ -140,10 +140,14 @@ func NewClient( opt = append(opt, core.WithErr(errors)) } oc := core.NewClient(opt...) + pollInterval := time.Second * 10 + if cacheExpiration/2 > pollInterval { + pollInterval = cacheExpiration / 2 + } client := Client{ client: oc, app: app, - deviceCache: NewDeviceCache(cacheExpiration, time.Minute, errors), + deviceCache: NewDeviceCache(cacheExpiration, pollInterval, errors), observeResourceCache: kitSync.NewMap(), deviceOwner: deviceOwner, subscriptions: make(map[string]subscription), diff --git a/client/deleteDevice.go b/client/deleteDevice.go deleted file mode 100644 index ee56f65b..00000000 --- a/client/deleteDevice.go +++ /dev/null @@ -1,17 +0,0 @@ -package client - -import ( - "context" -) - -func (c *Client) DeleteDevice(ctx context.Context, deviceID string) (bool, error) { - dev, ok := c.deviceCache.LoadAndDeleteDevice(ctx, deviceID) - if !ok { - return false, nil - } - err := dev.Close(ctx) - if err != nil { - c.errors(err) - } - return true, nil -} diff --git a/client/deleteDevices.go b/client/deleteDevices.go new file mode 100644 index 00000000..e9ecd376 --- /dev/null +++ b/client/deleteDevices.go @@ -0,0 +1,28 @@ +package client + +import ( + "context" + "fmt" +) + +func (c *Client) DeleteDevice(ctx context.Context, deviceID string) bool { + devs := c.DeleteDevices(ctx, []string{deviceID}) + return len(devs) > 0 +} + +// DeleteDevices deletes a device from the cache. If deviceIDFilter is empty, all devices are deleted. +func (c *Client) DeleteDevices(ctx context.Context, deviceIDFilter []string) []string { + devs := c.deviceCache.LoadAndDeleteDevices(deviceIDFilter) + if len(devs) == 0 { + return nil + } + deviceIDs := make([]string, 0, len(devs)) + for _, d := range devs { + deviceIDs = append(deviceIDs, d.DeviceID()) + err := d.Close(ctx) + if err != nil { + c.errors(fmt.Errorf("can't close device %v during deleting device from the cache: %w", d.DeviceID(), err)) + } + } + return deviceIDs +} diff --git a/client/deleteDevice_test.go b/client/deleteDevices_test.go similarity index 95% rename from client/deleteDevice_test.go rename to client/deleteDevices_test.go index 3a4093e3..da9b4e55 100644 --- a/client/deleteDevice_test.go +++ b/client/deleteDevices_test.go @@ -191,10 +191,9 @@ func TestClientDeleteDevice(t *testing.T) { deviceID string } tests := []struct { - name string - args args - want bool - wantErr bool + name string + args args + want bool }{ { name: "not found", @@ -202,8 +201,7 @@ func TestClientDeleteDevice(t *testing.T) { addDevice: nil, deviceID: "not-found", }, - want: false, - wantErr: false, + want: false, }, { name: "found", @@ -211,16 +209,14 @@ func TestClientDeleteDevice(t *testing.T) { addDevice: addDirectDeviceToCache, deviceID: "found", }, - want: true, - wantErr: false, + want: true, }, { name: "try to delete twice", args: args{ deviceID: "found", }, - want: false, - wantErr: false, + want: false, }, { name: "delete device with resource observation", @@ -250,8 +246,7 @@ func TestClientDeleteDevice(t *testing.T) { checkForSkip: func(ctx context.Context, t *testing.T, c *Client, deviceID string) { _, links, err := c.GetDevice(ctx, deviceID) require.NoError(t, err) - ok, err := c.DeleteDevice(ctx, deviceID) - require.NoError(t, err) + ok := c.DeleteDevice(ctx, deviceID) require.True(t, ok) res := links.GetResourceLinks(resources.ResourceType) @@ -333,8 +328,7 @@ func TestClientDeleteDevice(t *testing.T) { err := c.DisownDevice(ctx, deviceID) require.NoError(t, err) }() - ok, err := c.DeleteDevice(ctx, deviceID) - require.NoError(t, err) + ok := c.DeleteDevice(ctx, deviceID) require.True(t, ok) for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -347,12 +341,7 @@ func TestClientDeleteDevice(t *testing.T) { if tt.args.addDevice != nil { testCtx = tt.args.addDevice(ctx, t, c, tt.args.deviceID) } - got, err := c.DeleteDevice(testCtx, tt.args.deviceID) - if tt.wantErr { - require.Error(t, err) - return - } - require.NoError(t, err) + got := c.DeleteDevice(testCtx, tt.args.deviceID) require.Equal(t, tt.want, got) if tt.args.cleanUp != nil { tt.args.cleanUp(testCtx, t, c, tt.args.deviceID) diff --git a/client/deviceCache.go b/client/deviceCache.go index c4fe9c57..631e607b 100644 --- a/client/deviceCache.go +++ b/client/deviceCache.go @@ -49,14 +49,12 @@ func NewDeviceCache(deviceExpiration, pollInterval time.Duration, errors func(er } // This function loads the device from the cache and deletes it from the cache. To cleanup the device you have to call device.Close. -func (c *DeviceCache) LoadAndDeleteDevice(ctx context.Context, deviceID string) (*core.Device, bool) { - d := c.devicesCache.Load(deviceID) - if d == nil { +func (c *DeviceCache) LoadAndDeleteDevice(deviceID string) (*core.Device, bool) { + devs := c.LoadAndDeleteDevices([]string{deviceID}) + if len(devs) == 0 { return nil, false } - dev := d.Data().(*core.Device) - c.devicesCache.Delete(deviceID) - return dev, true + return devs[0], true } func (c *DeviceCache) GetDevice(deviceID string) (*core.Device, bool) { @@ -184,6 +182,27 @@ func (c *DeviceCache) GetDevicesFoundByIP() map[string]string { return devices } +func (c *DeviceCache) LoadAndDeleteDevices(deviceIDFilter []string) []*core.Device { + devices := make([]*core.Device, 0, len(deviceIDFilter)) + if len(deviceIDFilter) == 0 { + for _, val := range c.devicesCache.PullOutAll() { + d := val.(*core.Device) + devices = append(devices, d) + } + return devices + } + for _, deviceID := range deviceIDFilter { + val := c.devicesCache.Load(deviceID) + if val == nil { + continue + } + c.devicesCache.Delete(deviceID) + d := val.Data().(*core.Device) + devices = append(devices, d) + } + return devices +} + func (c *DeviceCache) Close(ctx context.Context) error { var errors []error if c.closed.CompareAndSwap(false, true) { diff --git a/client/deviceCache_test.go b/client/deviceCache_test.go index 061d29c2..1e1652e1 100644 --- a/client/deviceCache_test.go +++ b/client/deviceCache_test.go @@ -58,11 +58,11 @@ func TestDeviceCacheContentHandling(t *testing.T) { require.True(t, found) require.False(t, expiration.IsZero()) - dev, removed := cache.LoadAndDeleteDevice(context.TODO(), device2ID) + dev, removed := cache.LoadAndDeleteDevice(device2ID) require.True(t, removed) err := dev.Close(context.TODO()) require.NoError(t, err) - dev, removed = cache.LoadAndDeleteDevice(context.TODO(), device2ID) + dev, removed = cache.LoadAndDeleteDevice(device2ID) require.False(t, removed) require.Nil(t, dev) @@ -77,7 +77,7 @@ func TestDeviceCacheContentHandling(t *testing.T) { require.True(t, found) require.False(t, expiration.IsZero()) - dev, removed = cache.LoadAndDeleteDevice(context.TODO(), device1ID) + dev, removed = cache.LoadAndDeleteDevice(device1ID) require.True(t, removed) err = dev.Close(context.TODO()) require.NoError(t, err) diff --git a/client/disownDevice.go b/client/disownDevice.go index b75a16f9..ff73f409 100644 --- a/client/disownDevice.go +++ b/client/disownDevice.go @@ -14,7 +14,7 @@ func (c *Client) DisownDevice(ctx context.Context, deviceID string, opts ...Comm return err } defer func() { - dev, ok := c.deviceCache.LoadAndDeleteDevice(ctx, d.DeviceID()) + dev, ok := c.deviceCache.LoadAndDeleteDevice(d.DeviceID()) if ok { dev.Close(ctx) } diff --git a/client/getDevice.go b/client/getDevice.go index 7bc06dbe..c8ba7d93 100644 --- a/client/getDevice.go +++ b/client/getDevice.go @@ -24,7 +24,7 @@ func getLinksDevice(ctx context.Context, dev *core.Device, disableUDPEndpoints b // come back online func deleteDeviceNotFoundByIP(ctx context.Context, deviceCache *DeviceCache, dev *core.Device) { if dev.FoundByIP() == "" { - deviceCache.LoadAndDeleteDevice(ctx, dev.DeviceID()) + deviceCache.LoadAndDeleteDevice(dev.DeviceID()) } dev.Close(ctx) } diff --git a/client/getDevice_test.go b/client/getDevice_test.go index 853dc77e..8c90afb5 100644 --- a/client/getDevice_test.go +++ b/client/getDevice_test.go @@ -196,14 +196,12 @@ func TestClientGetDeviceByIP(t *testing.T) { require.NotEmpty(t, got.Details.(*device.Device).ProtocolIndependentID) got.Details.(*device.Device).ProtocolIndependentID = "" require.Equal(t, tt.want, got) - ok, err := c.DeleteDevice(ctx, got.ID) - require.NoError(t, err) + ok := c.DeleteDevice(ctx, got.ID) require.True(t, ok) // we should not be able to remove the device second time - ok, err = c.DeleteDevice(ctx, got.ID) + ok = c.DeleteDevice(ctx, got.ID) require.False(t, ok) - require.NoError(t, err) }) } } diff --git a/client/getDevices.go b/client/getDevices.go index 45e67a12..2167053c 100644 --- a/client/getDevices.go +++ b/client/getDevices.go @@ -204,7 +204,7 @@ func (h *discoveryHandler) Handle(ctx context.Context, newdev *core.Device) { dev, _ := h.deviceCache.UpdateOrStoreDeviceWithExpiration(newdev) links, err := getLinksDevice(ctx, dev, h.disableUDPEndpoints) if err != nil { - dev, ok := h.deviceCache.LoadAndDeleteDevice(ctx, dev.DeviceID()) + dev, ok := h.deviceCache.LoadAndDeleteDevice(dev.DeviceID()) if ok { dev.Close(ctx) } diff --git a/client/ownDevice.go b/client/ownDevice.go index 37fb162f..030a7631 100644 --- a/client/ownDevice.go +++ b/client/ownDevice.go @@ -30,7 +30,7 @@ func (c *Client) OwnDevice(ctx context.Context, deviceID string, opts ...OwnOpti return c.deviceOwner.OwnDevice(ctx, deviceID, cfg.otmTypes, cfg.discoveryConfiguration, c.ownDeviceWithSigners, cfg.opts...) } -func (c *Client) updateCache(ctx context.Context, d *core.Device, oldDeviceID string) { +func (c *Client) updateCache(d *core.Device, oldDeviceID string) { if d.DeviceID() == oldDeviceID { return } @@ -44,7 +44,7 @@ func (c *Client) updateCache(ctx context.Context, d *core.Device, oldDeviceID st } // remove device from key oldDeviceID // we don't need to close it because it is already stored on new deviceID position - _, _ = c.deviceCache.LoadAndDeleteDevice(ctx, oldDeviceID) + _, _ = c.deviceCache.LoadAndDeleteDevice(oldDeviceID) } func (c *Client) ownDeviceWithSigners(ctx context.Context, deviceID string, otmClient []otm.Client, discoveryConfiguration core.DiscoveryConfiguration, opts ...core.OwnOption) (string, error) { @@ -71,7 +71,7 @@ func (c *Client) ownDeviceWithSigners(ctx context.Context, deviceID string, otmC if err != nil { return "", err } - c.updateCache(ctx, d, deviceID) + c.updateCache(d, deviceID) return d.DeviceID(), nil }