Skip to content

Commit 7e1f927

Browse files
committed
fix: Log async persist failures instead of raising them
Also rename AsyncStore.apply_async/commit_async/close_async to apply/commit/close (the async suffix was a carryover; AsyncStore is a sibling of Store and inherits no sync methods to disambiguate from), and fix isort ordering in async_config.py and the wrapper test.
1 parent 5948b78 commit 7e1f927

4 files changed

Lines changed: 34 additions & 32 deletions

File tree

ldclient/async_config.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,10 @@
88
compatibility guarantees.
99
"""
1010

11+
from dataclasses import dataclass
1112
from typing import Callable, List, Optional, Set
1213

1314
from ldclient.async_feature_store import AsyncInMemoryFeatureStore
14-
from dataclasses import dataclass
15-
1615
from ldclient.config import (
1716
DEFAULT_BASE_URI,
1817
DEFAULT_EVENTS_URI,

ldclient/impl/datasystem/async_store.py

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ def _stage_persist_delta(self, collections: Collections, persist: bool) -> Optio
105105
self._persist = persist
106106
return collections if self._should_persist() else None
107107

108-
async def apply_async(self, change_set: ChangeSet, persist: bool) -> None:
108+
async def apply(self, change_set: ChangeSet, persist: bool) -> None:
109109
"""
110110
Apply a changeset to the store using the async persist path.
111111
@@ -147,15 +147,18 @@ async def apply_async(self, change_set: ChangeSet, persist: bool) -> None:
147147
return
148148

149149
async with self._async_persist_lock:
150-
if is_full:
151-
await store.init(pending)
152-
else:
153-
for kind in pending:
154-
kind_data = pending[kind]
155-
for key in kind_data:
156-
await store.upsert(kind, kind_data[key])
157-
158-
async def commit_async(self) -> Optional[Exception]:
150+
try:
151+
if is_full:
152+
await store.init(pending)
153+
else:
154+
for kind in pending:
155+
kind_data = pending[kind]
156+
for key in kind_data:
157+
await store.upsert(kind, kind_data[key])
158+
except Exception as e:
159+
log.error("Store: couldn't persist changeset: %s", str(e))
160+
161+
async def commit(self) -> Optional[Exception]:
159162
"""
160163
Persist the data in the memory store to the async persistent store, if configured.
161164
@@ -192,7 +195,7 @@ def __mapping(data: Dict[str, ModelEntity]) -> Dict[str, Dict[str, Any]]:
192195
return e
193196
return None
194197

195-
async def close_async(self) -> Optional[Exception]:
198+
async def close(self) -> Optional[Exception]:
196199
"""
197200
Close the store and the async persistent store, if configured.
198201

ldclient/testing/impl/datasystem/test_fdv2_async_persistence.py

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -94,12 +94,12 @@ def _delta_changeset(key: str, version: int, on: bool) -> ChangeSet:
9494

9595

9696
@pytest.mark.asyncio
97-
async def test_apply_async_full_transfer_persists_via_init():
97+
async def test_apply_full_transfer_persists_via_init():
9898
async_store = FakeAsyncFeatureStore()
9999
store = AsyncStore(Listeners(), Listeners())
100100
store.with_async_persistence(async_store, True, None)
101101

102-
await store.apply_async(_full_changeset("flag-a", 1, True), True)
102+
await store.apply(_full_changeset("flag-a", 1, True), True)
103103

104104
# After a full transfer the memory store is authoritative and serves reads
105105
assert store.get_active_store() is store._memory_store
@@ -113,30 +113,30 @@ async def test_apply_async_full_transfer_persists_via_init():
113113

114114

115115
@pytest.mark.asyncio
116-
async def test_apply_async_delta_persists_via_upsert():
116+
async def test_apply_delta_persists_via_upsert():
117117
async_store = FakeAsyncFeatureStore()
118118
store = AsyncStore(Listeners(), Listeners())
119119
store.with_async_persistence(async_store, True, None)
120120

121-
await store.apply_async(_full_changeset("flag-a", 1, True), True)
121+
await store.apply(_full_changeset("flag-a", 1, True), True)
122122
async_store.init_called_count = 0
123123
async_store.upsert_calls = []
124124

125-
await store.apply_async(_delta_changeset("flag-a", 2, False), True)
125+
await store.apply(_delta_changeset("flag-a", 2, False), True)
126126

