Skip to content

Commit 1e0e4a3

Browse files
committed
test: Map dataSystem.fdv1Fallback JSON field with explicit JsonPropertyName
The contract-test service uses System.Text.Json with PropertyNamingPolicy = CamelCase. CamelCase converts the C# property name FDv1Fallback to "fDv1Fallback" (only the first character lowercased), but the harness sends the field as "fdv1Fallback" (lowercase 'd' too). The mismatch made sdkParams.DataSystem.FDv1Fallback always null, which meant dataSystemBuilder.FDv1FallbackSynchronizer(...) was never called, which meant dataSystemConfiguration.FDv1FallbackSynchronizer was null, which caused the OUTER composite's FDv1 fallback factory to throw NRE the moment the action applier tried to invoke it. Add an explicit [JsonPropertyName("fdv1Fallback")] attribute so the deserializer accepts the harness's wire format. Also remove the temporary debug logging from CompositeSource and SdkClientEntity that was used to identify the bug.
1 parent 15656d7 commit 1e0e4a3

3 files changed

Lines changed: 7 additions & 34 deletions

File tree

pkgs/sdk/server/contract-tests/Representations.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Text.Json.Serialization;
34
using LaunchDarkly.Sdk;
45

56
// Note, in order for System.Text.Json serialization/deserialization to work correctly, the members of
@@ -155,7 +156,9 @@ public class SdkConfigDataSystemParams
155156
public SdkConfigDataSynchronizerParams[] Synchronizers { get; set; }
156157
// FDv1Fallback configures the SDK's FDv1 Fallback Synchronizer, which is engaged only when
157158
// the LaunchDarkly server returns the FDv1 fallback directive. It is distinct from the FDv2
158-
// Primary/Fallback Synchronizers above.
159+
// Primary/Fallback Synchronizers above. The harness sends this as "fdv1Fallback" (lowercase
160+
// 'd'); we override the default CamelCase mapping (which would produce "fDv1Fallback").
161+
[JsonPropertyName("fdv1Fallback")]
159162
public SdkConfigPollingParams FDv1Fallback { get; set; }
160163
public string PayloadFilter { get; set; }
161164
}

