Skip to content

Commit 064f65c

Browse files
committed
fix: modified initializer behavior to spec
This commit will make it so that the client will only report initalized when a valid selector is present in the basis
1 parent e5b121f commit 064f65c

File tree

3 files changed

+117
-2
lines changed

3 files changed

+117
-2
lines changed

ldclient/datasystem.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ def builder(_: LDConfig) -> Initializer:
134134

135135
return builder
136136

137+
137138
def default() -> ConfigBuilder:
138139
"""
139140
Default is LaunchDarkly's recommended flag data acquisition strategy.

ldclient/impl/datasystem/fdv2.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -409,9 +409,11 @@ def _run_initializers(self, set_on_ready: Event):
409409
# Apply the basis to the store
410410
self._store.apply(basis.change_set, basis.persist)
411411

412-
# Set ready event
413-
if not set_on_ready.is_set():
412+
# Set ready event if an only if a selector is defined for the changeset
413+
selector_is_defined = basis.change_set.selector is not None and basis.change_set.selector.is_defined()
414+
if selector_is_defined and not set_on_ready.is_set():
414415
set_on_ready.set()
416+
return
415417
except Exception as e:
416418
log.error("Initializer failed with exception: %s", e)
417419

ldclient/testing/impl/datasystem/test_fdv2_datasystem.py

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
# pylint: disable=missing-docstring
22

3+
import os
4+
import tempfile
35
from threading import Event
46
from typing import List
57

68
from mock import Mock
79

810
from ldclient.config import Config, DataSystemConfig
11+
from ldclient.datasystem import file_ds_builder
912
from ldclient.impl.datasystem import DataAvailability
1013
from ldclient.impl.datasystem.fdv2 import FDv2
1114
from ldclient.integrations.test_datav2 import TestDataV2
@@ -432,3 +435,112 @@ def test_fdv2_stays_on_fdv1_after_fallback():
432435
store = fdv2.store
433436
flag = store.get(FEATURES, "fdv1-flag", lambda x: x)
434437
assert flag is not None
438+
439+
440+
def test_fdv2_with_file_to_polling_initializers():
441+
"""
442+
Test that FDv2 can be initialized with a file data source and a polling data source.
443+
In this case the results from the file data source should be overwritten by the
444+
results from the polling datasource.
445+
"""
446+
initial_flag_data = '''
447+
{
448+
"flags": {
449+
"feature-flag": {
450+
"key": "feature-flag",
451+
"version": 0,
452+
"on": false,
453+
"fallthrough": {
454+
"variation": 0
455+
},
456+
"variations": ["off", "on"]
457+
}
458+
}
459+
}
460+
'''
461+
f, path = tempfile.mkstemp(suffix='.json')
462+
try:
463+
os.write(f, initial_flag_data.encode("utf-8"))
464+
os.close(f)
465+
466+
td_initializer = TestDataV2.data_source()
467+
td_initializer.update(td_initializer.flag("feature-flag").on(True))
468+
469+
# We actually do not care what this synchronizer does.
470+
td_synchronizer = TestDataV2.data_source()
471+
472+
data_system_config = DataSystemConfig(
473+
initializers=[file_ds_builder([path]), td_initializer.build_initializer],
474+
primary_synchronizer=td_synchronizer.build_synchronizer,
475+
)
476+
477+
set_on_ready = Event()
478+
fdv2 = FDv2(Config(sdk_key="dummy"), data_system_config)
479+
count = 0
480+
481+
def listener(_: FlagChange):
482+
nonlocal count
483+
count += 1
484+
485+
fdv2.flag_tracker.add_listener(listener)
486+
487+
fdv2.start(set_on_ready)
488+
assert set_on_ready.wait(1), "Data system did not become ready in time"
489+
assert count == 2, "Invalid initializer process"
490+
fdv2.stop()
491+
finally:
492+
os.remove(path)
493+
494+
495+
def test_fdv2_with_polling_to_file_initializers():
496+
"""
497+
Test that when FDv2 is initialized with a polling datasource and a file datasource
498+
then only the polling processor needs to run.
499+
"""
500+
initial_flag_data = '''
501+
{
502+
"flags": {
503+
"feature-flag": {
504+
"key": "feature-flag",
505+
"version": 0,
506+
"on": false,
507+
"fallthrough": {
508+
"variation": 0
509+
},
510+
"variations": ["off", "on"]
511+
}
512+
}
513+
}
514+
'''
515+
f, path = tempfile.mkstemp(suffix='.json')
516+
try:
517+
os.write(f, initial_flag_data.encode("utf-8"))
518+
os.close(f)
519+
520+
td_initializer = TestDataV2.data_source()
521+
td_initializer.update(td_initializer.flag("feature-flag").on(True))
522+
523+
# We actually do not care what this synchronizer does.
524+
td_synchronizer = TestDataV2.data_source()
525+
526+
data_system_config = DataSystemConfig(
527+
initializers=[td_initializer.build_initializer, file_ds_builder([path])],
528+
primary_synchronizer=td_synchronizer.build_synchronizer,
529+
)
530+
531+
set_on_ready = Event()
532+
fdv2 = FDv2(Config(sdk_key="dummy"), data_system_config)
533+
count = 0
534+
535+
def listener(_: FlagChange):
536+
nonlocal count
537+
count += 1
538+
539+
fdv2.flag_tracker.add_listener(listener)
540+
541+
fdv2.start(set_on_ready)
542+
assert set_on_ready.wait(1), "Data system did not become ready in time"
543+
assert count == 1, "Invalid initializer process"
544+
fdv2.stop()
545+
finally:
546+
os.remove(path)

0 commit comments

Comments
 (0)