Skip to content

Commit 8da2666

Browse files
committed
fix: Update DataSystemConfig to accept list of synchronizers
1 parent 3c802c1 commit 8da2666

File tree

8 files changed

+206
-169
lines changed

8 files changed

+206
-169
lines changed

.github/workflows/ci.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,16 +61,16 @@ jobs:
6161
with:
6262
test_service_port: 9000
6363
token: ${{ secrets.GITHUB_TOKEN }}
64-
stop_service: 'false'
65-
enable_persistence_tests: 'true'
64+
stop_service: "false"
65+
enable_persistence_tests: "true"
6666

6767
- name: Run contract tests v3
6868
uses: launchdarkly/gh-actions/actions/contract-tests@contract-tests-v1
6969
with:
7070
test_service_port: 9000
7171
token: ${{ secrets.GITHUB_TOKEN }}
72-
version: v3.0.0-alpha.2
73-
enable_persistence_tests: 'true'
72+
version: v3.0.0-alpha.3
73+
enable_persistence_tests: "true"
7474

7575
windows:
7676
runs-on: windows-latest

contract-tests/client_entity.py

Lines changed: 18 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -58,50 +58,32 @@ def __init__(self, tag, config):
5858
initializers.append(polling_builder)
5959

6060
datasystem.initializers(initializers)
61-
sync_config = datasystem_config.get('synchronizers')
62-
if sync_config is not None:
63-
primary = sync_config.get('primary')
64-
secondary = sync_config.get('secondary')
65-
66-
primary_builder = None
67-
secondary_builder = None
61+
sync_configs = datasystem_config.get('synchronizers')
62+
if sync_configs is not None:
63+
sync_builders = []
6864
fallback_builder = None
6965

70-
if primary is not None:
71-
streaming = primary.get('streaming')
66+
for sync_config in sync_configs:
67+
streaming = sync_config.get('streaming')
7268
if streaming is not None:
73-
primary_builder = streaming_ds_builder()
74-
_set_optional_value(streaming, "baseUri", primary_builder.base_uri)
75-
_set_optional_time(streaming, "initialRetryDelayMs", primary_builder.initial_reconnect_delay)
76-
elif primary.get('polling') is not None:
77-
polling = primary.get('polling')
78-
79-
primary_builder = polling_ds_builder()
80-
_set_optional_value(polling, "baseUri", primary_builder.base_uri)
81-
_set_optional_time(polling, "pollIntervalMs", primary_builder.poll_interval)
69+
builder = streaming_ds_builder()
70+
_set_optional_value(streaming, "baseUri", builder.base_uri)
71+
_set_optional_time(streaming, "initialRetryDelayMs", builder.initial_reconnect_delay)
72+
sync_builders.append(builder)
73+
elif sync_config.get('polling') is not None:
74+
polling = sync_config.get('polling')
75+
76+
builder = polling_ds_builder()
77+
_set_optional_value(polling, "baseUri", builder.base_uri)
78+
_set_optional_time(polling, "pollIntervalMs", builder.poll_interval)
79+
sync_builders.append(builder)
8280

8381
fallback_builder = fdv1_fallback_ds_builder()
8482
_set_optional_value(polling, "baseUri", fallback_builder.base_uri)
8583
_set_optional_time(polling, "pollIntervalMs", fallback_builder.poll_interval)
8684

87-
if secondary is not None:
88-
streaming = secondary.get('streaming')
89-
if streaming is not None:
90-
secondary_builder = streaming_ds_builder()
91-
_set_optional_value(streaming, "baseUri", secondary_builder.base_uri)
92-
_set_optional_time(streaming, "initialRetryDelayMs", secondary_builder.initial_reconnect_delay)
93-
elif secondary.get('polling') is not None:
94-
polling = secondary.get('polling')
95-
96-
secondary_builder = polling_ds_builder()
97-
_set_optional_value(polling, "baseUri", secondary_builder.base_uri)
98-
_set_optional_time(polling, "pollIntervalMs", secondary_builder.poll_interval)
99-
fallback_builder = fdv1_fallback_ds_builder()
100-
_set_optional_value(polling, "baseUri", fallback_builder.base_uri)
101-
_set_optional_time(polling, "pollIntervalMs", fallback_builder.poll_interval)
102-
103-
if primary_builder is not None:
104-
datasystem.synchronizers(primary_builder, secondary_builder)
85+
if sync_builders:
86+
datasystem.synchronizers(*sync_builders)
10587
if fallback_builder is not None:
10688
datasystem.fdv1_compatible_synchronizer(fallback_builder)
10789

