Skip to content

Commit d14c83c

Browse files
committed
Fix eventual consistency and introduce new flag
1 parent 4d91da1 commit d14c83c

File tree

3 files changed

+32
-8
lines changed

3 files changed

+32
-8
lines changed

fastapi_users_db_dynamodb/__init__.py

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -225,12 +225,19 @@ def _ensure_email_lower(self, data: dict[str, Any]) -> None:
225225
if "email" in data and isinstance(data["email"], str):
226226
data["email"] = data["email"].lower()
227227

228-
async def get(self, id: ID | str) -> UP | None:
228+
async def get(
229+
self,
230+
id: ID | str,
231+
instant_update: bool = False,
232+
) -> UP | None:
229233
"""Get a user by id and hydrate oauth_accounts if available."""
230234
id_str = self._ensure_id_str(id)
231235

232236
async with self._table(self.user_table_name, self._resource_region) as table:
233-
resp = await table.get_item(Key={self.primary_key: id_str})
237+
resp = await table.get_item(
238+
Key={self.primary_key: id_str},
239+
ConsistentRead=instant_update,
240+
)
234241
item = resp.get("Item")
235242
user = self._item_to_user(item)
236243

@@ -335,11 +342,19 @@ async def create(self, create_dict: dict[str, Any]) -> UP:
335342
raise ValueError("Could not cast DB item to User model")
336343
return refreshed_user
337344

338-
async def update(self, user: UP, update_dict: dict[str, Any]) -> UP:
345+
async def update(
346+
self,
347+
user: UP,
348+
update_dict: dict[str, Any],
349+
instant_update: bool = False,
350+
) -> UP:
339351
"""Update a user with update_dict and return the updated UP instance."""
340352
user_id = self._extract_id_from_user(user)
341353
async with self._table(self.user_table_name, self._resource_region) as table:
342-
resp = await table.get_item(Key={self.primary_key: user_id})
354+
resp = await table.get_item(
355+
Key={self.primary_key: user_id},
356+
ConsistentRead=instant_update,
357+
)
343358
current = resp.get("Item", None)
344359

345360
if not current:
@@ -414,7 +429,7 @@ async def update_oauth_account(
414429
user: UP,
415430
oauth_account: OAP, # type: ignore
416431
update_dict: dict[str, Any],
417-
) -> UP | None:
432+
) -> UP:
418433
"""Update an OAuth account and return the refreshed user (UP)."""
419434
if self.oauth_account_table is None or self.oauth_account_table_name is None:
420435
raise NotImplementedError()

fastapi_users_db_dynamodb/access_token.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,12 +119,16 @@ def _ensure_token(self, token: Any) -> str:
119119
return str(token)
120120

121121
async def get_by_token(
122-
self, token: str, max_age: datetime | None = None
122+
self,
123+
token: str,
124+
max_age: datetime | None = None,
125+
instant_update: bool = False,
123126
) -> AP | None:
124127
"""Retrieve an access token by token string."""
125128
async with self._table(self.table_name, self._resource_region) as table:
126129
resp = await table.get_item(
127-
Key={self.primary_key: self._ensure_token(token)}
130+
Key={self.primary_key: self._ensure_token(token)},
131+
ConsistentRead=instant_update,
128132
)
129133
item = resp.get("Item")
130134

tests/test_users.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,8 +199,13 @@ async def test_queries_oauth(
199199
)
200200
assert user.oauth_accounts[0].access_token == "NEW_TOKEN" # type: ignore
201201

202+
#! IMPORTANT: Since DynamoDB uses eventual consistency, we need a small delay (e.g. `time.sleep(0.01)`) \
203+
#! to ensure the user was fully updated. In production, this should be negligible. \
204+
#! Alternatively, the `get` and `update` methods of the `DynamoDBDatabase` class allow users \
205+
#! to enable consistent reads via the `instant_update` argument.
206+
202207
# Get by id
203-
id_user = await dynamodb_user_db_oauth.get(user.id)
208+
id_user = await dynamodb_user_db_oauth.get(user.id, instant_update=True)
204209
assert id_user is not None
205210
assert id_user.id == user.id
206211
assert id_user.oauth_accounts[0].access_token == "NEW_TOKEN" # type: ignore

0 commit comments

Comments
 (0)