Skip to content

Commit

Permalink
FFM-10837 Remove redundancy in the seen target process (#105)
Browse files Browse the repository at this point in the history
  • Loading branch information
erdirowlands committed Mar 13, 2024
1 parent 6c38292 commit 12d31bb
Show file tree
Hide file tree
Showing 5 changed files with 50 additions and 29 deletions.
9 changes: 9 additions & 0 deletions client/api/analytics/AnalyticsManager.cs
Expand Up @@ -96,6 +96,13 @@ private void PushToEvaluationAnalyticsCache(FeatureConfig featureConfig, Variati

private void PushToTargetAnalyticsCache(Target target)
{

if (target.IsPrivate)
{
// Target is marked as private, so don't send it in analytics
return;
}

if (analyticsPublisherService.IsTargetSeen(target))
{
// Target has already been processed in a previous interval, so ignore it.
Expand All @@ -118,6 +125,8 @@ private void PushToTargetAnalyticsCache(Target target)
// change did not go as far as to maintain two caches (due to effort involved), but differentiate them based on subclassing, so
// the counter used for target metrics isn't needed, but causes no issue.
targetAnalyticsCache.Put(targetAnalytics, 1);

analyticsPublisherService.MarkTargetAsSeen(target);
}

private void LogMetricsIgnoredWarning(string cacheType, int cacheSize, int bufferSize)
Expand Down
24 changes: 8 additions & 16 deletions client/api/analytics/AnalyticsPublisherService.cs
Expand Up @@ -17,9 +17,7 @@ internal class AnalyticsPublisherService
private static readonly string VariationIdentifierAttribute = "variationIdentifier";
private static readonly string TargetAttribute = "target";
internal static readonly ConcurrentDictionary<Target, byte> SeenTargets = new();
private static readonly ConcurrentDictionary<Target, byte> StagingSeenTargets = new();
private static readonly string SdkType = "SDK_TYPE";
private static readonly string AnonymousTarget = "anonymous";
private static readonly string Server = "server";
private static readonly string SdkLanguage = "SDK_LANGUAGE";
private static readonly string SdkVersion = "SDK_VERSION";
Expand Down Expand Up @@ -55,9 +53,7 @@ public void SendDataAndResetCache()
logger.LogDebug("Sending analytics data :{@a}", metrics);
connector.PostMetrics(metrics);
}

foreach (var uniqueTarget in StagingSeenTargets.Keys) SeenTargets.TryAdd(uniqueTarget, 0);
StagingSeenTargets.Clear();

logger.LogDebug("Successfully sent analytics data to the server");
evaluationAnalyticsCache.resetCache();
targetAnalyticsCache.resetCache();
Expand Down Expand Up @@ -94,15 +90,14 @@ public void SendDataAndResetCache()
SetMetricsAttributes(metricsData, VariationValueAttribute, evaluation.Variation.Value);
SetMetricsAttributes(metricsData, TargetAttribute, evaluation.Target.Identifier);
SetCommonSdkAttributes(metricsData);
StagingSeenTargets.TryAdd(evaluation.Target, 0);
metrics.MetricsData.Add(metricsData);
}

// Handle TargetAnalytics
foreach (var targetAnalytic in targetsCache)
{
var target = targetAnalytic.Key.Target;
if (target != null && !SeenTargets.ContainsKey(target) && !target.IsPrivate)
if (target != null)
{
var targetData = new TargetData
{
Expand All @@ -111,15 +106,6 @@ public void SendDataAndResetCache()
Attributes = new List<KeyValue>()
};

// Add target attributes, respecting private attributes
foreach (var attribute in target.Attributes)
if (target.PrivateAttributes == null || !target.PrivateAttributes.Contains(attribute.Key))
targetData.Attributes.Add(new KeyValue
{ Key = attribute.Key, Value = attribute.Value });

// Add to StagingSeenTargets for future reference
StagingSeenTargets.TryAdd(target, 0);

metrics.TargetData.Add(targetData);
}
}
Expand All @@ -132,6 +118,12 @@ public bool IsTargetSeen(Target target)
{
return SeenTargets.ContainsKey(target);
}

public void MarkTargetAsSeen(Target target)
{
SeenTargets.TryAdd(target, 0);
}


private void SetCommonSdkAttributes(MetricsData metricsData)
{
Expand Down
26 changes: 16 additions & 10 deletions client/dto/Target.cs
Expand Up @@ -20,9 +20,8 @@ public Target()
{
attributes = new Dictionary<string, string>();
}

[Obsolete("isPrivate and privateAttributes will be removed in a future release use other constructor instead")]
public Target(string identifier, string name, Dictionary<string, string> attributes, bool isPrivate, HashSet<string> privateAttributes)

public Target(string identifier, string name, Dictionary<string, string> attributes)
{
if (attributes == null)
{
Expand All @@ -35,11 +34,20 @@ public Target(string identifier, string name, Dictionary<string, string> attribu

Identifier = identifier;
Name = name;
IsPrivate = isPrivate;
PrivateAttributes = privateAttributes;
IsPrivate = false;
}

public Target(string identifier, string name, Dictionary<string, string> attributes, bool isPrivate)
{
this.attributes = attributes ?? new Dictionary<string, string>();
this.identifier = identifier;
this.name = name;
this.isPrivate = isPrivate;
}

public Target(string identifier, string name, Dictionary<string, string> attributes)

[Obsolete("privateAttributes will be removed in a future release. Use Target(string identifier, string name, Dictionary<string, string> attributes, bool isPrivate) to mark the entire target as private.")]
public Target(string identifier, string name, Dictionary<string, string> attributes, bool isPrivate, HashSet<string> privateAttributes)
{
if (attributes == null)
{
Expand All @@ -52,14 +60,13 @@ public Target(string identifier, string name, Dictionary<string, string> attribu

Identifier = identifier;
Name = name;
IsPrivate = false;
IsPrivate = isPrivate;
PrivateAttributes = privateAttributes;
}

public string Name { get => name; set => name = value; }
public string Identifier { get => identifier; set => identifier = value; }
public Dictionary<string, string> Attributes { get => attributes; set => attributes = value; }

[Obsolete("Private attributes will be removed in a future release")]
public bool IsPrivate { get => isPrivate; set => isPrivate = value; }

[Obsolete("Private attributes will be removed in a future release")]
Expand Down Expand Up @@ -149,7 +156,6 @@ public TargetBuilder Attributes(Dictionary<string, string> attributes)
return this;
}

[Obsolete("Private attributes will be removed in a future release")]
public TargetBuilder IsPrivate(bool isPrivate)
{
this.isPrivate = isPrivate;
Expand Down
14 changes: 14 additions & 0 deletions docs/further_reading.md
Expand Up @@ -24,6 +24,20 @@ CfClient.Instance.Initialize(apiKey, Config.Builder()
| enableStream | SetStreamEnabled(true) | Enable streaming mode. | true |
| enableAnalytics | SetAnalyticsEnabled(true) | Enable analytics. Metrics data is posted every 60s | true |

# Anonymous Target

If you do not want a `Target` to be sent to Harness servers, you can use the `isPrivate` field.

```csharp
Target target1 = Target.builder()
.Identifier("myIdentifier")
.Name("myName")
.IsPrivate(true)
.Attributes(new Dictionary<string, string>(){{"email", "demo@harness.io"}})
.build();
```


## Logging Configuration
You can configure the logger using Serilog.
Add Serilog to your project with the following commands
Expand Down
6 changes: 3 additions & 3 deletions ff-netF48-server-sdk.csproj
Expand Up @@ -8,10 +8,10 @@
<PackageId>ff-dotnet-server-sdk</PackageId>
<RootNamespace>io.harness.cfsdk</RootNamespace>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<Version>1.5.0</Version>
<Version>1.6.0</Version>
<PackOnBuild>true</PackOnBuild>
<PackageVersion>1.5.0</PackageVersion>
<AssemblyVersion>1.5.0</AssemblyVersion>
<PackageVersion>1.6.0</PackageVersion>
<AssemblyVersion>1.6.0</AssemblyVersion>
<Authors>support@harness.io</Authors>
<Copyright>Copyright © 2023</Copyright>
<PackageIconUrl>https://harness.io/icon-ff.svg</PackageIconUrl>
Expand Down

0 comments on commit 12d31bb

Please sign in to comment.