pkgs/sdk/server/contract-tests/SdkClientEntity.cs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -509,10 +509,8 @@ private static Configuration BuildSdkConfig(SdkConfigParams sdkParams, ILogAdapt
509509
// Configure the FDv1 Fallback Synchronizer directly from dataSystem.fdv1Fallback,
510510
// separate from the FDv2 Primary/Fallback synchronizer chain. This is engaged only
511511
// in response to a server-directed FDv1 Fallback Directive.
512-
Console.WriteLine($"DEBUG: tag={tag} FDv1Fallback null? {sdkParams.DataSystem.FDv1Fallback == null}");
513512
if (sdkParams.DataSystem.FDv1Fallback != null)
514513
{
515-
Console.WriteLine($"DEBUG: tag={tag} FDv1Fallback.BaseUri={sdkParams.DataSystem.FDv1Fallback.BaseUri}");
516514
if (sdkParams.DataSystem.FDv1Fallback.BaseUri != null)
517515
{
518516
endpoints.Polling(sdkParams.DataSystem.FDv1Fallback.BaseUri);
@@ -526,7 +524,6 @@ private static Configuration BuildSdkConfig(SdkConfigParams sdkParams, ILogAdapt
526524
}
527525

528526
dataSystemBuilder.FDv1FallbackSynchronizer(fdv1FallbackBuilder);
529-
Console.WriteLine($"DEBUG: tag={tag} dataSystemBuilder.FDv1FallbackSynchronizer set to {fdv1FallbackBuilder}");
530527
}
531528

532529
builder.DataSystem(dataSystemBuilder);

pkgs/sdk/server/src/Internal/DataSources/CompositeDataSource/CompositeSource.cs

Lines changed: 3 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,6 @@ private void EnqueueAction(Action action)
152152
{
153153
if (_actionQueueShutdown)
154154
{
155-
_log.Debug("{0} EnqueueAction skipped: action queue is shutdown.", _compositeDescription);
156155
return;
157156
}
158157
_pendingActions.Enqueue(action);
@@ -165,7 +164,6 @@ private void EnqueueAction(Action action)
165164

166165
if (shouldProcess)
167166
{
168-
_log.Debug("{0} EnqueueAction starting Task.Run for ProcessQueuedActions.", _compositeDescription);
169167
// Process actions on a background thread to prevent blocking the caller
170168
// and allow Start() to return even when actions are continuously enqueued
171169
_ = Task.Run(() => ProcessQueuedActions());
@@ -177,7 +175,6 @@ private void EnqueueAction(Action action)
177175
/// </summary>
178176
private void ProcessQueuedActions()
179177
{
180-
_log.Debug("{0} ProcessQueuedActions started.", _compositeDescription);
181178
while (true)
182179
{
183180
Action action;
@@ -186,8 +183,6 @@ private void ProcessQueuedActions()
186183
// Check if disposed to allow disposal to interrupt action processing
187184
if (_actionQueueShutdown || _pendingActions.Count == 0)
188185
{
189-
_log.Debug("{0} ProcessQueuedActions exiting: shutdown={1}, count={2}.",
190-
_compositeDescription, _actionQueueShutdown, _pendingActions.Count);
191186
_isProcessingActions = false;
192187
return;
193188
}
@@ -204,27 +199,23 @@ private void ProcessQueuedActions()
204199
{
205200
action();
206201
}
207-
catch (Exception ex)
202+
catch
208203
{
209-
_log.Debug("{0} ProcessQueuedActions: action threw {1}.", _compositeDescription, ex);
210204
// Continue processing remaining actions even if one fails
205+
// TODO: need to add logging, will add in next PR
211206
}
212207
}
213208
}
214209

215210
// This method must only be called while holding _lock.
216211
private void TryFindNextUnderLock()
217212
{
218-
_log.Debug("{0} TryFindNextUnderLock entered. _currentDataSource null? {1}",
219-
_compositeDescription, _currentDataSource == null);
220213
if (_currentDataSource != null)
221214
{
222215
return;
223216
}
224217

225218
var entry = _sourcesList.Next();
226-
_log.Debug("{0} TryFindNextUnderLock: entry.Factory null? {1}",
227-
_compositeDescription, entry.Factory == null);
228219

229220
if (entry.Factory == null)
230221
{
@@ -253,26 +244,13 @@ private void TryFindNextUnderLock()
253244
observers = Array.Empty<IDataSourceObserver>();
254245
}
255246

256-
_log.Debug("{0} TryFindNextUnderLock: observers built, invoking factory", _compositeDescription);
257-
258247
// here we wrap the sink in observability so that we can trigger actions, the sanitized sink is
259248
// invoked before the observers to ensure actions don't trigger before data can propagate
260249
var observableUpdates = new ObservableDataSourceUpdates(_sanitizedUpdateSink, observers);
261250
var disableableUpdates = _disableableTracker.WrapAndTrack(observableUpdates);
262251

263252
_currentEntry = entry;
264-
try
265-
{
266-
_currentDataSource = entry.Factory(disableableUpdates);
267-
_log.Debug("{0} TryFindNextUnderLock: factory returned {1}",
268-
_compositeDescription, _currentDataSource?.ToString() ?? "null");
269-
}
270-
catch (Exception ex)
271-
{
272-
_log.Debug("{0} TryFindNextUnderLock: factory threw {1}",
273-
_compositeDescription, ex);
274-
throw;
275-
}
253+
_currentDataSource = entry.Factory(disableableUpdates);
276254
}
277255

278256
#region ICompositeSourceActionable
@@ -281,16 +259,13 @@ public Task<bool> StartCurrent()
281259
{
282260
if (_disposed)
283261
{
284-
_log.Debug("{0} StartCurrent skipped: composite disposed.", _compositeDescription);
285262
return Task.FromResult(false);
286263
}
287264

288-
_log.Debug("{0} StartCurrent enqueuing action.", _compositeDescription);
289265
var tcs = new TaskCompletionSource<bool>();
290266

291267
EnqueueAction(() =>
292268
{
293-
_log.Debug("{0} StartCurrent action running.", _compositeDescription);
294269
try
295270
{
296271
IDataSource dataSourceToStart;
@@ -301,8 +276,6 @@ public Task<bool> StartCurrent()
301276
dataSourceToStart = _currentDataSource;
302277
disposed = _disposed;
303278
}
304-
_log.Debug("{0} StartCurrent action: dataSourceToStart={1}, disposed={2}.",
305-
_compositeDescription, dataSourceToStart?.ToString() ?? "null", disposed);
306279

307280
if (disposed)
308281
{

0 commit comments

Comments
 (0)