8000 Prevent race condition for Store write operations (#6689) · localstack/localstack@ae4a2ad · GitHub
[go: up one dir, main page]

Skip to content

Commit ae4a2ad

Browse files
Prevent race condition for Store write operations (#6689)
1 parent fc8b122 commit ae4a2ad

File tree

1 file changed

+34
-19
lines changed

1 file changed

+34
-19
lines changed

localstack/services/stores.py

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ class SqsStore(BaseStore):
2525

2626
import re
2727
from collections.abc import Callable
28+
from threading import RLock
2829
from typing import Any, Type, TypeVar, Union
2930

3031
from boto3 import Session
@@ -141,36 +142,41 @@ class RegionBundle(dict):
141142
"""
142143

143144
def __init__(
144-
self, service_name: str, store: Type[BaseStoreType], account_id: str, validate: bool = True
145+
self,
146+
service_name: str,
147+
store: Type[BaseStoreType],
148+
account_id: str,
149+
validate: bool = True,
150+
lock: RLock = None,
145151
):
146152
self.store = store
147153
self.account_id = account_id
148154
self.service_name = service_name
149155
self.validate = validate
156+
self.lock = lock or RLock()
150157

151158
self.valid_regions = Session().get_available_regions(service_name)
152159

153160
# Keeps track of all cross-region attributes
154161
self._global = {}
155162

156163
def __getitem__(self, region_name) -> BaseStoreType:
157-
if region_name in self.keys():
158-
return super().__getitem__(region_name)
159-
160164
if self.validate and region_name not in self.valid_regions:
161165
# Tip: Try using a valid region or valid service name
162166
raise ValueError(
163167
f"'{region_name}' is not a valid AWS region name for {self.service_name}"
164168
)
165169

166-
store_obj = self.store()
170+
with self.lock:
171+
if region_name not in self.keys():
172+
store_obj = self.store()
167173

168-
store_obj._global = self._global
169-
store_obj._service_name = self.service_name
170-
store_obj._account_id = self.account_id
171-
store_obj._region_name = region_name
174+
store_obj._global = self._global
175+
store_obj._service_name = self.service_name
176+
store_obj._account_id = self.account_id
177+
store_obj._region_name = region_name
172178

173-
self[region_name] = store_obj
179+
self[region_name] = store_obj
174180

175181
return super().__getitem__(region_name)
176182

@@ -187,8 +193,11 @@ def reset(self):
187193
# reset the local attributes
188194
elif attr.startswith(LOCAL_ATTR_PREFIX):
189195
delattr(store_inst, attr)
196+
190197
self._global.clear()
191-
self.clear()
198+
199+
with self.lock:
200+
self.clear()
192201

193202

194203
class AccountRegionBundle(dict):
@@ -205,23 +214,29 @@ def __init__(self, service_name: str, store: Type[BaseStoreType], validate: bool
205214
self.service_name = service_name
206215
self.store = store
207216
self.validate = validate
217+
self.lock = RLock()
208218

209219
def __getitem__(self, account_id: str) -> RegionBundle:
210220
if self.validate and not re.match(r"\d{12}", account_id):
211221
raise ValueError(f"'{account_id}' is not a valid AWS account ID")
212222

213-
if account_id not in self.keys():
214-
self[account_id] = RegionBundle(
215-
service_name=self.service_name,
216-
store=self.store,
217-
account_id=account_id,
218-
validate=self.validate,
219-
)
223+
with self.lock:
224+
if account_id not in self.keys():
225+
self[account_id] = RegionBundle(
226+
service_name=self.service_name,
227+
store=self.store,
228+
account_id=account_id,
229+
validate=self.validate,
230+
lock=self.lock,
231+
)
232+
220233
return super().__getitem__(account_id)
221234

222235
def reset(self):
223236
"""Clear all store data."""
224237
# For safety, clear all referenced region bundles, if any
225238
for region_bundle in self.values():
226239
region_bundle.reset()
227-
self.clear()
240+
241+
with self.lock:
242+
self.clear()

0 commit comments

Comments
 (0)
0