Add state syncing via CRDT

This commit is contained in:
Aleksandar Petrov
2025-12-01 17:08:56 -04:00
parent b9ae0c98b6
commit 71526b2ab9
36 changed files with 5635 additions and 66 deletions
@@ -2,25 +2,71 @@ package exploremapimpl
import (
"context"
"time"
"github.com/grafana/grafana/pkg/infra/db"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/services/exploremap"
"github.com/grafana/grafana/pkg/services/exploremap/realtime"
"github.com/grafana/grafana/pkg/services/live"
)
type Service struct {
store store
tracer tracing.Tracer
hub *realtime.OperationHub
}
var _ exploremap.Service = &Service{}
func ProvideService(db db.DB, tracer tracing.Tracer) exploremap.Service {
// storeAdapter adapts the internal store interface to the realtime Store interface
type storeAdapter struct {
store store
}
func (s *storeAdapter) Update(ctx context.Context, cmd *exploremap.UpdateExploreMapCommand) (*exploremap.ExploreMapDTO, error) {
return s.store.Update(ctx, cmd)
}
func (s *storeAdapter) Get(ctx context.Context, query *exploremap.GetExploreMapByUIDQuery) (*exploremap.ExploreMapDTO, error) {
// Get returns ExploreMap, but we need ExploreMapDTO
mapData, err := s.store.Get(ctx, query)
if err != nil {
return nil, err
}
return &exploremap.ExploreMapDTO{
UID: mapData.UID,
Title: mapData.Title,
Data: mapData.Data,
CreatedBy: mapData.CreatedBy,
UpdatedBy: mapData.UpdatedBy,
CreatedAt: mapData.CreatedAt,
UpdatedAt: mapData.UpdatedAt,
}, nil
}
func ProvideService(db db.DB, tracer tracing.Tracer, liveService *live.GrafanaLive) exploremap.Service {
store := &sqlStore{
db: db,
}
// Create adapter for realtime hub
adapter := &storeAdapter{store: store}
// Create operation hub for CRDT synchronization
hub := realtime.NewOperationHub(liveService, adapter)
// Register channel handler with Grafana Live
channelHandler := realtime.NewExploreMapChannelHandler(hub)
liveService.GrafanaScope.Features["explore-map"] = channelHandler
// Start background snapshot worker (saves CRDT state to SQL every 30 seconds)
go hub.StartSnapshotWorker(context.Background(), 30*time.Second)
return &Service{
tracer: tracer,
store: &sqlStore{
db: db,
},
store: store,
hub: hub,
}
}