Skip to content
This repository was archived by the owner on May 22, 2025. It is now read-only.

Commit f36408f

Browse files
prepare 6.2.0 release (#143)
1 parent 0f5b529 commit f36408f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+3020
-939
lines changed

.ldrelease/config.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ template:
1616
# See Releaser docs - this causes the generated documentation to include all public APIs from CommonSdk
1717
LD_RELEASE_DOCS_ASSEMBLIES: LaunchDarkly.ServerSdk LaunchDarkly.CommonSdk
1818
LD_RELEASE_DOCS_TARGET_FRAMEWORK: net452
19-
LD_RELEASE_TEST_TARGET_FRAMEWORK: net46
19+
LD_RELEASE_TEST_TARGET_FRAMEWORK: net452
2020

2121
releasableBranches:
2222
- name: master

src/LaunchDarkly.ServerSdk/Components.cs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,39 @@ namespace LaunchDarkly.Sdk.Server
2222
/// </remarks>
2323
public static class Components
2424
{
25+
/// <summary>
26+
/// Returns a configuration builder for the SDK's big segments feature.
27+
/// </summary>
28+
/// <remarks>
29+
/// <para>
30+
/// "Big segments" are a specific type of user segments. For more information, read the LaunchDarkly
31+
/// documentation about user segments: https://docs.launchdarkly.com/home/users
32+
/// </para>
33+
/// <para>
34+
/// After configuring this object, use <see cref="ConfigurationBuilder.BigSegments(IBigSegmentsConfigurationFactory)"/>
35+
/// to store it in your SDK configuration. For example, using the Redis integration:
36+
/// </para>
37+
/// <code>
38+
/// var config = Configuration.Builder(sdkKey)
39+
/// .BigSegments(Components.BigSegments(Redis.DataStore().Prefix("app1"))
40+
/// .UserCacheSize(2000))
41+
/// .Build();
42+
/// </code>
43+
/// <para>
44+
/// You must always specify the <paramref name="storeFactory"/> parameter, to tell the SDK what database
45+
/// you are using. Several database integrations exist for the LaunchDarkly SDK, each with its own
46+
/// behavior and options specific to that database; this is described via some implementation of
47+
/// <see cref="IBigSegmentStoreFactory"/>. The <see cref="BigSegmentsConfigurationBuilder"/> adds
48+
/// configuration options for aspects of SDK behavior that are independent of the database. In the
49+
/// example above, <code>Prefix</code> is an option specifically for the Redis integration, whereas
50+
/// <code>UserCacheSize</code> is an option that can be used for any data store type.
51+
/// </para>
52+
/// </remarks>
53+
/// <param name="storeFactory">the factory for the underlying data store</param>
54+
/// <returns>a <see cref="BigSegmentsConfigurationBuilder"/></returns>
55+
public static BigSegmentsConfigurationBuilder BigSegments(IBigSegmentStoreFactory storeFactory) =>
56+
new BigSegmentsConfigurationBuilder(storeFactory);
57+
2558
/// <summary>
2659
/// Returns a configuration object that disables direct connection with LaunchDarkly for feature
2760
/// flag updates.

src/LaunchDarkly.ServerSdk/Configuration.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ public class Configuration
1616
{
1717
#region Public properties
1818

19+
/// <summary>
20+
/// A factory object that creates an implementation of <see cref="IBigSegmentsConfigurationFactory"/>.
21+
/// </summary>
22+
public IBigSegmentsConfigurationFactory BigSegmentsConfigurationFactory { get; }
23+
1924
/// <summary>
2025
/// A factory object that creates an implementation of <see cref="IDataSource"/>, which will
2126
/// receive feature flag data.
@@ -98,7 +103,7 @@ public static Configuration Default(string sdkKey)
98103
}
99104

100105
/// <summary>
101-
/// Creates an <see cref="ConfigurationBuilder"/> for constructing a configuration object using a fluent syntax.
106+
/// Creates a <see cref="ConfigurationBuilder"/> for constructing a configuration object using a fluent syntax.
102107
/// </summary>
103108
/// <remarks>
104109
/// This is the only method for building a <see cref="Configuration"/> if you are setting properties
@@ -146,6 +151,7 @@ public static ConfigurationBuilder Builder(Configuration fromConfiguration)
146151

147152
internal Configuration(ConfigurationBuilder builder)
148153
{
154+
BigSegmentsConfigurationFactory = builder._bigSegmentsConfigurationFactory;
149155
DataSourceFactory = builder._dataSourceFactory;
150156
DataStoreFactory = builder._dataStoreFactory;
151157
DiagnosticOptOut = builder._diagnosticOptOut;

src/LaunchDarkly.ServerSdk/ConfigurationBuilder.cs

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,11 @@ public sealed class ConfigurationBuilder
2626
{
2727
#region Internal properties
2828

29-
internal static readonly TimeSpan DefaultStartWaitTime = TimeSpan.FromSeconds(5);
29+
internal static readonly TimeSpan DefaultStartWaitTime = TimeSpan.FromSeconds(10);
3030

3131
// Let's try to keep these properties and methods alphabetical so they're easy to find. Note that they
3232
// are internal rather than private so that they can be read by the Configuration constructor.
33+
internal IBigSegmentsConfigurationFactory _bigSegmentsConfigurationFactory = null;
3334
internal IDataSourceFactory _dataSourceFactory = null;
3435
internal IDataStoreFactory _dataStoreFactory = null;
3536
internal bool _diagnosticOptOut = false;
@@ -51,6 +52,7 @@ internal ConfigurationBuilder(string sdkKey)
5152

5253
internal ConfigurationBuilder(Configuration copyFrom)
5354
{
55+
_bigSegmentsConfigurationFactory = copyFrom.BigSegmentsConfigurationFactory;
5456
_dataSourceFactory = copyFrom.DataSourceFactory;
5557
_dataStoreFactory = copyFrom.DataStoreFactory;
5658
_diagnosticOptOut = copyFrom.DiagnosticOptOut;
@@ -62,7 +64,6 @@ internal ConfigurationBuilder(Configuration copyFrom)
6264
_startWaitTime = copyFrom.StartWaitTime;
6365
}
6466

65-
6667
#endregion
6768

6869
#region Public methods
@@ -77,6 +78,43 @@ public Configuration Build()
7778
return new Configuration(this);
7879
}
7980

81+
/// <summary>
82+
/// Sets the configuration of the SDK's big segments feature.
83+
/// </summary>
84+
/// <remarks>
85+
/// <para>
86+
/// "Big segments" are a specific type of user segments. For more information, read the LaunchDarkly
87+
/// documentation about user segments: https://docs.launchdarkly.com/home/users
88+
/// </para>
89+
/// <para>
90+
/// If you are using this feature, you will normally specify a database implementation that matches how
91+
/// the LaunchDarkly Relay Proxy is configured, since the Relay Proxy manages the big segment data.
92+
/// </para>
93+
/// <para>
94+
/// By default, there is no implementation and big segments cannot be evaluated. In this case, any flag
95+
/// evaluation that references a big segment will behave as if no users are included in any big
96+
/// segments, and the <see cref="EvaluationReason"/> associated with any such flag evaluation will have
97+
/// a <see cref="EvaluationReason.BigSegmentsStatus"/> of <see cref="BigSegmentsStatus.NotConfigured"/>.
98+
/// </para>
99+
/// </remarks>
100+
/// <example>
101+
/// <code>
102+
/// // This example uses the Redis integration
103+
/// var config = Configuration.Builder(sdkKey)
104+
/// .BigSegments(Components.BigSegments(Redis.DataStore().Prefix("app1"))
105+
/// .UserCacheSize(2000))
106+
/// .Build();
107+
/// </code>
108+
/// </example>
109+
/// <param name="bigSegmentsConfigurationFactory">a configuration factory object returned by
110+
/// <see cref="Components.BigSegments(IBigSegmentStoreFactory)"/></param>
111+
/// <returns>the same builder</returns>
112+
public ConfigurationBuilder BigSegments(IBigSegmentsConfigurationFactory bigSegmentsConfigurationFactory)
113+
{
114+
_bigSegmentsConfigurationFactory = bigSegmentsConfigurationFactory;
115+
return this;
116+
}
117+
80118
/// <summary>
81119
/// Sets the implementation of the component that receives feature flag data from LaunchDarkly,
82120
/// using a factory object.
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
using System;
2+
using LaunchDarkly.Sdk.Server.Interfaces;
3+
4+
namespace LaunchDarkly.Sdk.Server.Integrations
5+
{
6+
/// <summary>
7+
/// Contains methods for configuring the SDK's big segments behavior.
8+
/// </summary>
9+
/// <remarks>
10+
/// <para>
11+
/// "Big segments" are a specific type of user segments. For more information, read the LaunchDarkly
12+
/// documentation about user segments: https://docs.launchdarkly.com/home/users
13+
/// </para>
14+
/// <para>
15+
/// If you want to set non-default values for any of these properties, create a builder with
16+
/// <see cref="Components.BigSegments(IBigSegmentStoreFactory)"/>, change its properties with the
17+
/// methods of this class, and pass it to <see cref="ConfigurationBuilder.BigSegments(IBigSegmentsConfigurationFactory)"/>:
18+
/// </para>
19+
/// </remarks>
20+
/// <example>
21+
/// <code>
22+
/// // This example uses the Redis integration
23+
/// var config = Configuration.Builder(sdkKey)
24+
/// .BigSegments(Components.BigSegments(Redis.DataStore().Prefix("app1"))
25+
/// .UserCacheSize(2000))
26+
/// .Build();
27+
/// </code>
28+
/// </example>
29+
public sealed class BigSegmentsConfigurationBuilder : IBigSegmentsConfigurationFactory
30+
{
31+
/// <summary>
32+
/// Default value for <see cref="UserCacheSize(int)"/>.
33+
/// </summary>
34+
public const int DefaultUserCacheSize = 1000;
35+
36+
/// <summary>
37+
/// Default value for <see cref="UserCacheTime(TimeSpan)"/>: five seconds.
38+
/// </summary>
39+
public static readonly TimeSpan DefaultUserCacheTime = TimeSpan.FromSeconds(5);
40+
41+
/// <summary>
42+
/// Default value for <see cref="StatusPollInterval(TimeSpan)"/>: five seconds.
43+
/// </summary>
44+
public static readonly TimeSpan DefaultStatusPollInterval = TimeSpan.FromSeconds(5);
45+
46+
/// <summary>
47+
/// Default value for <see cref="StaleAfter(TimeSpan)"/>: two minutes.
48+
/// </summary>
49+
public static readonly TimeSpan DefaultStaleAfter = TimeSpan.FromMinutes(2);
50+
51+
private readonly IBigSegmentStoreFactory _storeFactory;
52+
private int _userCacheSize = DefaultUserCacheSize;
53+
private TimeSpan _userCacheTime = DefaultUserCacheTime;
54+
private TimeSpan _statusPollInterval = DefaultStatusPollInterval;
55+
private TimeSpan _staleAfter = DefaultStaleAfter;
56+
57+
internal BigSegmentsConfigurationBuilder(IBigSegmentStoreFactory storeFactory)
58+
{
59+
_storeFactory = storeFactory;
60+
}
61+
62+
/// <summary>
63+
/// Sets the maximum number of users whose big segment state will be cached by the SDK
64+
/// at any given time.
65+
/// </summary>
66+
/// <remarks>
67+
/// <para>
68+
/// To reduce database traffic, the SDK maintains a least-recently-used cache by user key. When a feature
69+
/// flag that references a big segment is evaluated for some user who is not currently in the cache, the
70+
/// SDK queries the database for all big segment memberships of that user, and stores them together in a
71+
/// single cache entry. If the cache is full, the oldest entry is dropped.
72+
/// </para>
73+
/// <para>
74+
/// A higher value for <see cref="UserCacheSize(int)"/> means that database queries for big segments will
75+
/// be done less often for recently-referenced users, if the application has many users, at the cost of
76+
/// increased memory used by the cache.
77+
/// </para>
78+
/// <para>
79+
/// Cache entries can also expire based on the setting of <see cref="UserCacheTime(TimeSpan)"/>.
80+
/// </para>
81+
/// </remarks>
82+
/// <param name="userCacheSize">the maximum number of user states to cache</param>
83+
/// <returns>the builder</returns>
84+
/// <seealso cref="DefaultUserCacheSize"/>
85+
public BigSegmentsConfigurationBuilder UserCacheSize(int userCacheSize)
86+
{
87+
_userCacheSize = userCacheSize;
88+
return this;
89+
}
90+
91+
/// <summary>
92+
/// Sets the maximum length of time that the big segment state for a user will be cached
93+
/// by the SDK.
94+
/// </summary>
95+
/// <remarks>
96+
/// <para>
97+
/// See <see cref="UserCacheSize(int)"/> for more about this cache. A higher value for
98+
/// <see cref="UserCacheTime(TimeSpan)"/> means that database queries for the big segment state of any
99+
/// given user will be done less often, but that changes to segment membership may not be detected as soon.
100+
/// </para>
101+
/// </remarks>
102+
/// <param name="userCacheTime">the cache TTL</param>
103+
/// <returns>the builder</returns>
104+
/// <seealso cref="DefaultUserCacheTime"/>
105+
public BigSegmentsConfigurationBuilder UserCacheTime(TimeSpan userCacheTime)
106+
{
107+
_userCacheTime = userCacheTime;
108+
return this;
109+
}
110+
111+
/// <summary>
112+
/// Sets the interval at which the SDK will poll the big segment store to make sure
113+
/// it is available and to determine how long ago it was updated.
114+
/// </summary>
115+
/// <param name="statusPollInterval">the status polling interval (any value less than or
116+
/// equal to zero will be changed to <see cref="DefaultStatusPollInterval"/>)</param>
117+
/// <returns>the builder</returns>
118+
/// <seealso cref="DefaultStatusPollInterval"/>
119+
public BigSegmentsConfigurationBuilder StatusPollInterval(TimeSpan statusPollInterval)
120+
{
121+
_statusPollInterval = statusPollInterval > TimeSpan.Zero ? statusPollInterval :
122+
DefaultStatusPollInterval;
123+
return this;
124+
}
125+
126+
/// <summary>
127+
/// Sets the maximum length of time between updates of the big segments data before the data
128+
/// is considered out of date.
129+
/// </summary>
130+
/// <remarks>
131+
/// <para>
132+
/// Normally, the LaunchDarkly Relay Proxy updates a timestamp in the big segments store at intervals to
133+
/// confirm that it is still in sync with the LaunchDarkly data, even if there have been no changes to the
134+
/// data. If the timestamp falls behind the current time by the amount specified in
135+
/// <see cref="StaleAfter(TimeSpan)"/>, the SDK assumes that something is not working correctly in this
136+
/// process and that the data may not be accurate.
137+
/// </para>
138+
/// <para>
139+
/// While in a stale state, the SDK will still continue using the last known data, but
140+
/// <see cref="IBigSegmentStoreStatusProvider.Status"/> will return true in its Stale property, and any
141+
/// <see cref="EvaluationReason"/> generated from a feature flag that references a big segment will have
142+
/// a <see cref="EvaluationReason.BigSegmentsStatus"/> of <see cref="BigSegmentsStatus.Stale"/>.
143+
/// </para>
144+
/// </remarks>
145+
/// <param name="staleAfter">the time limit for marking the data as stale (any value less
146+
/// than or equal to zero will be changed to <see cref="DefaultStaleAfter"/>)</param>
147+
/// <returns>the builder</returns>
148+
public BigSegmentsConfigurationBuilder StaleAfter(TimeSpan staleAfter)
149+
{
150+
_staleAfter = staleAfter > TimeSpan.Zero ? staleAfter : DefaultStaleAfter;
151+
return this;
152+
}
153+
154+
/// <inheritdoc/>
155+
public BigSegmentsConfiguration CreateBigSegmentsConfiguration(LdClientContext context)
156+
{
157+
var store = _storeFactory is null ? null : _storeFactory.CreateBigSegmentStore(context);
158+
return new BigSegmentsConfiguration(
159+
store,
160+
_userCacheSize,
161+
_userCacheTime,
162+
_statusPollInterval,
163+
_staleAfter
164+
);
165+
}
166+
}
167+
}

src/LaunchDarkly.ServerSdk/Integrations/TestData.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,8 @@ internal TestData UsePreconfiguredSegment(Segment segment)
215215
{
216216
segment = new Segment(segment.Key,
217217
newVersion,
218-
segment.Deleted, segment.Included, segment.Excluded, segment.Rules, segment.Salt);
218+
segment.Deleted, segment.Included, segment.Excluded, segment.Rules, segment.Salt,
219+
segment.Unbounded, segment.Generation);
219220
}
220221
newItem = new ItemDescriptor(newVersion, segment);
221222
_currentSegments[segment.Key] = newItem;
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
using System;
2+
3+
namespace LaunchDarkly.Sdk.Server.Interfaces
4+
{
5+
/// <summary>
6+
/// Information about the status of a big segment store, provided by
7+
/// <see cref="IBigSegmentStoreStatusProvider"/>.
8+
/// </summary>
9+
/// <remarks>
10+
/// "Big segments" are a specific type of user segments. For more information, read the LaunchDarkly
11+
/// documentation about user segments: https://docs.launchdarkly.com/home/users
12+
/// </remarks>
13+
public struct BigSegmentStoreStatus
14+
{
15+
/// <summary>
16+
/// True if the big segment store is able to respond to queries, so that the SDK can
17+
/// evaluate whether a user is in a segment or not.
18+
/// </summary>
19+
/// <remarks>
20+
/// If this property is false, the store is not able to make queries (for instance, it may not have
21+
/// a valid database connection). In this case, the SDK will treat any reference to a big segment
22+
/// as if no users are included in that segment. Also, the <see cref="EvaluationReason"/>
23+
/// associated with any flag evaluation that references a big segment when the store is not
24+
/// available will have a <see cref="EvaluationReason.BigSegmentsStatus"/> of
25+
/// <see cref="BigSegmentsStatus.StoreError"/>.
26+
/// </remarks>
27+
public bool Available { get; set; }
28+
29+
/// <summary>
30+
/// True if the big segment store is available, but has not been updated within the amount of time
31+
/// specified by <see cref="LaunchDarkly.Sdk.Server.Integrations.BigSegmentsConfigurationBuilder.StaleAfter(TimeSpan)"/>.
32+
/// </summary>
33+
/// <remarks>
34+
/// This may indicate that the LaunchDarkly Relay Proxy, which populates the store, has stopped
35+
/// running or has become unable to receive fresh data from LaunchDarkly. Any feature flag
36+
/// evaluations that reference a big segment will be using the last known data, which may be out
37+
/// of date.
38+
/// </remarks>
39+
public bool Stale { get; set; }
40+
41+
/// <inheritdoc/>
42+
public override string ToString() =>
43+
string.Format("(Available={0},Stale={1})", Available, Stale);
44+
}
45+
}

0 commit comments

Comments
 (0)