Skip to content

Commit 06cb8eb

Browse files
x0sinaImMohammad20000
authored andcommitted
perf(user): speed up delete/update paths by avoiding extra relation and lifetime-usage loads
1 parent ac79456 commit 06cb8eb

File tree

3 files changed

+78
-41
lines changed

3 files changed

+78
-41
lines changed

app/operation/__init__.py

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -108,8 +108,25 @@ async def get_validated_sub(self, db: AsyncSession, token: str) -> User:
108108

109109
return db_user
110110

111-
async def get_validated_user(self, db: AsyncSession, username: str, admin: AdminDetails) -> User:
112-
db_user = await get_user(db, username, load_usage_logs=False)
111+
async def get_validated_user(
112+
self,
113+
db: AsyncSession,
114+
username: str,
115+
admin: AdminDetails,
116+
*,
117+
load_admin: bool = True,
118+
load_next_plan: bool = True,
119+
load_usage_logs: bool = False,
120+
load_groups: bool = True,
121+
) -> User:
122+
db_user = await get_user(
123+
db,
124+
username,
125+
load_admin=load_admin,
126+
load_next_plan=load_next_plan,
127+
load_usage_logs=load_usage_logs,
128+
load_groups=load_groups,
129+
)
113130
if not db_user:
114131
await self.raise_error(message="User not found", code=404)
115132

