Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SDKS-7838 Show Cached flag sets #261

Merged
merged 11 commits into from
Jan 11, 2024
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ require (
github.com/gin-gonic/gin v1.9.1
github.com/google/uuid v1.3.0
github.com/splitio/gincache v1.0.1
github.com/splitio/go-split-commons/v5 v5.1.2-0.20240108145819-63cfece95155
github.com/splitio/go-toolkit/v5 v5.3.3-0.20240108144147-a36a17c46788
github.com/splitio/go-split-commons/v5 v5.2.0
github.com/splitio/go-toolkit/v5 v5.4.0
github.com/stretchr/testify v1.8.4
go.etcd.io/bbolt v1.3.6
golang.org/x/exp v0.0.0-20231006140011-7918f672742d
Expand Down
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,10 @@ github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUA
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/splitio/gincache v1.0.1 h1:dLYdANY/BqH4KcUMCe/LluLyV5WtuE/LEdQWRE06IXU=
github.com/splitio/gincache v1.0.1/go.mod h1:CcgJDSM9Af75kyBH0724v55URVwMBuSj5x1eCWIOECY=
github.com/splitio/go-split-commons/v5 v5.1.2-0.20240108145819-63cfece95155 h1:SecApJYs4oumtZMIBwnE2DFyImlwiuswXIdZDaW42uE=
github.com/splitio/go-split-commons/v5 v5.1.2-0.20240108145819-63cfece95155/go.mod h1:sdXeIX4UdBf+EPdVAdBC+f2RuFUh/44bqHlTLy3rH/I=
github.com/splitio/go-toolkit/v5 v5.3.3-0.20240108144147-a36a17c46788 h1:URrg0BcgUzFE4pAacFlrWiTrmEdH4SY03XXLLWVtqLc=
github.com/splitio/go-toolkit/v5 v5.3.3-0.20240108144147-a36a17c46788/go.mod h1:xYhUvV1gga9/1029Wbp5pjnR6Cy8nvBpjw99wAbsMko=
github.com/splitio/go-split-commons/v5 v5.2.0 h1:1P66JdUV1Fj1DUeWU1rwkeObqinl9AecRxDsktBsx0g=
github.com/splitio/go-split-commons/v5 v5.2.0/go.mod h1:m1Od/jxiSUJXpdbRvRxTaKeSAdQVem5AZr7AjI4xXn8=
github.com/splitio/go-toolkit/v5 v5.4.0 h1:g5WFpRhQomnXCmvfsNOWV4s5AuUrWIZ+amM68G8NBKM=
github.com/splitio/go-toolkit/v5 v5.4.0/go.mod h1:xYhUvV1gga9/1029Wbp5pjnR6Cy8nvBpjw99wAbsMko=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
Expand Down
Binary file added main
Binary file not shown.
1 change: 1 addition & 0 deletions splitio/admin/controllers/dashboard.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,5 +157,6 @@ func (c *DashboardController) gatherStats() *dashboard.GlobalStats {
LoggedErrors: errorCount,
LoggedMessages: errorMessages,
Uptime: int64(c.runtime.Uptime().Seconds()),
FlagSets: getFlagSetsInfo(c.storages.SplitStorage),
}
}
22 changes: 22 additions & 0 deletions splitio/admin/controllers/helpers.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package controllers