127127
assert any(call[1] == "flag-a" and call[2] == 2 for call in async_store.upsert_calls)
128128
assert async_store.snapshot()[FEATURES]["flag-a"]["on"] is False
129129

130130

131131
@pytest.mark.asyncio
132-
async def test_apply_async_read_only_does_not_persist():
132+
async def test_apply_read_only_does_not_persist():
133133
async_store = FakeAsyncFeatureStore()
134134
store = AsyncStore(Listeners(), Listeners())
135135
# writable=False -> READ_ONLY: never write to the store
136136
store.with_async_persistence(async_store, False, None)
137137

138-
await store.apply_async(_full_changeset("flag-a", 1, True), True)
139-
await store.apply_async(_delta_changeset("flag-a", 2, False), True)
138+
await store.apply(_full_changeset("flag-a", 1, True), True)
139+
await store.apply(_delta_changeset("flag-a", 2, False), True)
140140

141141
assert async_store.init_called_count == 0
142142
assert async_store.upsert_calls == []
@@ -145,7 +145,7 @@ async def test_apply_async_read_only_does_not_persist():
145145

146146

147147
@pytest.mark.asyncio
148-
async def test_apply_async_fires_change_set_listeners():
148+
async def test_apply_fires_change_set_listeners():
149149
async_store = FakeAsyncFeatureStore()
150150
received: List[ChangeSet] = []
151151
change_set_listeners = Listeners()
@@ -155,29 +155,29 @@ async def test_apply_async_fires_change_set_listeners():
155155
store.with_async_persistence(async_store, True, None)
156156

157157
cs = _full_changeset("flag-a", 1, True)
158-
await store.apply_async(cs, True)
158+
await store.apply(cs, True)
159159

160160
assert received == [cs]
161161

162162

163163
@pytest.mark.asyncio
164-
async def test_commit_async_writes_memory_to_store():
164+
async def test_commit_writes_memory_to_store():
165165
async_store = FakeAsyncFeatureStore()
166166
store = AsyncStore(Listeners(), Listeners())
167167
store.with_async_persistence(async_store, True, None)
168168

169169
# Populate memory without persisting yet (read-only apply through memory)
170-
await store.apply_async(_full_changeset("flag-a", 1, True), True)
170+
await store.apply(_full_changeset("flag-a", 1, True), True)
171171
async_store.init_called_count = 0
172172

173-
err = await store.commit_async()
173+
err = await store.commit()
174174
assert err is None
175175
assert async_store.init_called_count == 1
176176
assert "flag-a" in async_store.snapshot()[FEATURES]
177177

178178

179179
@pytest.mark.asyncio
180-
async def test_commit_async_returns_error_on_failure():
180+
async def test_commit_returns_error_on_failure():
181181
class FailingStore(FakeAsyncFeatureStore):
182182
async def init(self, all_data):
183183
raise RuntimeError("boom")
@@ -186,22 +186,22 @@ async def init(self, all_data):
186186
store = AsyncStore(Listeners(), Listeners())
187187
# Read-only so the deferred persist is skipped and memory is populated first.
188188
store.with_async_persistence(async_store, False, None)
189-
await store.apply_async(_full_changeset("flag-a", 1, True), True)
189+
await store.apply(_full_changeset("flag-a", 1, True), True)
190190

191191
# Now make it writable and commit, which triggers the failing init.
192192
store._persistent_store_writable = True
193-
err = await store.commit_async()
193+
err = await store.commit()
194194
assert isinstance(err, RuntimeError)
195195
assert str(err) == "boom"
196196

197197

198198
@pytest.mark.asyncio
199-
async def test_close_async_closes_async_store():
199+
async def test_close_closes_async_store():
200200
async_store = FakeAsyncFeatureStore()
201201
store = AsyncStore(Listeners(), Listeners())
202202
store.with_async_persistence(async_store, True, None)
203203

204-
err = await store.close_async()
204+
err = await store.close()
205205
assert err is None
206206
assert async_store.closed is True
207207

ldclient/testing/test_feature_store_client_wrapper.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
from typing import Callable, List
33
from unittest.mock import Mock
44

5-
from ldclient.impl.datasystem.fdv1 import _FeatureStoreClientWrapper
65
from ldclient.impl.datastore.status import DataStoreUpdateSinkImpl
6+
from ldclient.impl.datasystem.fdv1 import _FeatureStoreClientWrapper
77
from ldclient.impl.listeners import Listeners
88

99

0 commit comments

Comments
 (0)