@@ -118,8 +135,25 @@ async def get_validated_user(self, db: AsyncSession, username: str, admin: Admin
118135

119136
return db_user
120137

121-
async def get_validated_user_by_id(self, db: AsyncSession, user_id: int, admin: AdminDetails) -> User:
122-
db_user = await get_user_by_id(db, user_id, load_usage_logs=False)
138+
async def get_validated_user_by_id(
139+
self,
140+
db: AsyncSession,
141+
user_id: int,
142+
admin: AdminDetails,
143+
*,
144+
load_admin: bool = True,
145+
load_next_plan: bool = True,
146+
load_usage_logs: bool = False,
147+
load_groups: bool = True,
148+
) -> User:
149+
db_user = await get_user_by_id(
150+
db,
151+
user_id,
152+
load_admin=load_admin,
153+
load_next_plan=load_next_plan,
154+
load_usage_logs=load_usage_logs,
155+
load_groups=load_groups,
156+
)
123157
if not db_user:
124158
await self.raise_error(message="User not found", code=404)
125159

app/operation/admin.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,13 @@ async def remove_all_users(self, db: AsyncSession, username: str, admin: AdminDe
211211

212212
user_operation = UserOperation(self.operator_type)
213213
serialized_users = [
214-
await user_operation.validate_user(db, user, include_subscription_url=False) for user in users
214+
await user_operation.validate_user(
215+
db,
216+
user,
217+
include_subscription_url=False,
218+
include_lifetime_used_traffic=False,
219+
)
220+
for user in users
215221
]
216222

217223
await remove_users(db, users)

app/operation/user.py

Lines changed: 33 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -207,46 +207,41 @@ async def _persist_bulk_users(
207207
return subscription_urls
208208

209209
async def validate_user(
210-
self, db: AsyncSession, db_user: User, *, include_subscription_url: bool = True
210+
self,
211+
db: AsyncSession,
212+
db_user: User,
213+
*,
214+
include_subscription_url: bool = True,
215+
include_lifetime_used_traffic: bool = True,
211216
) -> UserNotificationResponse:
212-
lifetime_used_traffic = await get_user_lifetime_used_traffic(db, db_user.id)
213-
group_names = [group.name for group in db_user.groups] if db_user.groups else []
214-
group_ids = [group.id for group in db_user.groups] if db_user.groups else []
215-
216-
user = UserNotificationResponse(
217-
id=db_user.id,
218-
username=db_user.username,
219-
status=db_user.status,
220-
used_traffic=db_user.used_traffic,
221-
lifetime_used_traffic=lifetime_used_traffic,
222-
created_at=db_user.created_at,
223-
edit_at=db_user.edit_at,
224-
online_at=db_user.online_at,
225-
proxy_settings=db_user.proxy_settings,
226-
expire=db_user.expire,
227-
data_limit=db_user.data_limit,
228-
data_limit_reset_strategy=db_user.data_limit_reset_strategy,
229-
note=db_user.note,
230-
on_hold_expire_duration=db_user.on_hold_expire_duration,
231-
on_hold_timeout=db_user.on_hold_timeout,
232-
group_ids=group_ids,
233-
next_plan=db_user.next_plan,
234-
auto_delete_in_days=db_user.auto_delete_in_days,
235-
admin=db_user.admin,
236-
group_names=group_names,
217+
lifetime_used_traffic = (
218+
await get_user_lifetime_used_traffic(db, db_user.id)
219+
if include_lifetime_used_traffic
220+
else int(db_user.used_traffic or 0)
237221
)
222+
loaded_groups = db_user.__dict__.get("groups") or []
223+
group_names = [group.name for group in loaded_groups]
224+
group_ids = [group.id for group in loaded_groups]
225+
user_data = dict(db_user.__dict__)
226+
user_data["lifetime_used_traffic"] = lifetime_used_traffic
227+
user_data["group_names"] = group_names
228+
user_data["group_ids"] = group_ids
229+
230+
user = UserNotificationResponse.model_validate(user_data)
238231
if include_subscription_url:
239232
user.subscription_url = await self.generate_subscription_url(user)
240233
return user
241234

242-
async def update_user(self, db: AsyncSession, db_user: User) -> UserNotificationResponse:
235+
async def update_user(
236+
self, db: AsyncSession, db_user: User, *, include_lifetime_used_traffic: bool = True
237+
) -> UserNotificationResponse:
243238
if self._is_non_blocking_sync_operator(self.operator_type):
244239
proto_user = await serialize_user(db_user)
245240
schedule_sync_task(sync_proto_user(proto_user))
246241
else:
247242
await sync_user(db_user)
248243

249-
user = await self.validate_user(db, db_user)
244+
user = await self.validate_user(db, db_user, include_lifetime_used_traffic=include_lifetime_used_traffic)
250245
return user
251246

252247
async def create_user(self, db: AsyncSession, new_user: UserCreate, admin: AdminDetails) -> UserResponse:
@@ -261,7 +256,7 @@ async def create_user(self, db: AsyncSession, new_user: UserCreate, admin: Admin
261256
except IntegrityError:
262257
await self.raise_error(message="User already exists", code=409, db=db)
263258

264-
user = await self.update_user(db, db_user)
259+
user = await self.update_user(db, db_user, include_lifetime_used_traffic=False)
265260

266261
logger.info(f'New user "{db_user.username}" with id "{db_user.id}" added by admin "{admin.username}"')
267262

@@ -281,7 +276,7 @@ async def _modify_user(
281276
old_status = db_user.status
282277

283278
db_user = await modify_user(db, db_user, modified_user)
284-
user = await self.update_user(db, db_user)
279+
user = await self.update_user(db, db_user, include_lifetime_used_traffic=False)
285280

286281
logger.info(f'User "{user.username}" with id "{db_user.id}" modified by admin "{admin.username}"')
287282

@@ -302,9 +297,11 @@ async def modify_user(
302297
return await self._modify_user(db, db_user, modified_user, admin)
303298

304299
async def remove_user(self, db: AsyncSession, username: str, admin: AdminDetails):
305-
db_user = await self.get_validated_user(db, username, admin)
300+
db_user = await self.get_validated_user(db, username, admin, load_next_plan=False, load_groups=False)
306301

307-
user = await self.validate_user(db, db_user, include_subscription_url=False)
302+
user = await self.validate_user(
303+
db, db_user, include_subscription_url=False, include_lifetime_used_traffic=False
304+
)
308305
await remove_user(db, db_user)
309306
if self._is_non_blocking_sync_operator(self.operator_type):
310307
schedule_sync_task(sync_remove_user(user))
@@ -320,7 +317,7 @@ async def _reset_user_data_usage(self, db: AsyncSession, db_user: User, admin: A
320317
old_status = db_user.status
321318

322319
db_user = await reset_user_data_usage(db=db, db_user=db_user)
323-
user = await self.update_user(db, db_user)
320+
user = await self.update_user(db, db_user, include_lifetime_used_traffic=False)
324321

325322
if user.status != old_status:
326323
asyncio.create_task(notification.user_status_change(user, admin))
@@ -340,7 +337,7 @@ async def revoke_user_sub(self, db: AsyncSession, username: str, admin: AdminDet
340337
db_user = await self.get_validated_user(db, username, admin)
341338

342339
db_user = await revoke_user_sub(db=db, db_user=db_user)
343-
user = await self.update_user(db, db_user)
340+
user = await self.update_user(db, db_user, include_lifetime_used_traffic=False)
344341

345342
asyncio.create_task(notification.user_subscription_revoked(user, admin))
346343

@@ -364,7 +361,7 @@ async def active_next_plan(self, db: AsyncSession, username: str, admin: AdminDe
364361

365362
db_user = await reset_user_by_next(db=db, db_user=db_user)
366363

367-
user = await self.update_user(db, db_user)
364+
user = await self.update_user(db, db_user, include_lifetime_used_traffic=False)
368365

369366
if user.status != old_status:
370367
asyncio.create_task(notification.user_status_change(user, admin))
@@ -383,7 +380,7 @@ async def set_owner(
383380
db_user = await self.get_validated_user(db, username, admin)
384381

385382
db_user = await set_owner(db, db_user, new_admin)
386-
user = await self.validate_user(db, db_user)
383+
user = await self.validate_user(db, db_user, include_lifetime_used_traffic=False)
387384
logger.info(f'{user.username}"owner successfully set to{new_admin.username} by admin "{admin.username}"')
388385

389386
return user

0 commit comments

Comments
 (0)