import (
"sort"
"strings"
"time"

"github.com/splitio/go-split-commons/v5/storage"
Expand Down Expand Up @@ -209,6 +211,26 @@ func getEventsSize(eventStorage storage.EventMultiSdkConsumer) int64 {
return eventStorage.Count()
}

func getFlagSetsInfo(splitsStorage storage.SplitStorage) []dashboard.FlagSetsSummary {
flagSetNames := splitsStorage.GetAllFlagSetNames()

summaries := make([]dashboard.FlagSetsSummary, 0, len(flagSetNames))
featureFlagsBySets := splitsStorage.GetNamesByFlagSets(flagSetNames)

for key, featureFlags := range featureFlagsBySets {
summaries = append(summaries, dashboard.FlagSetsSummary{
Name: key,
FeatureFlagsAssociated: int64(len(featureFlags)),
FeatureFlags: strings.Join(featureFlags, ", "),
})
}
sort.Slice(summaries, func(i, j int) bool {
return summaries[j].Name > summaries[i].Name
})

return summaries
}

func getImpressionSize(impressionStorage storage.ImpressionMultiSdkConsumer) int64 {
if impressionStorage == nil {
return 0
Expand Down
13 changes: 10 additions & 3 deletions splitio/admin/controllers/observability.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ import (
"github.com/gin-gonic/gin"
)

type ObservabilityDto struct {
ActiveSplits []string `json:"activeSplits"`
ActiveSegments map[string]int `json:"activeSegments"`
ActiveFlagSets []string `json:"activeFlagSets"`
}

// ObservabilityController interface is used to have a single constructor that returns the apropriate controller
type ObservabilityController interface {
Register(gin.IRouter)
Expand All @@ -30,9 +36,10 @@ func (c *SyncObservabilityController) Register(router gin.IRouter) {
}

func (c *SyncObservabilityController) observability(ctx *gin.Context) {
ctx.JSON(200, gin.H{
"activeSplits": c.splits.SplitNames(),
"activeSegments": c.segments.NamesAndCount(),
ctx.JSON(200, ObservabilityDto{
ActiveSplits: c.splits.SplitNames(),
ActiveSegments: c.segments.NamesAndCount(),
ActiveFlagSets: c.splits.GetAllFlagSetNames(),
})
}

Expand Down
131 changes: 131 additions & 0 deletions splitio/admin/controllers/observability_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
package controllers

import (
"encoding/json"
"io"
"net/http"
"net/http/httptest"
"testing"

"github.com/gin-gonic/gin"
"github.com/splitio/go-split-commons/v5/dtos"
"github.com/splitio/go-split-commons/v5/storage/mocks"
"github.com/splitio/go-toolkit/v5/datastructures/set"
"github.com/splitio/go-toolkit/v5/logging"
adminCommon "github.com/splitio/split-synchronizer/v5/splitio/admin/common"
"github.com/splitio/split-synchronizer/v5/splitio/provisional/observability"
)

func TestSyncObservabilityEndpoint(t *testing.T) {
logger := logging.NewLogger(nil)

extSplitStorage := &extMockSplitStorage{
&mocks.MockSplitStorage{
SplitNamesCall: func() []string {
return []string{"split1", "split2", "split3"}
},
SegmentNamesCall: func() *set.ThreadUnsafeSet {
return set.NewSet("segment1")
},
GetAllFlagSetNamesCall: func() []string {
return []string{"fSet1", "fSet2"}
},
},
nil,
}

extSegmentStorage := &extMockSegmentStorage{
MockSegmentStorage: &mocks.MockSegmentStorage{},
SizeCall: func(name string) (int, error) {
switch name {
case "segment1":
return 10, nil
case "segment2":
return 20, nil
}
return 0, nil
},
}

oSplitStorage, err := observability.NewObservableSplitStorage(extSplitStorage, logger)
if err != nil {
t.Error(err)
return
}

oSegmentStorage, err := observability.NewObservableSegmentStorage(logger, extSplitStorage, extSegmentStorage)
if err != nil {
t.Error(err)
return
}

storages := adminCommon.Storages{
SplitStorage: oSplitStorage,
SegmentStorage: oSegmentStorage,
}

ctrl, err := NewObservabilityController(false, logger, storages)

if err != nil {
t.Error(err)
return
}

resp := httptest.NewRecorder()
ctx, router := gin.CreateTestContext(resp)
ctrl.Register(router)

ctx.Request, _ = http.NewRequest(http.MethodGet, "/observability", nil)
router.ServeHTTP(resp, ctx.Request)

if resp.Code != 200 {
t.Error("hay crap.")
}

body, err := io.ReadAll(resp.Body)
if err != nil {
t.Error(err)
return
}

var result ObservabilityDto
if err := json.Unmarshal(body, &result); err != nil {
t.Error("there should be no error ", err)
}

if len(result.ActiveFlagSets) != 2 {
t.Errorf("Active flag sets should be 2. Actual %d", len(result.ActiveFlagSets))
}

if len(result.ActiveSplits) != 3 {
t.Errorf("Active splits should be 3. Actual %d", len(result.ActiveSplits))
}

if len(result.ActiveSegments) != 1 {
t.Errorf("Active segments should be 1. Actual %d", len(result.ActiveSegments))
}
}

// TODO: should unify this classes
type extMockSplitStorage struct {
*mocks.MockSplitStorage
UpdateWithErrorsCall func([]dtos.SplitDTO, []dtos.SplitDTO, int64) error
}

func (e *extMockSplitStorage) UpdateWithErrors(toAdd []dtos.SplitDTO, toRemove []dtos.SplitDTO, cn int64) error {
return e.UpdateWithErrorsCall(toAdd, toRemove, cn)
}

type extMockSegmentStorage struct {
*mocks.MockSegmentStorage
UpdateWithSummaryCall func(string, *set.ThreadUnsafeSet, *set.ThreadUnsafeSet, int64) (int, int, error)
SizeCall func(string) (int, error)
}

func (e *extMockSegmentStorage) UpdateWithSummary(name string, toAdd *set.ThreadUnsafeSet, toRemove *set.ThreadUnsafeSet, till int64) (added int, removed int, err error) {
return e.UpdateWithSummaryCall(name, toAdd, toRemove, till)
}

func (e *extMockSegmentStorage) Size(name string) (int, error) {
return e.SizeCall(name)
}
Loading