Feature/unified storage search dual reader (#108291)

* Add UnifiedStorageSearchDualReaderEnabled feature flag

Signed-off-by: Maicon Costa <maiconscosta@gmail.com>

* Refactor UniSearch Dual Reader

Signed-off-by: Maicon Costa <maiconscosta@gmail.com>

* Run make gen-feature-toggles

Signed-off-by: Maicon Costa <maiconscosta@gmail.com>

* fix: unit tests search_client

Signed-off-by: Bruno Abrantes <bruno@brunoabrantes.com>

* feat: cancels shadow search requests after 500ms

Signed-off-by: Bruno Abrantes <bruno@brunoabrantes.com>

---------

Signed-off-by: Maicon Costa <maiconscosta@gmail.com>
Signed-off-by: Bruno Abrantes <bruno@brunoabrantes.com>
Co-authored-by: Will Assis <william@williamassis.com>
Co-authored-by: Bruno Abrantes <bruno@brunoabrantes.com>
This commit is contained in:
maicon
2025-07-18 14:43:56 +02:00
committed by GitHub
co-authored by Will Assis Bruno Abrantes
parent 0b451117ee
commit 2dba473015
15 changed files with 562 additions and 27 deletions
+3 -2
View File
@@ -15,6 +15,7 @@ import (
"github.com/grafana/grafana/pkg/registry/apis/dashboard/legacysearcher"
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/search/sort"
"github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/storage/legacysql/dualwrite"
@@ -46,9 +47,9 @@ type k8sHandler struct {
}
func NewK8sHandler(dual dualwrite.Service, namespacer request.NamespaceMapper, gvr schema.GroupVersionResource,
restConfig func(context.Context) (*rest.Config, error), dashStore dashboards.Store, userSvc user.Service, resourceClient resource.ResourceClient, sorter sort.Service) K8sHandler {
restConfig func(context.Context) (*rest.Config, error), dashStore dashboards.Store, userSvc user.Service, resourceClient resource.ResourceClient, sorter sort.Service, features featuremgmt.FeatureToggles) K8sHandler {
legacySearcher := legacysearcher.NewDashboardSearchClient(dashStore, sorter)
searchClient := resource.NewSearchClient(dualwrite.NewSearchAdapter(dual), gvr.GroupResource(), resourceClient, legacySearcher)
searchClient := resource.NewSearchClient(dualwrite.NewSearchAdapter(dual), gvr.GroupResource(), resourceClient, legacySearcher, features)
return &k8sHandler{
namespacer: namespacer,
@@ -5,7 +5,6 @@ import (
"fmt"
"sync"
"github.com/prometheus/client_golang/prometheus"
"go.opentelemetry.io/otel/attribute"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@@ -18,11 +17,13 @@ import (
"github.com/grafana/grafana/pkg/services/apiserver/client"
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/search/sort"
"github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/storage/legacysql/dualwrite"
"github.com/grafana/grafana/pkg/storage/unified/resource"
"github.com/prometheus/client_golang/prometheus"
)
type K8sClientFactory func(ctx context.Context, version string) client.K8sHandler
@@ -44,8 +45,9 @@ func NewK8sClientWithFallback(
sorter sort.Service,
dual dualwrite.Service,
reg prometheus.Registerer,
features featuremgmt.FeatureToggles,
) *K8sClientWithFallback {
newClientFunc := newK8sClientFactory(cfg, restConfigProvider, dashboardStore, userService, resourceClient, sorter, dual)
newClientFunc := newK8sClientFactory(cfg, restConfigProvider, dashboardStore, userService, resourceClient, sorter, dual, features)
return &K8sClientWithFallback{
K8sHandler: newClientFunc(context.Background(), dashboardv0.VERSION),
newClientFunc: newClientFunc,
@@ -112,6 +114,7 @@ func newK8sClientFactory(
resourceClient resource.ResourceClient,
sorter sort.Service,
dual dualwrite.Service,
features featuremgmt.FeatureToggles,
) K8sClientFactory {
clientCache := make(map[string]client.K8sHandler)
cacheMutex := &sync.RWMutex{}
@@ -149,7 +152,7 @@ func newK8sClientFactory(
}
span.AddEvent("Creating new client")
newClient := client.NewK8sHandler(dual, request.GetNamespaceMapper(cfg), gvr, restConfigProvider.GetRestConfig, dashboardStore, userService, resourceClient, sorter)
newClient := client.NewK8sHandler(dual, request.GetNamespaceMapper(cfg), gvr, restConfigProvider.GetRestConfig, dashboardStore, userService, resourceClient, sorter, features)
clientCache[version] = newClient
return newClient
@@ -387,7 +387,7 @@ func ProvideDashboardServiceImpl(
serverLockService *serverlock.ServerLockService,
kvstore kvstore.KVStore,
) (*DashboardServiceImpl, error) {
k8sclient := dashboardclient.NewK8sClientWithFallback(cfg, restConfigProvider, dashboardStore, userService, resourceClient, sorter, dual, r)
k8sclient := dashboardclient.NewK8sClientWithFallback(cfg, restConfigProvider, dashboardStore, userService, resourceClient, sorter, dual, r, features)
dashSvc := &DashboardServiceImpl{
cfg: cfg,
log: log.New("dashboard-service"),
@@ -61,6 +61,7 @@ func ProvideService(cfg *setting.Cfg, db db.DB, dashboardService dashboards.Dash
userService,
unified,
sorter,
features,
),
dashSvc: dashboardService,
log: log.New("dashboard-version"),
+8
View File
@@ -1819,6 +1819,14 @@ var (
Expression: "false",
RequiresRestart: true,
},
{
Name: "unifiedStorageSearchDualReaderEnabled",
Description: "Enable dual reader for unified storage search",
Stage: FeatureStageExperimental,
Owner: grafanaSearchAndStorageSquad,
HideFromAdminPage: true,
HideFromDocs: true,
},
}
)
+1
View File
@@ -235,3 +235,4 @@ enablePluginImporter,experimental,@grafana/plugins-platform-backend,false,false,
otelLogsFormatting,experimental,@grafana/observability-logs,false,false,true
alertingNotificationHistory,experimental,@grafana/alerting-squad,false,false,false
pluginAssetProvider,experimental,@grafana/plugins-platform-backend,false,true,false
unifiedStorageSearchDualReaderEnabled,experimental,@grafana/search-and-storage,false,false,false
1 Name Stage Owner requiresDevMode RequiresRestart FrontendOnly
235 otelLogsFormatting experimental @grafana/observability-logs false false true
236 alertingNotificationHistory experimental @grafana/alerting-squad false false false
237 pluginAssetProvider experimental @grafana/plugins-platform-backend false true false
238 unifiedStorageSearchDualReaderEnabled experimental @grafana/search-and-storage false false false
+4
View File
@@ -950,4 +950,8 @@ const (
// FlagPluginAssetProvider
// Allows decoupled core plugins to load from the Grafana CDN
FlagPluginAssetProvider = "pluginAssetProvider"
// FlagUnifiedStorageSearchDualReaderEnabled
// Enable dual reader for unified storage search
FlagUnifiedStorageSearchDualReaderEnabled = "unifiedStorageSearchDualReaderEnabled"
)
+31 -17
View File
@@ -2450,6 +2450,22 @@
"requiresRestart": true
}
},
{
"metadata": {
"name": "pluginAssetProvider",
"resourceVersion": "1752486584712",
"creationTimestamp": "2025-07-14T09:49:44Z"
},
"spec": {
"description": "Allows decoupled core plugins to load from the Grafana CDN",
"stage": "experimental",
"codeowner": "@grafana/plugins-platform-backend",
"requiresRestart": true,
"hideFromAdminPage": true,
"hideFromDocs": true,
"expression": "false"
}
},
{
"metadata": {
"name": "pluginLoadingRefactor",
@@ -2467,22 +2483,6 @@
"expression": "false"
}
},
{
"metadata": {
"name": "pluginAssetProvider",
"resourceVersion": "1752486584712",
"creationTimestamp": "2025-07-14T09:49:44Z"
},
"spec": {
"description": "Allows decoupled core plugins to load from the Grafana CDN",
"stage": "experimental",
"codeowner": "@grafana/plugins-platform-backend",
"requiresRestart": true,
"hideFromAdminPage": true,
"hideFromDocs": true,
"expression": "false"
}
},
{
"metadata": {
"name": "pluginProxyPreserveTrailingSlash",
@@ -3350,6 +3350,20 @@
"hideFromDocs": true
}
},
{
"metadata": {
"name": "unifiedStorageSearchDualReaderEnabled",
"resourceVersion": "1752500336818",
"creationTimestamp": "2025-07-14T13:38:56Z"
},
"spec": {
"description": "Enable dual reader for unified storage search",
"stage": "experimental",
"codeowner": "@grafana/search-and-storage",
"hideFromAdminPage": true,
"hideFromDocs": true
}
},
{
"metadata": {
"name": "unifiedStorageSearchPermissionFiltering",
@@ -3436,4 +3450,4 @@
}
}
]
}
}
+2
View File
@@ -122,6 +122,7 @@ func ProvideService(
userService,
resourceClient,
sorter,
features,
)
unifiedStore := ProvideUnifiedStore(k8sHandler, userService, tracer)
@@ -140,6 +141,7 @@ func ProvideService(
userService,
resourceClient,
sorter,
features,
)
srv.dashboardK8sClient = dashHandler
}
@@ -199,7 +199,7 @@ func TestIntegrationFolderServiceViaUnifiedStorage(t *testing.T) {
tracer := noop.NewTracerProvider().Tracer("TestIntegrationFolderServiceViaUnifiedStorage")
dashboardStore := dashboards.NewFakeDashboardStore(t)
k8sCli := client.NewK8sHandler(dualwrite.ProvideTestService(), request.GetNamespaceMapper(cfg), folderv1.FolderResourceInfo.GroupVersionResource(), restCfgProvider.GetRestConfig, dashboardStore, userService, nil, sort.ProvideService())
k8sCli := client.NewK8sHandler(dualwrite.ProvideTestService(), request.GetNamespaceMapper(cfg), folderv1.FolderResourceInfo.GroupVersionResource(), restCfgProvider.GetRestConfig, dashboardStore, userService, nil, sort.ProvideService(), nil)
unifiedStore := ProvideUnifiedStore(k8sCli, userService, tracer)
ctx := context.Background()