Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/Splitio/Constants/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,5 +65,7 @@ public static class ApiVersions
public static class EventMetadataKeys
{
public static string Flags => "flags";
public static string Segments => "segments";
public static string RuleBasedSegments => "ruleBasedSegments";
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using Splitio.Domain;
using Splitio.Constants;
using Splitio.Domain;
using Splitio.Services.Cache.Interfaces;
using Splitio.Services.Common;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading.Tasks;
Expand All @@ -10,12 +12,15 @@ public class InMemoryRuleBasedSegmentCache : IRuleBasedSegmentCache
{
private readonly ConcurrentDictionary<string, RuleBasedSegment> _cache;
private long _changeNumber;
private readonly EventsManager<SdkEvent, SdkInternalEvent, EventMetadata> _eventsManager;

public InMemoryRuleBasedSegmentCache(ConcurrentDictionary<string, RuleBasedSegment> cache,
EventsManager<SdkEvent, SdkInternalEvent, EventMetadata> eventsManger,
long changeNumber = -1)
{
_cache = cache;
_changeNumber = changeNumber;
_eventsManager = eventsManger;
}

#region Sync Methods
Expand Down Expand Up @@ -45,9 +50,11 @@ public long GetChangeNumber()
// Producer
public void Update(List<RuleBasedSegment> toAdd, List<string> toRemove, long till)
{
List<string> toNotify = new List<string>();
foreach (var rbSegment in toAdd)
{
_cache.AddOrUpdate(rbSegment.Name, rbSegment, (key, oldValue) => rbSegment);
toNotify.Add(rbSegment.Name);
}

foreach (var name in toRemove)
Expand All @@ -56,6 +63,10 @@ public void Update(List<RuleBasedSegment> toAdd, List<string> toRemove, long til
}

SetChangeNumber(till);
_eventsManager.NotifyInternalEvent(SdkInternalEvent.RuleBasedSegmentsUpdated,
new EventMetadata(new Dictionary<string, object> { { EventMetadataKeys.RuleBasedSegments, toNotify } }),
Splitio.Util.Helper.GetSdkEventIfApplicable(SdkInternalEvent.RuleBasedSegmentsUpdated,
_eventsManager));
}

public void SetChangeNumber(long changeNumber)
Expand Down
16 changes: 14 additions & 2 deletions src/Splitio/Services/Cache/Classes/InMemorySegmentCache.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using Splitio.Domain;
using Splitio.Constants;
using Splitio.Domain;
using Splitio.Services.Cache.Interfaces;
using Splitio.Services.Common;
using Splitio.Services.Logger;
using Splitio.Services.Shared.Classes;
using System.Collections.Concurrent;
Expand All @@ -13,10 +15,12 @@ public class InMemorySegmentCache : ISegmentCache
private readonly ISplitLogger _log = WrapperAdapter.Instance().GetLogger(typeof(InMemorySegmentCache));

private readonly ConcurrentDictionary<string, Segment> _segments;
private readonly EventsManager<SdkEvent, SdkInternalEvent, EventMetadata> _eventsManager;

public InMemorySegmentCache(ConcurrentDictionary<string, Segment> segments)
public InMemorySegmentCache(ConcurrentDictionary<string, Segment> segments, EventsManager<SdkEvent, SdkInternalEvent, EventMetadata> eventsManger)
{
_segments = segments;
_eventsManager = eventsManger;
}

#region Methods Sync
Expand All @@ -31,13 +35,21 @@ public void AddToSegment(string segmentName, List<string> segmentKeys)
}

segment.AddKeys(segmentKeys);
_eventsManager.NotifyInternalEvent(SdkInternalEvent.SegmentsUpdated,
new EventMetadata(new Dictionary<string, object> { { EventMetadataKeys.Segments, segmentName } }),
Splitio.Util.Helper.GetSdkEventIfApplicable(SdkInternalEvent.SegmentsUpdated,
_eventsManager));
}

