Skip to content

Commit 90c5939

Browse files
authored
feat: remote plugin disconnect clean the plugin cache (#667)
1 parent 2a722a2 commit 90c5939

3 files changed

Lines changed: 99 additions & 2 deletions

File tree

internal/service/install_service/controlpanel.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import (
99
"github.com/langgenius/dify-plugin-daemon/internal/types/models"
1010
"github.com/langgenius/dify-plugin-daemon/internal/types/models/curd"
1111
"github.com/langgenius/dify-plugin-daemon/pkg/entities/plugin_entities"
12+
"github.com/langgenius/dify-plugin-daemon/pkg/utils/cache"
13+
"github.com/langgenius/dify-plugin-daemon/pkg/utils/cache/helper"
1214
"github.com/langgenius/dify-plugin-daemon/pkg/utils/log"
1315
)
1416

@@ -72,6 +74,11 @@ func (l *InstallListener) OnDebuggingRuntimeConnected(runtime *debugging_runtime
7274
return
7375
}
7476
installation = existingInstallation
77+
78+
key := helper.PluginInstallationCacheKey(pluginID, runtime.TenantId())
79+
if _, delErr := cache.AutoDelete[models.PluginInstallation](key); delErr != nil {
80+
log.Warn("failed to invalidate plugin installation cache", "key", key, "error", delErr)
81+
}
7582
}
7683

7784
// FIXME(Yeuoly): temporary solution for managing plugin installation model in DB

internal/types/models/curd/atomic.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,13 @@ func InstallPlugin(
226226
return nil, nil, err
227227
}
228228

229+
// Invalidate plugin installation cache to avoid stale reads
230+
pluginID := pluginToBeReturns.PluginID
231+
pluginInstallationCacheKey := helper.PluginInstallationCacheKey(pluginID, tenantId)
232+
if _, delErr := cache.AutoDelete[models.PluginInstallation](pluginInstallationCacheKey); delErr != nil {
233+
log.Warn("failed to clear plugin installation cache", "key", pluginInstallationCacheKey, "error", delErr)
234+
}
235+
229236
return pluginToBeReturns, installationToBeReturns, nil
230237
}
231238

@@ -660,8 +667,8 @@ func UpgradePlugin(
660667
if err != nil {
661668
return nil, err
662669
}
663-
pluginId := newPluginUniqueIdentifier.PluginID() // get the pluginId
664-
pluginInstallationCacheKey := helper.PluginInstallationCacheKey(pluginId, tenantId) // make cache key
670+
pluginID := newPluginUniqueIdentifier.PluginID() // get the pluginId
671+
pluginInstallationCacheKey := helper.PluginInstallationCacheKey(pluginID, tenantId) // make cache key
665672
if _, err = cache.AutoDelete[models.PluginInstallation](pluginInstallationCacheKey); err != nil {
666673
return nil, err
667674
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package curd
2+
3+
import (
4+
"strings"
5+
"testing"
6+
7+
"github.com/google/uuid"
8+
"github.com/langgenius/dify-plugin-daemon/internal/db"
9+
"github.com/langgenius/dify-plugin-daemon/internal/types/app"
10+
"github.com/langgenius/dify-plugin-daemon/internal/types/models"
11+
"github.com/langgenius/dify-plugin-daemon/pkg/entities/plugin_entities"
12+
"github.com/langgenius/dify-plugin-daemon/pkg/utils/cache"
13+
"github.com/langgenius/dify-plugin-daemon/pkg/utils/cache/helper"
14+
"github.com/stretchr/testify/require"
15+
)
16+
17+
// Test that InstallPlugin invalidates PluginInstallation cache key so that
18+
// subsequent reads fall back to DB instead of returning stale data.
19+
func TestInstallPlugin_InvalidateInstallationCache(t *testing.T) {
20+
// Init Redis
21+
require.NoError(t, cache.InitRedisClient("127.0.0.1:6379", "", "difyai123456", false, 0, nil))
22+
t.Cleanup(func() { _ = cache.Close() })
23+
24+
// Init DB
25+
cfg := &app.Config{
26+
DBType: app.DB_TYPE_POSTGRESQL,
27+
DBUsername: "postgres",
28+
DBPassword: "difyai123456",
29+
DBHost: "localhost",
30+
DBPort: 5432,
31+
DBDatabase: "dify_plugin_daemon",
32+
DBSslMode: "disable",
33+
}
34+
cfg.SetDefault()
35+
db.Init(cfg)
36+
t.Cleanup(db.Close)
37+
38+
tenantID := uuid.NewString()
39+
pluginName := "cache_invalidate_" + uuid.NewString()
40+
checksum := strings.ReplaceAll(uuid.NewString(), "-", "")
41+
if len(checksum) > 32 {
42+
checksum = checksum[:32]
43+
}
44+
45+
identifier, err := plugin_entities.NewPluginUniqueIdentifier("tester/" + pluginName + ":1.0.0.0@" + checksum)
46+
require.NoError(t, err)
47+
pluginID := identifier.PluginID()
48+
49+
// Seed a stale cache value for the installation key
50+
key := helper.PluginInstallationCacheKey(pluginID, tenantID)
51+
require.NoError(t, cache.AutoSet[models.PluginInstallation](key, models.PluginInstallation{
52+
Model: models.Model{ID: "OLD"},
53+
PluginID: pluginID,
54+
PluginUniqueIdentifier: identifier.String(),
55+
TenantID: tenantID,
56+
RuntimeType: string(plugin_entities.PLUGIN_RUNTIME_TYPE_LOCAL),
57+
}))
58+
// Perform install which should invalidate the cache key
59+
_, _, err = InstallPlugin(
60+
tenantID,
61+
identifier,
62+
plugin_entities.PLUGIN_RUNTIME_TYPE_LOCAL,
63+
&plugin_entities.PluginDeclaration{},
64+
"unittest",
65+
map[string]any{"from": "test"},
66+
)
67+
require.NoError(t, err)
68+
69+
// Read using AutoGetWithGetter — should miss cache, call getter (DB), then set fresh cache
70+
inst, err := cache.AutoGetWithGetter(key, func() (*models.PluginInstallation, error) {
71+
v, e := db.GetOne[models.PluginInstallation](
72+
db.Equal("plugin_id", pluginID),
73+
db.Equal("tenant_id", tenantID),
74+
)
75+
if e != nil {
76+
return nil, e
77+
}
78+
return &v, nil
79+
})
80+
require.NoError(t, err)
81+
require.NotNil(t, inst)
82+
require.NotEqual(t, "OLD", inst.ID, "cache should have been invalidated and refetched from DB")
83+
}

0 commit comments

Comments
 (0)