Skip to content

Commit b386ce5

Browse files
committed
hub: add ExpiryMap.UpdateExpiration and sync SMART fetch intervals (#1800)
- Update smartFetchMap expiration when agent smart interval changes - Prevent background SMART fetching before initial system details are loaded - Add buffer to SMART fetch timing check - Get rid of unnecessary pointers in expirymap
1 parent e527534 commit b386ce5

File tree

3 files changed

+53
-10
lines changed

3 files changed

+53
-10
lines changed

internal/hub/expirymap/expirymap.go

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,15 @@ type val[T comparable] struct {
1616
}
1717

1818
type ExpiryMap[T comparable] struct {
19-
store *store.Store[string, *val[T]]
19+
store store.Store[string, val[T]]
2020
stopChan chan struct{}
2121
stopOnce sync.Once
2222
}
2323

2424
// New creates a new expiry map with custom cleanup interval
2525
func New[T comparable](cleanupInterval time.Duration) *ExpiryMap[T] {
2626
m := &ExpiryMap[T]{
27-
store: store.New(map[string]*val[T]{}),
27+
store: *store.New(map[string]val[T]{}),
2828
stopChan: make(chan struct{}),
2929
}
3030
go m.startCleaner(cleanupInterval)
@@ -33,7 +33,7 @@ func New[T comparable](cleanupInterval time.Duration) *ExpiryMap[T] {
3333

3434
// Set stores a value with the given TTL
3535
func (m *ExpiryMap[T]) Set(key string, value T, ttl time.Duration) {
36-
m.store.Set(key, &val[T]{
36+
m.store.Set(key, val[T]{
3737
value: value,
3838
expires: time.Now().Add(ttl),
3939
})
@@ -116,3 +116,12 @@ func (m *ExpiryMap[T]) cleanup() {
116116
}
117117
}
118118
}
119+
120+
// UpdateExpiration updates the expiration time of a key
121+
func (m *ExpiryMap[T]) UpdateExpiration(key string, ttl time.Duration) {
122+
value, ok := m.store.GetOk(key)
123+
if ok {
124+
value.expires = time.Now().Add(ttl)
125+
m.store.Set(key, value)
126+
}
127+
}

internal/hub/expirymap/expirymap_test.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,33 @@ func TestExpiryMap_GenericTypes(t *testing.T) {
178178
})
179179
}
180180

181+
func TestExpiryMap_UpdateExpiration(t *testing.T) {
182+
em := New[string](time.Hour)
183+
184+
// Set a value with short TTL
185+
em.Set("key1", "value1", time.Millisecond*50)
186+
187+
// Verify it exists
188+
assert.True(t, em.Has("key1"))
189+
190+
// Update expiration to a longer TTL
191+
em.UpdateExpiration("key1", time.Hour)
192+
193+
// Wait for the original TTL to pass
194+
time.Sleep(time.Millisecond * 100)
195+
196+
// Should still exist because expiration was updated
197+
assert.True(t, em.Has("key1"))
198+
value, ok := em.GetOk("key1")
199+
assert.True(t, ok)
200+
assert.Equal(t, "value1", value)
201+
202+
// Try updating non-existent key (should not panic)
203+
assert.NotPanics(t, func() {
204+
em.UpdateExpiration("nonexistent", time.Hour)
205+
})
206+
}
207+
181208
func TestExpiryMap_ZeroValues(t *testing.T) {
182209
em := New[string](time.Hour)
183210

internal/hub/systems/system.go

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -139,13 +139,25 @@ func (sys *System) update() error {
139139
// create system records
140140
_, err = sys.createRecords(data)
141141

142+
// if details were included and fetched successfully, mark details as fetched and update smart interval if set by agent
143+
if err == nil && data.Details != nil {
144+
sys.detailsFetched.Store(true)
145+
// update smart interval if it's set on the agent side
146+
if data.Details.SmartInterval > 0 {
147+
sys.smartInterval = data.Details.SmartInterval
148+
// make sure we reset expiration of lastFetch to remain as long as the new smart interval
149+
// to prevent premature expiration leading to new fetch if interval is different.
150+
sys.manager.smartFetchMap.UpdateExpiration(sys.Id, sys.smartInterval+time.Minute)
151+
}
152+
}
153+
142154
// Fetch and save SMART devices when system first comes online or at intervals
143-
if backgroundSmartFetchEnabled() {
155+
if backgroundSmartFetchEnabled() && sys.detailsFetched.Load() {
144156
if sys.smartInterval <= 0 {
145157
sys.smartInterval = time.Hour
146158
}
147159
lastFetch, _ := sys.manager.smartFetchMap.GetOk(sys.Id)
148-
if time.Since(time.UnixMilli(lastFetch)) >= sys.smartInterval && sys.smartFetching.CompareAndSwap(false, true) {
160+
if time.Since(time.UnixMilli(lastFetch-1e4)) >= sys.smartInterval && sys.smartFetching.CompareAndSwap(false, true) {
149161
go func() {
150162
defer sys.smartFetching.Store(false)
151163
sys.manager.smartFetchMap.Set(sys.Id, time.Now().UnixMilli(), sys.smartInterval+time.Minute)
@@ -223,11 +235,6 @@ func (sys *System) createRecords(data *system.CombinedData) (*core.Record, error
223235
if err := createSystemDetailsRecord(txApp, data.Details, sys.Id); err != nil {
224236
return err
225237
}
226-
sys.detailsFetched.Store(true)
227-
// update smart interval if it's set on the agent side
228-
if data.Details.SmartInterval > 0 {
229-
sys.smartInterval = data.Details.SmartInterval
230-
}
231238
}
232239

233240
// update system record (do this last because it triggers alerts and we need above records to be inserted first)

0 commit comments

Comments
 (0)