public void RemoveFromSegment(string segmentName, List<string> segmentKeys)
{
if (_segments.TryGetValue(segmentName, out Segment segment))
{
segment.RemoveKeys(segmentKeys);
_eventsManager.NotifyInternalEvent(SdkInternalEvent.SegmentsUpdated,
new EventMetadata(new Dictionary<string, object> { { EventMetadataKeys.Segments, segmentName } }),
Splitio.Util.Helper.GetSdkEventIfApplicable(SdkInternalEvent.SegmentsUpdated,
_eventsManager));
}
}

Expand Down
7 changes: 3 additions & 4 deletions src/Splitio/Services/Client/Classes/JSONFileClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,9 @@ public JSONFileClient(string splitsFilePath,
IRuleBasedSegmentCache ruleBasedSegmentCache = null
) : base("localhost", fallbackTreatmentCalculator)
{
_segmentCache = segmentCacheInstance ?? new InMemorySegmentCache(new ConcurrentDictionary<string, Segment>());
var rbsCache = ruleBasedSegmentCache ?? new InMemoryRuleBasedSegmentCache(new ConcurrentDictionary<string, RuleBasedSegment>());
var eventsManager = new EventsManager<SdkEvent, SdkInternalEvent, EventMetadata>(new EventsManagerConfig());
_segmentCache = segmentCacheInstance ?? new InMemorySegmentCache(new ConcurrentDictionary<string, Segment>(), eventsManager);
var rbsCache = ruleBasedSegmentCache ?? new InMemoryRuleBasedSegmentCache(new ConcurrentDictionary<string, RuleBasedSegment>(), eventsManager);

var segmentFetcher = new JSONFileSegmentFetcher(segmentsFilePath, _segmentCache);
var splitChangeFetcher = new JSONFileSplitChangeFetcher(splitsFilePath);
Expand All @@ -54,8 +55,6 @@ public JSONFileClient(string splitsFilePath,
}

BuildFlagSetsFilter(new HashSet<string>());
var eventsManager = new EventsManager<SdkEvent, SdkInternalEvent, EventMetadata>(new EventsManagerConfig());

_featureFlagCache = featureFlagCacheInstance ?? new InMemorySplitCache(new ConcurrentDictionary<string, ParsedSplit>(parsedSplits), _flagSetsFilter, eventsManager);
_impressionsLog = impressionsLog;
_eventsLog = eventsLog;
Expand Down
4 changes: 2 additions & 2 deletions src/Splitio/Services/Client/Classes/SelfRefreshingClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,12 +101,12 @@ private void BuildSplitCache()

private void BuildSegmentCache()
{
_segmentCache = new InMemorySegmentCache(new ConcurrentDictionary<string, Segment>(_config.ConcurrencyLevel, InitialCapacity));
_segmentCache = new InMemorySegmentCache(new ConcurrentDictionary<string, Segment>(_config.ConcurrencyLevel, InitialCapacity), _eventsManager);
}

private void BuildRuleBasedSegmentCache()
{
_ruleBasedSegmentCache = new InMemoryRuleBasedSegmentCache(new ConcurrentDictionary<string, RuleBasedSegment>(_config.ConcurrencyLevel, InitialCapacity));
_ruleBasedSegmentCache = new InMemoryRuleBasedSegmentCache(new ConcurrentDictionary<string, RuleBasedSegment>(_config.ConcurrencyLevel, InitialCapacity), _eventsManager);
}

private void BuildTelemetryStorage()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Splitio.Domain;
using Splitio.Services.Cache.Classes;
using Splitio.Services.Common;
using Splitio.Services.SegmentFetcher.Classes;
using System.Collections.Concurrent;

Expand All @@ -26,7 +27,8 @@ public SelfRefreshingSegmentFetcherTests()
public void ExecuteGetSuccessfulWithResultsFromJSONFile()
{
//Arrange
var segmentCache = new InMemorySegmentCache(new ConcurrentDictionary<string, Segment>());
var eventsManager = new EventsManager<SdkEvent, SdkInternalEvent, EventMetadata>(new EventsManagerConfig());
var segmentCache = new InMemorySegmentCache(new ConcurrentDictionary<string, Segment>(), eventsManager);

var segmentFetcher = new JSONFileSegmentFetcher($"{rootFilePath}segment_payed.json", segmentCache);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,13 @@ public TargetingRulesFetcherTests()
public async Task ExecuteGetSuccessfulWithResultsFromJSONFile()
{
//Arrange
var segmentCache = new InMemorySegmentCache(new ConcurrentDictionary<string, Segment>());
var rbsCache = new InMemoryRuleBasedSegmentCache(new ConcurrentDictionary<string, RuleBasedSegment>());
var eventsManager = new EventsManager<SdkEvent, SdkInternalEvent, EventMetadata>(new EventsManagerConfig());
var segmentCache = new InMemorySegmentCache(new ConcurrentDictionary<string, Segment>(), eventsManager);
var rbsCache = new InMemoryRuleBasedSegmentCache(new ConcurrentDictionary<string, RuleBasedSegment>(), eventsManager);
var segmentFetcher = new JSONFileSegmentFetcher($"{rootFilePath}segment_payed.json", segmentCache);
var splitParser = new FeatureFlagParser(segmentCache, segmentFetcher);
var splitChangeFetcher = new JSONFileSplitChangeFetcher($"{rootFilePath}splits_staging.json");
var flagSetsFilter = new FlagSetsFilter(new HashSet<string>());
var eventsManager = new EventsManager<SdkEvent, SdkInternalEvent, EventMetadata>(new EventsManagerConfig());
var splitCache = new InMemorySplitCache(new ConcurrentDictionary<string, ParsedSplit>(), flagSetsFilter, eventsManager);
var gates = new InMemoryReadinessGatesCache();
var taskManager = new TasksManager(gates);
Expand Down Expand Up @@ -84,13 +84,13 @@ public async Task ExecuteGetSuccessfulWithResultsFromJSONFile()
public async Task ExecuteGetSuccessfulWithResultsFromJSONFileIncludingTrafficAllocation()
{
//Arrange
var segmentCache = new InMemorySegmentCache(new ConcurrentDictionary<string, Segment>());
var rbsCache = new InMemoryRuleBasedSegmentCache(new ConcurrentDictionary<string, RuleBasedSegment>());
var eventsManager = new EventsManager<SdkEvent, SdkInternalEvent, EventMetadata>(new EventsManagerConfig());
var segmentCache = new InMemorySegmentCache(new ConcurrentDictionary<string, Segment>(), eventsManager);
var rbsCache = new InMemoryRuleBasedSegmentCache(new ConcurrentDictionary<string, RuleBasedSegment>(), eventsManager);
var segmentFetcher = new JSONFileSegmentFetcher($"{rootFilePath}segment_payed.json", segmentCache);
var splitParser = new FeatureFlagParser(segmentCache, segmentFetcher);
var splitChangeFetcher = new JSONFileSplitChangeFetcher($"{rootFilePath}splits_staging_4.json");
var flagSetsFilter = new FlagSetsFilter(new HashSet<string>());
var eventsManager = new EventsManager<SdkEvent, SdkInternalEvent, EventMetadata>(new EventsManagerConfig());
var splitCache = new InMemorySplitCache(new ConcurrentDictionary<string, ParsedSplit>(), flagSetsFilter, eventsManager);
var gates = new InMemoryReadinessGatesCache();
var taskManager = new TasksManager(gates);
Expand Down Expand Up @@ -142,16 +142,16 @@ public async Task ExecuteGetWithoutResults()
var sdkSegmentApiClient = new SegmentSdkApiClient(httpClient, telemetryStorage, baseUrl);
var apiSegmentChangeFetcher = new ApiSegmentChangeFetcher(sdkSegmentApiClient);
var gates = new InMemoryReadinessGatesCache();
var segmentCache = new InMemorySegmentCache(new ConcurrentDictionary<string, Segment>());
var eventsManager = new EventsManager<SdkEvent, SdkInternalEvent, EventMetadata>(new EventsManagerConfig());
var segmentCache = new InMemorySegmentCache(new ConcurrentDictionary<string, Segment>(), eventsManager);
var segmentsQueue = new SplitQueue<SelfRefreshingSegment>();
var taskManager = new TasksManager(gates);
var worker = new SegmentTaskWorker(4, segmentsQueue);
segmentsQueue.AddObserver(worker);
var segmentsTask = taskManager.NewPeriodicTask(Splitio.Enums.Task.SegmentsFetcher, 3000);
var segmentFetcher = new SelfRefreshingSegmentFetcher(apiSegmentChangeFetcher, segmentCache, segmentsQueue, segmentsTask, gates);
var rbsCache = new InMemoryRuleBasedSegmentCache(new ConcurrentDictionary<string, RuleBasedSegment>());
var rbsCache = new InMemoryRuleBasedSegmentCache(new ConcurrentDictionary<string, RuleBasedSegment>(), eventsManager);
var splitParser = new FeatureFlagParser(segmentCache, segmentFetcher);
var eventsManager = new EventsManager<SdkEvent, SdkInternalEvent, EventMetadata>(new EventsManagerConfig());
var splitCache = new InMemorySplitCache(new ConcurrentDictionary<string, ParsedSplit>(), flagSetsFilter, eventsManager);
var task = taskManager.NewPeriodicTask(Splitio.Enums.Task.FeatureFlagsFetcher, 3000);
var featureFlagSyncService = new FeatureFlagUpdater(splitParser, splitCache, flagSetsFilter, rbsCache);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Splitio.Domain;
using Splitio.Services.Cache.Classes;
using Splitio.Services.Common;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading.Tasks;
Expand All @@ -11,12 +13,17 @@ namespace Splitio_Tests.Unit_Tests.Cache.InMemory
public class RuleBasedSegmentCacheTests
{
private InMemoryRuleBasedSegmentCache _segmentCache;
private EventsManager<SdkEvent, SdkInternalEvent, EventMetadata> _eventsManager;
private bool SdkUpdate = false;
private EventMetadata eMetadata = null;
public event EventHandler<EventMetadata> PublicSdkUpdateHandler;

[TestInitialize]
public void Setup()
{
var cache = new ConcurrentDictionary<string, RuleBasedSegment>();
_segmentCache = new InMemoryRuleBasedSegmentCache(cache);
_eventsManager = new EventsManager<SdkEvent, SdkInternalEvent, EventMetadata>(new EventsManagerConfig());
_segmentCache = new InMemoryRuleBasedSegmentCache(cache, _eventsManager);
}

[TestMethod]
Expand Down Expand Up @@ -142,5 +149,40 @@ public void Contains_ShouldReturnTrue()
Assert.IsFalse(_segmentCache.Contains(new List<string> { "segment1", "segment3" }));
Assert.IsTrue(_segmentCache.Contains(new List<string> { "segment1", "segment2" }));
}

[TestMethod]
public void Update_ShouldNotifyEvent()
{
// Arrange
Splitio.Util.Helper.BuildInternalSdkEventStatus(_eventsManager);

var segmentToAdd = new RuleBasedSegment { Name = "segment-to-add" };
var segmentToRemove = new RuleBasedSegment { Name = "segment-to-remove" };
var till = 67890;
var toNotify = new List<string> { { "segment-to-add" } };
PublicSdkUpdateHandler += sdkUpdate_callback;
_eventsManager.Register(SdkEvent.SdkUpdate, sdkUpdate_callback);
_eventsManager.Register(SdkEvent.SdkReady, sdkUpdate_callback);
_eventsManager.NotifyInternalEvent(SdkInternalEvent.SdkReady, new EventMetadata(new Dictionary<string, object>()),
Splitio.Util.Helper.GetSdkEventIfApplicable(SdkInternalEvent.SdkReady, _eventsManager));

// Act
SdkUpdate = false;
_segmentCache.Update(new List<RuleBasedSegment> { segmentToAdd, segmentToRemove }, new List<string> { segmentToRemove.Name }, till);

// Assert
Assert.IsTrue(SdkUpdate);
Assert.IsTrue(eMetadata.ContainKey(Splitio.Constants.EventMetadataKeys.RuleBasedSegments));
List<string> rbsegments = (List<string>)eMetadata.GetData()[Splitio.Constants.EventMetadataKeys.RuleBasedSegments];
Assert.IsTrue(rbsegments.Count == 2);
Assert.IsTrue(rbsegments.Contains("segment-to-add"));
Assert.IsTrue(rbsegments.Contains("segment-to-remove"));
}

private void sdkUpdate_callback(object sender, EventMetadata metadata)
{
SdkUpdate = true;
eMetadata = metadata;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,29 @@
using Splitio.Domain;
using Splitio.Services.Cache.Classes;
using Splitio.Services.Cache.Interfaces;
using Splitio.Services.Common;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading.Tasks;
using WireMock.Pact.Models.V2;

namespace Splitio_Tests.Unit_Tests.Cache
{
[TestClass]
public class SegmentCacheAsyncTests
{
private readonly ISegmentCache _cache;
private EventsManager<SdkEvent, SdkInternalEvent, EventMetadata> _eventsManager;
private bool SdkUpdate = false;
private EventMetadata eMetadata = null;
public event EventHandler<EventMetadata> PublicSdkUpdateHandler;

public SegmentCacheAsyncTests()
{
var segments = new ConcurrentDictionary<string, Segment>();

_cache = new InMemorySegmentCache(segments);
_eventsManager = new EventsManager<SdkEvent, SdkInternalEvent, EventMetadata>(new EventsManagerConfig());
_cache = new InMemorySegmentCache(segments, _eventsManager);
}

[TestMethod]
Expand Down Expand Up @@ -47,5 +54,35 @@ public async Task IsInSegmentAsyncTestTrue()
//Assert
Assert.IsTrue(result);
}

[TestMethod]
public async Task NotifyEventsTest()
{
//Arrange
var segmentName = "segment_test";
Splitio.Util.Helper.BuildInternalSdkEventStatus(_eventsManager);
var toNotify = new List<string> { { segmentName } };
PublicSdkUpdateHandler += sdkUpdate_callback;
_eventsManager.Register(SdkEvent.SdkUpdate, sdkUpdate_callback);
_eventsManager.Register(SdkEvent.SdkReady, sdkUpdate_callback);
_eventsManager.NotifyInternalEvent(SdkInternalEvent.SdkReady, new EventMetadata(new Dictionary<string, object>()),
Splitio.Util.Helper.GetSdkEventIfApplicable(SdkInternalEvent.SdkReady, _eventsManager));

//Act
SdkUpdate = false;
_cache.AddToSegment(segmentName, new List<string> { "abcd", "zzzzf" });

//Assert
Assert.IsTrue(SdkUpdate);
Assert.IsTrue(eMetadata.ContainKey(Splitio.Constants.EventMetadataKeys.Segments));
string segment = (string)eMetadata.GetData()[Splitio.Constants.EventMetadataKeys.Segments];
Assert.AreEqual(segmentName, segment);
}

private void sdkUpdate_callback(object sender, EventMetadata metadata)
{
SdkUpdate = true;
eMetadata = metadata;
}
}
}
Loading