ldclient/config.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -181,11 +181,12 @@ class DataSystemConfig:
181181
initializers: Optional[List[DataSourceBuilder[Initializer]]]
182182
"""The initializers for the data system."""
183183

184-
primary_synchronizer: Optional[DataSourceBuilder[Synchronizer]]
185-
"""The primary synchronizer for the data system."""
186-
187-
secondary_synchronizer: Optional[DataSourceBuilder[Synchronizer]] = None
188-
"""The secondary synchronizers for the data system."""
184+
synchronizers: Optional[List[DataSourceBuilder[Synchronizer]]]
185+
"""
186+
The synchronizers for the data system, ordered by preference.
187+
The first synchronizer is the most preferred, with subsequent synchronizers
188+
serving as fallbacks in order of decreasing preference.
189+
"""
189190

190191
data_store_mode: DataStoreMode = DataStoreMode.READ_WRITE
191192
"""The data store mode specifies the mode in which the persistent store will operate, if present."""

ldclient/datasystem.py

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,7 @@ class ConfigBuilder: # pylint: disable=too-few-public-methods
2929

3030
def __init__(self) -> None:
3131
self._initializers: Optional[List[DataSourceBuilder[Initializer]]] = None
32-
self._primary_synchronizer: Optional[DataSourceBuilder[Synchronizer]] = None
33-
self._secondary_synchronizer: Optional[DataSourceBuilder[Synchronizer]] = None
32+
self._synchronizers: List[DataSourceBuilder[Synchronizer]] = []
3433
self._fdv1_fallback_synchronizer: Optional[DataSourceBuilder[Synchronizer]] = None
3534
self._store_mode: DataStoreMode = DataStoreMode.READ_ONLY
3635
self._data_store: Optional[FeatureStore] = None
@@ -44,14 +43,23 @@ def initializers(self, initializers: Optional[List[DataSourceBuilder[Initializer
4443

4544
def synchronizers(
4645
self,
47-
primary: DataSourceBuilder[Synchronizer],
48-
secondary: Optional[DataSourceBuilder[Synchronizer]] = None,
46+
*sync_builders: DataSourceBuilder[Synchronizer]
4947
) -> "ConfigBuilder":
5048
"""
5149
Sets the synchronizers for the data system.
50+
51+
Accepts one or more synchronizer builders, ordered by preference.
52+
The first synchronizer is the most preferred, with subsequent
53+
synchronizers serving as fallbacks in order of decreasing preference.
54+
55+
Examples:
56+
builder.synchronizers(streaming_builder)
57+
builder.synchronizers(streaming_builder, polling_builder)
58+
builder.synchronizers(sync1, sync2, sync3)
5259
"""
53-
self._primary_synchronizer = primary
54-
self._secondary_synchronizer = secondary
60+
if len(sync_builders) == 0:
61+
raise ValueError("At least one synchronizer must be provided")
62+
self._synchronizers = list(sync_builders)
5563
return self
5664

5765
def fdv1_compatible_synchronizer(
@@ -77,13 +85,9 @@ def build(self) -> DataSystemConfig:
7785
"""
7886
Builds the data system configuration.
7987
"""
80-
if self._secondary_synchronizer is not None and self._primary_synchronizer is None:
81-
raise ValueError("Primary synchronizer must be set if secondary is set")
82-
8388
return DataSystemConfig(
8489
initializers=self._initializers,
85-
primary_synchronizer=self._primary_synchronizer,
86-
secondary_synchronizer=self._secondary_synchronizer,
90+
synchronizers=self._synchronizers if len(self._synchronizers) > 0 else None,
8791
fdv1_fallback_synchronizer=self._fdv1_fallback_synchronizer,
8892
data_store_mode=self._store_mode,
8993
data_store=self._data_store,

0 commit comments

Comments
 (0)