Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
108 commits
Select commit Hold shift + click to select a range
c82ea14
add models
chillaq Dec 4, 2025
c6983d4
Update SdkInternalEvent.cs
chillaq Dec 5, 2025
36e40ea
Merge pull request #310 from splitio/FME-11587-models
chillaq Dec 5, 2025
cb26c84
added events manager config
chillaq Dec 5, 2025
4b27df3
Added sdk ready event
chillaq Dec 5, 2025
8a61742
removed large segment from config
chillaq Dec 5, 2025
137f79a
Merge pull request #311 from splitio/FME-11586-config
chillaq Dec 5, 2025
3571825
initial draft - events manager and supporting classes
chillaq Dec 8, 2025
87a5284
added concurrent dictionary for storage
chillaq Dec 9, 2025
c60d844
Added event delivery queue and tests
chillaq Dec 9, 2025
80d8fc6
added event handler and event delivery classes
chillaq Dec 10, 2025
376e739
added event manager and handler
chillaq Dec 10, 2025
6077a51
Merge branch 'feature/sdk-update-event' into FME-11584-events-manager…
chillaq Dec 10, 2025
37874f1
updated requirement for sdkready event
chillaq Dec 10, 2025
b92e3ab
updated IEventManager interface
chillaq Dec 10, 2025
e342316
polishing
chillaq Dec 10, 2025
750d854
polis
chillaq Dec 10, 2025
f2d005a
polish
chillaq Dec 11, 2025
d5e2332
Update pipeline dotnet-client-repo
chillaq Dec 11, 2025
a36a11f
polish
chillaq Dec 11, 2025
28edf36
Merge branch 'FME-11584-events-manager-handler' of https://github.com…
chillaq Dec 11, 2025
d899bdc
polish
chillaq Dec 11, 2025
5dd4ee7
polish
chillaq Dec 11, 2025
391258b
Removed internal event subscriptions, moved handler logic to manager.
chillaq Dec 11, 2025
83d98c1
polishing
chillaq Dec 11, 2025
d3d0240
polish
chillaq Dec 11, 2025
f054f13
fixed suppressed by condition
chillaq Dec 12, 2025
29b6a04
added generic type events
chillaq Dec 12, 2025
e9732e3
Used abstract events in EventsManager class
chillaq Dec 13, 2025
7c4f85e
polish
chillaq Dec 13, 2025
4948606
fixed build error
chillaq Dec 13, 2025
ed0a724
replaced config data struct with class
chillaq Dec 16, 2025
b614733
removed static config manager constructor
chillaq Dec 16, 2025
a5e771f
polish
chillaq Dec 17, 2025
1bc8bfd
added config as a parameter to events manager to be used by helper
chillaq Dec 17, 2025
d4a630e
replace event prop dict with struct
chillaq Dec 17, 2025
9ab18e2
added firing sdk events to split cache
chillaq Dec 17, 2025
86f483c
Merge pull request #313 from splitio/FME-11584-events-manager-handler
chillaq Dec 17, 2025
bb47dd2
polish
chillaq Dec 17, 2025
73c8746
added event notification to segments and rbsegments cache
chillaq Dec 17, 2025
7c04c2e
Merge pull request #315 from splitio/FME-11583-events-split-cache
chillaq Dec 17, 2025
39fa419
fixed building jsonlocal client
chillaq Dec 17, 2025
43956b6
Merge branch 'feature/sdk-update-event' into FME-11582-event-segments
chillaq Dec 17, 2025
2e361c1
added events for ready and timedout
chillaq Dec 18, 2025
435f395
polish
chillaq Dec 18, 2025
b6d8a66
Update src/Splitio/Services/Cache/Classes/InMemoryRuleBasedSegmentCac…
chillaq Dec 22, 2025
376b7ad
Update src/Splitio/Services/Cache/Classes/InMemoryRuleBasedSegmentCac…
chillaq Dec 22, 2025
00f0868
Update src/Splitio/Services/Cache/Classes/InMemorySegmentCache.cs
chillaq Dec 22, 2025
5bd554e
Update src/Splitio/Services/Cache/Classes/InMemorySegmentCache.cs
chillaq Dec 22, 2025
9c32ca0
Added notify when removing rbsegment
chillaq Dec 22, 2025
492d2e2
fixed test
chillaq Dec 22, 2025
015310e
moved helper fucntions to events manager class
chillaq Dec 22, 2025
9621a26
applied event manager changes
chillaq Dec 22, 2025
47d57c0
Merge branch 'FME-11582-event-segments' into FME-11754-events-ready-t…
chillaq Dec 22, 2025
4af93a3
polish
chillaq Dec 23, 2025
c5ea092
polish
chillaq Dec 23, 2025
99e44ce
Merge branch 'FME-11754-events-ready-timedout' of https://github.com/…
chillaq Dec 23, 2025
6d50715
polish
chillaq Dec 23, 2025
e7af0f7
polish
chillaq Dec 23, 2025
b93bdc0
refactor event delivery to use action callback
chillaq Dec 23, 2025
4520c7c
refactor delivery and event manager to use action callback
chillaq Dec 23, 2025
26a78f8
polish
chillaq Dec 23, 2025
9415ac1
polish
chillaq Dec 23, 2025
a1d89a9
passed EventDelivery as a param to EventsManager
chillaq Dec 23, 2025
c0d0b87
add EventDelivery as a paramater to EventsManager
chillaq Dec 24, 2025
748a6d5
Updated client and integration tests
chillaq Dec 24, 2025
fcf4352
Merge branch 'FME-11754-events-ready-timedout' into FME-11652-client-…
chillaq Dec 24, 2025
9f58ade
Merge pull request #316 from splitio/FME-11582-event-segments
chillaq Dec 26, 2025
4516fab
moved creating eventsManager and FallbackTreatment instances to client
chillaq Dec 26, 2025
96a82b1
Merge branch 'feature/sdk-update-event' into FME-11754-events-ready-t…
chillaq Dec 26, 2025
5d3ac98
Update src/Splitio/Services/Cache/Classes/InMemoryReadinessGatesCache.cs
chillaq Dec 26, 2025
1decd32
Merge branch 'FME-11754-events-ready-timedout' into FME-11652-client-…
chillaq Dec 26, 2025
855bb4f
fixed splitclient
chillaq Dec 26, 2025
b1207b7
Merge pull request #317 from splitio/FME-11754-events-ready-timedout
chillaq Dec 29, 2025
19084ab
Merge pull request #318 from splitio/FME-11652-client-integration
chillaq Dec 29, 2025
3bb6e2b
added sorting events before notifying
chillaq Dec 30, 2025
10f12f1
Merge pull request #319 from splitio/FME-12052-sort-events
chillaq Dec 30, 2025
5b8b99a
applied fallback validation
chillaq Dec 30, 2025
c70e9d8
polish
chillaq Dec 30, 2025
70fb6af
reduce logging for events
chillaq Dec 31, 2025
53917bc
moved validation to config service
chillaq Jan 2, 2026
7ae1aab
changed fallback calc param to interface
chillaq Jan 5, 2026
a6df6cf
Merge pull request #320 from splitio/FME-12084-fallback-validation
chillaq Jan 5, 2026
a0533f7
updated metadata with Sdk event type
chillaq Jan 23, 2026
f781b25
removed list of rbsegments in notification
chillaq Jan 23, 2026
d9a7843
Merge pull request #322 from splitio/sdk-events-metadata
chillaq Jan 23, 2026
cc5d5bf
added InternalEventsTask to use a queue for firing internal events
chillaq Jan 23, 2026
ef78c18
added exception handling for add to queue task
chillaq Jan 26, 2026
b30127a
updated sdk block until ready service
chillaq Jan 26, 2026
bb1473c
added try catch when running the event callback
chillaq Jan 26, 2026
bc10a93
polish
chillaq Jan 26, 2026
8ff55a7
added threadpool for event handler call
chillaq Jan 26, 2026
f1d8a3b
use Task to add to queue and removed timeout event
chillaq Jan 29, 2026
b623041
polishing
chillaq Jan 29, 2026
7b6de44
cleanup
chillaq Jan 29, 2026
b7cf44b
updated version and changes
chillaq Jan 29, 2026
92c153b
moved sdkready fire to sync manager class
chillaq Jan 29, 2026
2d3758c
added imp prop to redis impressions
chillaq Jan 30, 2026
9efb436
suggestions
sanzmauro Jan 30, 2026
f9d8f89
update internalEvents
sanzmauro Jan 30, 2026
f8a1953
removed event test from sync manager
chillaq Jan 30, 2026
b34174f
cleanup
sanzmauro Jan 30, 2026
20e738d
Merge pull request #326 from splitio/events-suggestions
sanzmauro Jan 30, 2026
0d2ba07
Merge pull request #325 from splitio/FME-12323-redis-imp-prop
chillaq Jan 30, 2026
dd0786c
Merge pull request #323 from splitio/FME-12320-events-task
sanzmauro Jan 30, 2026
b3464e4
prevent event fire with only changenumber update
chillaq Jan 31, 2026
62f55e3
added tests
chillaq Jan 31, 2026
9244bb4
updated changes and added notice
chillaq Feb 2, 2026
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: 1 addition & 1 deletion .harness/pipeline.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ pipeline:
-CommitSha "<+codebase.commitSha>" `
-SonarProjectKey "dotnet-client" `
-SonarToken "<+secrets.getValue('sonarqube-token')>" `
-SonarUrl "https://sonar.harness.io/"
-SonarUrl "https://sonar.harness.io"
- parallel:
- step:
type: Run
Expand Down
6 changes: 6 additions & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
CHANGES

7.13.0 (Feb 2, 2026)
- Fixed impressions properties format in redis mode.
- Added the ability to listen to different events triggered by the SDK. Read more in our docs.
- SDK_UPDATE notify when a flag or user segment has changed
- SDK_READY notify when the SDK is ready to evaluate

7.12.0 (Sep 30, 2025)
- Added new configuration for Fallback Treatments, which allows setting a treatment value and optional config to be returned in place of "control", either globally or by flag. Read more in our docs.
- Added a maximum size payload when posting unique keys telemetry in batches
Expand Down
5 changes: 5 additions & 0 deletions NOTICE.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Harness Feature Management .NET SDK Copyright 2024-2026 Harness Inc.

This product includes software developed at Harness Inc. (https://harness.io/).

This product includes software originally developed by Split Software, Inc. (https://www.split.io/). Copyright 2015-2024 Split Software, Inc.
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,13 @@ public async Task RecordImpressionsCountAsync(Dictionary<string, int> impression
}
}

private RedisValue[] GetImpressions(IList<KeyImpression> items)
// public for tests
public RedisValue[] GetImpressions(IList<KeyImpression> items)
{
var impressions = items.Select(item => JsonConvertWrapper.SerializeObject(new
var impressions = items.Select(item => JsonConvertWrapper.SerializeObjectIgnoreNullValue(new
{
m = new { s = SdkVersion, i = MachineIp, n = MachineName },
i = new { k = item.keyName, b = item.bucketingKey, f = item.feature, t = item.treatment, r = item.label, c = item.changeNumber, m = item.time, pt = item.previousTime }
i = new { k = item.keyName, b = item.bucketingKey, f = item.feature, t = item.treatment, r = item.label, c = item.changeNumber, m = item.time, pt = item.previousTime, properties = item.properties }
}));

return impressions
Expand Down
6 changes: 3 additions & 3 deletions Splitio.Redis/Services/Client/Classes/RedisClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,14 @@ public class RedisClient : SplitClient
private IImpressionsCache _impressionsCache;
private ConnectionPoolManager _connectionPoolManager;
private IFeatureFlagCacheConsumer _featureFlagCacheConsumer;
private readonly new FallbackTreatmentCalculator _fallbackTreatmentCalculator;

public RedisClient(ConfigurationOptions config, string apiKey, FallbackTreatmentCalculator fallbackTreatmentCalculator) : base(apiKey, fallbackTreatmentCalculator)
public RedisClient(ConfigurationOptions config, string apiKey) : base(apiKey)
{
_config = new RedisConfig();
_fallbackTreatmentCalculator = fallbackTreatmentCalculator;

ReadConfig(config);

BuildFallbackCalculator(_config.FallbackTreatments);
BuildRedisCache();
BuildTreatmentLog(config.ImpressionListener);

Expand Down Expand Up @@ -77,6 +76,7 @@ private void ReadConfig(ConfigurationOptions config)
_config.FlagSetsInvalid = baseConfig.FlagSetsInvalid;
_config.Mode = config.Mode;
_config.FromCacheAdapterConfig(config.CacheAdapterConfig);
_config.FallbackTreatments = baseConfig.FallbackTreatments;
}

private void BuildRedisCache()
Expand Down
2 changes: 1 addition & 1 deletion Splitio.Redis/Splitio.Redis.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<Version>7.12.0</Version>
<Version>7.13.0-rc1</Version>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>SplitioRedis.snk</AssemblyOriginatorKeyFile>
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
Expand Down
2 changes: 1 addition & 1 deletion src/Splitio/Constants/Constants.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
namespace Splitio.Constants
{
public static class Push
{
{
public static string ControlPri => "control_pri";
public static string ControlSec => "control_sec";
public static string OccupancyPrefix => "[?occupancy=metrics.publishers]";
Expand Down
1 change: 1 addition & 0 deletions src/Splitio/Domain/BaseConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ public class BaseConfig
public ImpressionsMode ImpressionsMode { get; set; }
public HashSet<string> FlagSetsFilter { get; set; }
public int FlagSetsInvalid { get; set; }
public FallbackTreatmentsConfiguration FallbackTreatments { get; set; }

// Bloom Filter
public int BfExpectedElements { get; set; }
Expand Down
14 changes: 14 additions & 0 deletions src/Splitio/Domain/EventManagerConfigData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using System.Collections.Generic;

namespace Splitio.Domain
{
public class EventManagerConfigData<E, I>
{
public Dictionary<E, HashSet<I>> RequireAll { get; protected set ; }
public Dictionary<E, HashSet<I>> RequireAny { get; protected set; }
public Dictionary<E, HashSet<E>> Prerequisites { get; protected set; }
public Dictionary<E, HashSet<E>> SuppressedBy { get; protected set; }
public Dictionary<E, int> ExecutionLimits { get; protected set; }
public HashSet<E> EvaluationOrder { get; protected set; }
}
}
20 changes: 20 additions & 0 deletions src/Splitio/Domain/EventMetadata.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System.Collections.Generic;

namespace Splitio.Domain
{
public class EventMetadata
{
private readonly List<string> _names;
private readonly SdkEventType _type;

public EventMetadata(SdkEventType type, List<string> names)
{
_type = type;
_names = names;
}

public List<string> GetNames() { return _names; }

public SdkEventType GetEventType() { return _type; }
}
}
92 changes: 92 additions & 0 deletions src/Splitio/Domain/EventsManagerConfig.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
using System.Collections.Generic;
using System.Linq;

namespace Splitio.Domain
{
public class EventsManagerConfig : EventManagerConfigData<SdkEvent, SdkInternalEvent>
{
public EventsManagerConfig()
{
RequireAll = new Dictionary<SdkEvent, HashSet<SdkInternalEvent>>
{
{
SdkEvent.SdkReady, new HashSet<SdkInternalEvent>
{
SdkInternalEvent.SdkReady
}
}
};

Prerequisites = new Dictionary<SdkEvent, HashSet<SdkEvent>>
{
{
SdkEvent.SdkUpdate, new HashSet<SdkEvent>
{
SdkEvent.SdkReady
}
}
};

RequireAny = new Dictionary<SdkEvent, HashSet<SdkInternalEvent>>
{
{ SdkEvent.SdkUpdate, new HashSet<SdkInternalEvent>
{
SdkInternalEvent.RuleBasedSegmentsUpdated,
SdkInternalEvent.FlagsUpdated,
SdkInternalEvent.FlagKilledNotification,
SdkInternalEvent.SegmentsUpdated
}
}
};

SuppressedBy = new Dictionary<SdkEvent, HashSet<SdkEvent>>();

ExecutionLimits = new Dictionary<SdkEvent, int>
{
{ SdkEvent.SdkReady, 1 },
{ SdkEvent.SdkUpdate, -1 }
};

HashSet<SdkEvent> sortedEvents = new HashSet<SdkEvent>();
foreach (SdkEvent sdkEvent in new List<SdkEvent> { SdkEvent.SdkReady, SdkEvent.SdkUpdate })
{
sortedEvents = DFSRecursive(sdkEvent, sortedEvents);
}

EvaluationOrder = sortedEvents;
}

private HashSet<SdkEvent> DFSRecursive(SdkEvent sdkEvent, HashSet<SdkEvent> added)
{
if (added.Contains(sdkEvent)) return added;

foreach (SdkEvent dependentEvent in GetDependencies(sdkEvent))
{
added = DFSRecursive(dependentEvent, added);
}

added.Add(sdkEvent);

return added;
}

private HashSet<SdkEvent> GetDependencies(SdkEvent sdkEvent)
{
HashSet<SdkEvent> dependencies = new HashSet<SdkEvent>();
foreach (KeyValuePair<SdkEvent, HashSet<SdkEvent>> prerequisitesEvent in Prerequisites.Where(x => x.Key.Equals(sdkEvent)))
{
foreach (var prereqEvent in prerequisitesEvent.Value)
{
dependencies.Add(prereqEvent);
}
}

foreach (KeyValuePair<SdkEvent, HashSet<SdkEvent>> suppressedEvent in SuppressedBy.Where(x => x.Value.Contains(sdkEvent)))
{
dependencies.Add(suppressedEvent.Key);
}

return dependencies;
}
}
}
9 changes: 9 additions & 0 deletions src/Splitio/Domain/SdkEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@

namespace Splitio.Domain
{
public enum SdkEvent
{
SdkUpdate,
SdkReady
}
}
16 changes: 16 additions & 0 deletions src/Splitio/Domain/SdkEventNotification.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using Splitio.Domain;

namespace Splitio.Services.EventSource.Workers
{
public class SdkEventNotification
{
public SdkInternalEvent SdkInternalEvent { get; set; }
public EventMetadata EventMetadata { get; set; }

public SdkEventNotification(SdkInternalEvent sdkInternalEvent, EventMetadata eventMetadata)
{
SdkInternalEvent = sdkInternalEvent;
EventMetadata = eventMetadata;
}
}
}
9 changes: 9 additions & 0 deletions src/Splitio/Domain/SdkEventType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@

namespace Splitio.Domain
{
public enum SdkEventType
{
SegmentsUpdate,
FlagsUpdate
}
}
13 changes: 13 additions & 0 deletions src/Splitio/Domain/SdkInternalEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@

namespace Splitio.Domain
{
public enum SdkInternalEvent
{
FlagsUpdated,
FlagKilledNotification,
RuleBasedSegmentsUpdated,
SegmentsUpdated,
LargeSegmentsUpdated,
SdkReady
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
using Splitio.Services.Cache.Interfaces;
using Splitio.Domain;
using Splitio.Services.Cache.Interfaces;
using Splitio.Services.Tasks;
using System.Threading;
using System.Threading.Tasks;

namespace Splitio.Services.Client.Classes
{
public class InMemoryReadinessGatesCache : IStatusManager
{
private readonly CountdownEvent _sdkReady = new CountdownEvent(1);
private readonly CountdownEvent _sdkDestroyed = new CountdownEvent(1);
private readonly IInternalEventsTask _internalEventsTask;

public InMemoryReadinessGatesCache(IInternalEventsTask internalEventsTask)
{
_internalEventsTask = internalEventsTask;
}

public bool IsReady()
{
Expand All @@ -18,9 +27,10 @@ public bool WaitUntilReady(int milliseconds)
return _sdkReady.Wait(milliseconds);
}

public void SetReady()
public async Task SetReadyAsync()
{
_sdkReady.Signal();
await _internalEventsTask.AddToQueue(SdkInternalEvent.SdkReady, null);
}

public void SetDestroy()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
using Splitio.Domain;
using Splitio.Services.Cache.Interfaces;
using Splitio.Services.Logger;
using Splitio.Services.Shared.Classes;
using Splitio.Services.Tasks;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading.Tasks;
Expand All @@ -10,12 +13,16 @@ public class InMemoryRuleBasedSegmentCache : IRuleBasedSegmentCache
{
private readonly ConcurrentDictionary<string, RuleBasedSegment> _cache;
private long _changeNumber;
private readonly IInternalEventsTask _internalEventsTask;
private readonly ISplitLogger _log = WrapperAdapter.Instance().GetLogger(typeof(InMemoryRuleBasedSegmentCache));

public InMemoryRuleBasedSegmentCache(ConcurrentDictionary<string, RuleBasedSegment> cache,
IInternalEventsTask internalEventsTask,
long changeNumber = -1)
{
_cache = cache;
_changeNumber = changeNumber;
_internalEventsTask = internalEventsTask;
}

#region Sync Methods
Expand Down Expand Up @@ -56,6 +63,15 @@ public void Update(List<RuleBasedSegment> toAdd, List<string> toRemove, long til
}

SetChangeNumber(till);
if (toAdd.Count > 0 || toRemove.Count > 0)
{
Task task = new Task(() =>
{
_internalEventsTask.AddToQueue(SdkInternalEvent.RuleBasedSegmentsUpdated,
new EventMetadata(SdkEventType.SegmentsUpdate, new List<string>())).ContinueWith(OnAddToQueueFailed, TaskContinuationOptions.OnlyOnFaulted);
});
task.Start();
}
}

public void SetChangeNumber(long changeNumber)
Expand All @@ -75,5 +91,10 @@ public Task<RuleBasedSegment> GetAsync(string name)
return Task.FromResult(Get(name));
}
#endregion

public void OnAddToQueueFailed(Task task)
{
_log.Error($"Failed to add internal event to queue: {task.Exception.Message}");
}
}
}
Loading