Skip to content

Commit a5f1dac

Browse files
authored
add debug logs to highlight provider behaviour (#1432)
1 parent 178067a commit a5f1dac

File tree

4 files changed

+219
-16
lines changed

4 files changed

+219
-16
lines changed

backend/app/instance_provider.go

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -48,17 +48,10 @@ func (ip *instanceProvider) GetKey(ctx context.Context, pluginContext backend.Pl
4848
return nil, errors.New("app instance settings cannot be nil")
4949
}
5050

51-
// The instance key generated for app plugins should include both plugin ID, and the OrgID, since for a single
52-
// Grafana instance there might be different orgs using the same plugin.
53-
defaultKey := fmt.Sprintf("%s#%v", pluginContext.PluginID, pluginContext.OrgID)
54-
if tID := tenant.IDFromContext(ctx); tID != "" {
55-
return fmt.Sprintf("%s#%s", tID, defaultKey), nil
56-
}
57-
58-
return defaultKey, nil
51+
return instanceKey(ctx, pluginContext), nil
5952
}
6053

61-
func (ip *instanceProvider) NeedsUpdate(_ context.Context, pluginContext backend.PluginContext, cachedInstance instancemgmt.CachedInstance) bool {
54+
func (ip *instanceProvider) NeedsUpdate(ctx context.Context, pluginContext backend.PluginContext, cachedInstance instancemgmt.CachedInstance) bool {
6255
curConfig := pluginContext.GrafanaConfig
6356
cachedConfig := cachedInstance.PluginContext.GrafanaConfig
6457
configUpdated := !cachedConfig.Equal(curConfig)
@@ -67,9 +60,33 @@ func (ip *instanceProvider) NeedsUpdate(_ context.Context, pluginContext backend
6760
curAppSettings := pluginContext.AppInstanceSettings
6861
appUpdated := !curAppSettings.Updated.Equal(cachedAppSettings.Updated)
6962

63+
if appUpdated || configUpdated {
64+
logger := backend.Logger.FromContext(ctx)
65+
66+
ik := instanceKey(ctx, pluginContext)
67+
if appUpdated {
68+
logger.Debug("App instance needs update: app settings changed", "key", ik)
69+
}
70+
if configUpdated {
71+
logger.Debug("App instance needs update: config changed", "key", ik, "diff", curConfig.Diff(cachedConfig))
72+
}
73+
}
74+
7075
return appUpdated || configUpdated
7176
}
7277

7378
func (ip *instanceProvider) NewInstance(ctx context.Context, pluginContext backend.PluginContext) (instancemgmt.Instance, error) {
79+
backend.Logger.FromContext(ctx).Debug("App instance created", "key", instanceKey(ctx, pluginContext))
7480
return ip.factory(ctx, *pluginContext.AppInstanceSettings)
7581
}
82+
83+
func instanceKey(ctx context.Context, pluginContext backend.PluginContext) string {
84+
// The instance key generated for app plugins should include both plugin ID, and the OrgID, since for a single
85+
// Grafana instance there might be different orgs using the same plugin.
86+
defaultKey := fmt.Sprintf("%s#%v", pluginContext.PluginID, pluginContext.OrgID)
87+
if tID := tenant.IDFromContext(ctx); tID != "" {
88+
return fmt.Sprintf("%s#%s", tID, defaultKey)
89+
}
90+
91+
return defaultKey
92+
}

backend/config.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,48 @@ func (c *GrafanaCfg) Equal(c2 *GrafanaCfg) bool {
9898
return true
9999
}
100100

101+
// Diff returns the names of config fields that differ between this config and c2.
102+
// Returns field names for added, removed, or changed values.
103+
// Always returns a slice, never nil.
104+
func (c *GrafanaCfg) Diff(c2 *GrafanaCfg) []string {
105+
if c == nil && c2 == nil {
106+
return []string{}
107+
}
108+
if c == nil {
109+
var keys []string
110+
for k := range c2.config {
111+
keys = append(keys, k)
112+
}
113+
return keys
114+
}
115+
if c2 == nil {
116+
var keys []string
117+
for k := range c.config {
118+
keys = append(keys, k)
119+
}
120+
return keys
121+
}
122+
123+
var changed []string
124+
keys := make(map[string]bool)
125+
for k := range c.config {
126+
keys[k] = true
127+
}
128+
for k := range c2.config {
129+
keys[k] = true
130+
}
131+
132+
for key := range keys {
133+
v1, ok1 := c.config[key]
134+
v2, ok2 := c2.config[key]
135+
if ok1 != ok2 || v1 != v2 {
136+
changed = append(changed, key)
137+
}
138+
}
139+
140+
return changed
141+
}
142+
101143
// ProxyHash returns the last four characters of the base64-encoded
102144
// PDC client key contents, if present, for use in datasource instance
103145
// caching. The contents should be PEM-encoded, so we try to PEM-decode

backend/config_test.go

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,133 @@ func randomProxyContents() []byte {
375375

376376
var b64chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789/+"
377377

378+
func TestGrafanaCfg_Diff(t *testing.T) {
379+
t.Run("both configs nil should return empty slice", func(t *testing.T) {
380+
var cfg1, cfg2 *GrafanaCfg
381+
diff := cfg1.Diff(cfg2)
382+
require.Empty(t, diff)
383+
require.NotNil(t, diff)
384+
})
385+
386+
t.Run("one config nil should return keys from other config", func(t *testing.T) {
387+
cfg1 := NewGrafanaCfg(map[string]string{"key1": "value1", "key2": "value2"})
388+
var cfg2 *GrafanaCfg
389+
390+
diff1 := cfg1.Diff(cfg2) // cfg1 has keys, cfg2 is nil
391+
diff2 := cfg2.Diff(cfg1) // cfg2 is nil, cfg1 has keys
392+
393+
require.Len(t, diff1, 2)
394+
require.Contains(t, diff1, "key1")
395+
require.Contains(t, diff1, "key2")
396+
397+
require.Len(t, diff2, 2)
398+
require.Contains(t, diff2, "key1")
399+
require.Contains(t, diff2, "key2")
400+
})
401+
402+
t.Run("one config empty should return keys from other config", func(t *testing.T) {
403+
cfg1 := NewGrafanaCfg(map[string]string{"key1": "value1", "key2": "value2"})
404+
cfg2 := NewGrafanaCfg(map[string]string{})
405+
406+
diff1 := cfg1.Diff(cfg2) // cfg1 has keys, cfg2 is empty
407+
diff2 := cfg2.Diff(cfg1) // cfg2 is empty, cfg1 has keys
408+
409+
require.Len(t, diff1, 2)
410+
require.Contains(t, diff1, "key1")
411+
require.Contains(t, diff1, "key2")
412+
413+
require.Len(t, diff2, 2)
414+
require.Contains(t, diff2, "key1")
415+
require.Contains(t, diff2, "key2")
416+
})
417+
418+
t.Run("empty configs should return empty slice", func(t *testing.T) {
419+
cfg1 := NewGrafanaCfg(map[string]string{})
420+
cfg2 := NewGrafanaCfg(map[string]string{})
421+
422+
diff := cfg1.Diff(cfg2)
423+
require.Empty(t, diff)
424+
})
425+
426+
t.Run("identical configs should return empty slice", func(t *testing.T) {
427+
config := map[string]string{
428+
"key1": "value1",
429+
"key2": "value2",
430+
"key3": "value3",
431+
}
432+
cfg1 := NewGrafanaCfg(config)
433+
cfg2 := NewGrafanaCfg(config)
434+
435+
diff := cfg1.Diff(cfg2)
436+
require.Empty(t, diff)
437+
})
438+
439+
t.Run("different values should return changed keys", func(t *testing.T) {
440+
cfg1 := NewGrafanaCfg(map[string]string{
441+
"key1": "value1",
442+
"key2": "value2",
443+
})
444+
cfg2 := NewGrafanaCfg(map[string]string{
445+
"key1": "different_value",
446+
"key2": "value2",
447+
})
448+
449+
diff := cfg1.Diff(cfg2)
450+
require.Len(t, diff, 1)
451+
require.Contains(t, diff, "key1")
452+
})
453+
454+
t.Run("added keys should be detected", func(t *testing.T) {
455+
cfg1 := NewGrafanaCfg(map[string]string{
456+
"key1": "value1",
457+
"key2": "value2",
458+
"key3": "value3",
459+
})
460+
cfg2 := NewGrafanaCfg(map[string]string{
461+
"key1": "value1",
462+
"key2": "value2",
463+
})
464+
465+
diff := cfg1.Diff(cfg2)
466+
require.Len(t, diff, 1)
467+
require.Contains(t, diff, "key3")
468+
})
469+
470+
t.Run("removed keys should be detected", func(t *testing.T) {
471+
cfg1 := NewGrafanaCfg(map[string]string{
472+
"key1": "value1",
473+
})
474+
cfg2 := NewGrafanaCfg(map[string]string{
475+
"key1": "value1",
476+
"key2": "value2",
477+
})
478+
479+
diff := cfg1.Diff(cfg2)
480+
require.Len(t, diff, 1)
481+
require.Contains(t, diff, "key2")
482+
})
483+
484+
t.Run("multiple changes should be detected", func(t *testing.T) {
485+
cfg1 := NewGrafanaCfg(map[string]string{
486+
"unchanged": "same_value",
487+
"modified": "old_value",
488+
"removed": "will_be_removed",
489+
})
490+
cfg2 := NewGrafanaCfg(map[string]string{
491+
"unchanged": "same_value",
492+
"modified": "new_value",
493+
"added": "new_key",
494+
})
495+
496+
diff := cfg1.Diff(cfg2)
497+
require.Len(t, diff, 3)
498+
require.Contains(t, diff, "modified")
499+
require.Contains(t, diff, "removed")
500+
require.Contains(t, diff, "added")
501+
require.NotContains(t, diff, "unchanged")
502+
})
503+
}
504+
378505
func BenchmarkProxyHash(b *testing.B) {
379506
count := 0
380507
kBytes := randomProxyContents()

backend/datasource/instance_provider.go

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ func NewInstanceManager(fn InstanceFactoryFunc) instancemgmt.InstanceManager {
3333
return instancemgmt.New(ip)
3434
}
3535

36-
// NewInstanceProvider create a new data source instance provuder,
36+
// NewInstanceProvider create a new data source instance provider,
3737
//
3838
// The instance provider is responsible for providing cache keys for data source instances,
3939
// creating new instances when needed and invalidating cached instances when they have been
@@ -59,14 +59,10 @@ func (ip *instanceProvider) GetKey(ctx context.Context, pluginContext backend.Pl
5959
return nil, errors.New("data source instance settings cannot be nil")
6060
}
6161

62-
dsID := pluginContext.DataSourceInstanceSettings.ID
63-
proxyHash := pluginContext.GrafanaConfig.ProxyHash()
64-
tenantID := tenant.IDFromContext(ctx)
65-
66-
return fmt.Sprintf("%d#%s#%s", dsID, tenantID, proxyHash), nil
62+
return instanceKey(ctx, pluginContext), nil
6763
}
6864

69-
func (ip *instanceProvider) NeedsUpdate(_ context.Context, pluginContext backend.PluginContext, cachedInstance instancemgmt.CachedInstance) bool {
65+
func (ip *instanceProvider) NeedsUpdate(ctx context.Context, pluginContext backend.PluginContext, cachedInstance instancemgmt.CachedInstance) bool {
7066
curConfig := pluginContext.GrafanaConfig
7167
cachedConfig := cachedInstance.PluginContext.GrafanaConfig
7268
configUpdated := !cachedConfig.Equal(curConfig)
@@ -75,10 +71,31 @@ func (ip *instanceProvider) NeedsUpdate(_ context.Context, pluginContext backend
7571
cachedDataSourceSettings := cachedInstance.PluginContext.DataSourceInstanceSettings
7672
dsUpdated := !curDataSourceSettings.Updated.Equal(cachedDataSourceSettings.Updated)
7773

74+
if dsUpdated || configUpdated {
75+
logger := backend.Logger.FromContext(ctx)
76+
77+
ik := instanceKey(ctx, pluginContext)
78+
if dsUpdated {
79+
logger.Debug("Datasource instance needs update: datasource settings changed", "key", ik)
80+
}
81+
if configUpdated {
82+
logger.Debug("Datasource instance needs update: config changed", "key", ik, "diff", curConfig.Diff(cachedConfig))
83+
}
84+
}
85+
7886
return dsUpdated || configUpdated
7987
}
8088

8189
func (ip *instanceProvider) NewInstance(ctx context.Context, pluginContext backend.PluginContext) (instancemgmt.Instance, error) {
8290
datasourceInstancesCreated.Inc()
91+
backend.Logger.FromContext(ctx).Debug("Datasource instance created", "key", instanceKey(ctx, pluginContext))
8392
return ip.factory(ctx, *pluginContext.DataSourceInstanceSettings)
8493
}
94+
95+
func instanceKey(ctx context.Context, pluginContext backend.PluginContext) string {
96+
dsID := pluginContext.DataSourceInstanceSettings.ID
97+
tenantID := tenant.IDFromContext(ctx)
98+
proxyHash := pluginContext.GrafanaConfig.ProxyHash()
99+
100+
return fmt.Sprintf("%d#%s#%s", dsID, tenantID, proxyHash)
101+
}

0 commit comments

Comments
 (0)