Skip to content

Commit 5c5dcd5

Browse files
committed
fix: Address potential race condition in FeatureStore update_availability
1 parent 54e62cc commit 5c5dcd5

File tree

1 file changed

+25
-24
lines changed

1 file changed

+25
-24
lines changed

ldclient/impl/datasystem/fdv2.py

Lines changed: 25 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -170,38 +170,39 @@ def __wrapper(self, fn: Callable):
170170
raise
171171

172172
def __update_availability(self, available: bool):
173-
try:
174-
self.__lock.lock()
175-
if available == self.__last_available:
176-
return
177-
self.__last_available = available
178-
finally:
173+
state_changed = False
174+
poller_to_stop = None
175+
task_to_start = None
176+
177+
self.__lock.lock()
178+
if available == self.__last_available:
179179
self.__lock.unlock()
180+
return
181+
182+
state_changed = True
183+
self.__last_available = available
184+
185+
if available:
186+
poller_to_stop = self.__poller
187+
self.__poller = None
188+
elif self.__poller is None:
189+
task_to_start = RepeatingTask("ldclient.check-availability", 0.5, 0, self.__check_availability)
190+
self.__poller = task_to_start
191+
self.__lock.unlock()
180192

181193
if available:
182194
log.warning("Persistent store is available again")
195+
else:
196+
log.warning("Detected persistent store unavailability; updates will be cached until it recovers")
183197

184198
status = DataStoreStatus(available, True)
185199
self.__store_update_sink.update_status(status)
186200

187-
if available:
188-
try:
189-
self.__lock.lock()
190-
if self.__poller is not None:
191-
self.__poller.stop()
192-
self.__poller = None
193-
finally:
194-
self.__lock.unlock()
201+
if poller_to_stop is not None:
202+
poller_to_stop.stop()
195203

196-
return
197-
198-
log.warning("Detected persistent store unavailability; updates will be cached until it recovers")
199-
task = RepeatingTask("ldclient.check-availability", 0.5, 0, self.__check_availability)
200-
201-
self.__lock.lock()
202-
self.__poller = task
203-
self.__poller.start()
204-
self.__lock.unlock()
204+
if task_to_start is not None:
205+
task_to_start.start()
205206

206207
def __check_availability(self):
207208
try:
@@ -487,7 +488,7 @@ def synchronizer_loop(self: 'FDv2'):
487488

488489
log.info("Recovery condition met, returning to primary synchronizer")
489490
except Exception as e:
490-
log.error("Failed to build primary synchronizer: %s", e)
491+
log.error("Failed to build synchronizer: %s", e)
491492
break
492493

493494
except Exception as e:

0 commit comments

Comments
 (0)