Enhance SQL KV store and backend diagnostics

- Updated sqlKV struct to maintain a reference to the dbProvider, preventing garbage collection of the database connection.
- Implemented Ping and checkDB methods in sqlKV for verifying database connection health.
- Enhanced kvStorageBackend to implement the DiagnosticsServer interface, adding IsHealthy method for health checks.
- Updated resource server to utilize the new diagnostics capabilities of kvStorageBackend.

These changes improve the reliability and maintainability of the SQL-based KV store and its integration with the resource server.
This commit is contained in:
Georges Chaudy
2025-11-18 12:12:05 +01:00
parent e156dfd92a
commit f5ef25233d
3 changed files with 50 additions and 6 deletions
+28 -4
View File
@@ -24,8 +24,9 @@ const (
// sqlKV implements the KV interface using SQL storage
type sqlKV struct {
db db.DB
dialect sqltemplate.Dialect
dbProvider db.DBProvider // Keep reference to prevent GC
db db.DB
dialect sqltemplate.Dialect
}
// NewSQLKV creates a new SQL-based KV store
@@ -55,8 +56,9 @@ func NewSQLKV(dbProvider db.DBProvider) (KV, error) {
}
return &sqlKV{
db: dbConn,
dialect: dialect,
dbProvider: dbProvider, // Keep reference to prevent GC from closing the database
db: dbConn,
dialect: dialect,
}, nil
}
@@ -774,3 +776,25 @@ func (k *sqlKV) BatchDelete(ctx context.Context, section string, keys []string)
func (k *sqlKV) UnixTimestamp(ctx context.Context) (int64, error) {
return time.Now().Unix(), nil
}
// Ping checks if the database connection is alive
func (k *sqlKV) Ping(ctx context.Context) error {
if k.db == nil {
return fmt.Errorf("database connection is nil")
}
return k.db.PingContext(ctx)
}
// checkDB verifies the database connection is still valid before operations
func (k *sqlKV) checkDB() error {
if k.db == nil {
return fmt.Errorf("database connection is nil")
}
// Quick ping to verify connection is alive
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
if err := k.db.PingContext(ctx); err != nil {
return fmt.Errorf("database connection is not healthy: %w", err)
}
return nil
}
@@ -74,6 +74,7 @@ type kvStorageBackend struct {
var _ StorageBackend = &kvStorageBackend{}
var _ LifecycleHooks = &kvStorageBackend{}
var _ resourcepb.DiagnosticsServer = &kvStorageBackend{}
type KVBackendOptions struct {
KvStore KV
@@ -151,6 +152,25 @@ func (k *kvStorageBackend) Stop(ctx context.Context) error {
return nil
}
// IsHealthy implements DiagnosticsServer
func (k *kvStorageBackend) IsHealthy(ctx context.Context, _ *resourcepb.HealthCheckRequest) (*resourcepb.HealthCheckResponse, error) {
// Check if the underlying KV store supports Ping (e.g., sqlKV)
type pinger interface {
Ping(context.Context) error
}
if p, ok := k.kv.(pinger); ok {
if err := p.Ping(ctx); err != nil {
return nil, fmt.Errorf("KV store health check failed: %w", err)
}
}
return &resourcepb.HealthCheckResponse{Status: resourcepb.HealthCheckResponse_SERVING}, nil
}
// Read implements DiagnosticsServer
func (k *kvStorageBackend) Read(ctx context.Context, req *resourcepb.ReadRequest) (*resourcepb.ReadResponse, error) {
return nil, ErrNotImplementedYet
}
// runCleanupOldEvents starts a background goroutine that periodically cleans up old events
func (k *kvStorageBackend) runCleanupOldEvents(ctx context.Context) {
// Run cleanup every hour
+2 -2
View File
@@ -19,6 +19,7 @@ import (
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/storage/unified/resource"
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
"github.com/grafana/grafana/pkg/storage/unified/sql/db/dbimpl"
)
@@ -122,8 +123,7 @@ func NewResourceServer(opts ServerOptions) (resource.ResourceServer, error) {
}
serverOptions.Backend = kvBackend
serverOptions.Lifecycle = kvBackend.(resource.LifecycleHooks)
// Note: kvStorageBackend doesn't implement Diagnostics yet
// The server will use noopService for diagnostics
serverOptions.Diagnostics = kvBackend.(resourcepb.DiagnosticsServer)
} else {
// Use existing SQL backend
backend, err := NewBackend(BackendOptions{