jebin2 commited on
Commit
8d69ae4
·
1 Parent(s): d72816f

table optimise

Browse files
core/models.py CHANGED
@@ -11,7 +11,7 @@ Tables:
11
  - ApiKeyUsage: API key load balancing
12
  - RateLimit: Rate limiting
13
  """
14
- from sqlalchemy import Column, Integer, String, Text, DateTime, JSON, Boolean, ForeignKey
15
  from sqlalchemy.orm import relationship
16
  from sqlalchemy.sql import func
17
  from core.database import Base
@@ -29,8 +29,8 @@ class User(Base):
29
  """
30
  __tablename__ = "users"
31
 
32
- id = Column(Integer, primary_key=True, autoincrement=True, index=True)
33
- user_id = Column(String(50), unique=True, index=True, nullable=False) # Backend generated UUID
34
  email = Column(String(255), unique=True, index=True, nullable=False)
35
 
36
  # Google OAuth fields
@@ -72,13 +72,12 @@ class ClientUser(Base):
72
  """
73
  __tablename__ = "client_users"
74
 
75
- id = Column(Integer, primary_key=True, autoincrement=True, index=True)
76
- user_id = Column(String(50), ForeignKey("users.user_id"), index=True, nullable=True) # Nullable for anonymous tracking
77
  client_user_id = Column(String(100), index=True, nullable=True) # Client-side temp identifier
78
 
79
- # IP tracking for network/location correlation
80
- ipv4_address = Column(String(15), nullable=True, index=True)
81
- ipv6_address = Column(String(45), nullable=True, index=True)
82
 
83
  # Device identification
84
  device_fingerprint = Column(String(255), nullable=True, index=True) # Browser fingerprint hash
@@ -110,11 +109,11 @@ class AuditLog(Base):
110
  """
111
  __tablename__ = "audit_logs"
112
 
113
- id = Column(Integer, primary_key=True, autoincrement=True, index=True)
114
  log_type = Column(String(20), index=True, nullable=False) # "client" or "server"
115
 
116
  # User tracking
117
- user_id = Column(String(50), ForeignKey("users.user_id"), nullable=True, index=True)
118
  client_user_id = Column(String(100), nullable=True, index=True) # For anonymous client logs
119
 
120
  # Event details
@@ -150,17 +149,22 @@ class GeminiJob(Base):
150
  Uses priority-tier system for worker assignment.
151
  """
152
  __tablename__ = "gemini_jobs"
 
 
 
 
 
153
 
154
- id = Column(Integer, primary_key=True, autoincrement=True, index=True)
155
  job_id = Column(String(100), unique=True, index=True, nullable=False) # Our ID for client
156
- user_id = Column(String(50), ForeignKey("users.user_id"), index=True, nullable=False)
157
  job_type = Column(String(20), index=True, nullable=False) # video, image, text, analyze
158
  third_party_id = Column(String(255), nullable=True) # Gemini operation name (for video)
159
- status = Column(String(20), default="queued", index=True) # queued, processing, completed, failed, cancelled
160
 
161
- # Priority-tier worker system
162
- priority = Column(String(10), default="fast", index=True) # fast (5s), medium (30s), slow (60s)
163
- next_process_at = Column(DateTime(timezone=True), nullable=True, index=True) # When worker should pick up again
164
  retry_count = Column(Integer, default=0) # Number of status check retries
165
 
166
  input_data = Column(JSON, nullable=True) # Request details (prompt, settings, etc.)
@@ -193,9 +197,9 @@ class PaymentTransaction(Base):
193
  """
194
  __tablename__ = "payment_transactions"
195
 
196
- id = Column(Integer, primary_key=True, autoincrement=True, index=True)
197
  transaction_id = Column(String(50), unique=True, index=True, nullable=False) # Our internal ID
198
- user_id = Column(String(50), ForeignKey("users.user_id"), index=True, nullable=False)
199
 
200
  # Order details
201
  gateway = Column(String(20), nullable=False) # razorpay, stripe
@@ -239,8 +243,8 @@ class Contact(Base):
239
  """
240
  __tablename__ = "contacts"
241
 
242
- id = Column(Integer, primary_key=True, autoincrement=True, index=True)
243
- user_id = Column(String(50), ForeignKey("users.user_id"), index=True, nullable=False)
244
  email = Column(String(255), nullable=False, index=True) # User's email
245
  subject = Column(String(500), nullable=True)
246
  message = Column(Text, nullable=False)
@@ -260,7 +264,7 @@ class RateLimit(Base):
260
  """
261
  __tablename__ = "rate_limits"
262
 
263
- id = Column(Integer, primary_key=True, autoincrement=True, index=True)
264
  identifier = Column(String(255), index=True, nullable=False) # IP or email
265
  endpoint = Column(String(255), index=True, nullable=False)
266
  attempts = Column(Integer, default=0)
 
11
  - ApiKeyUsage: API key load balancing
12
  - RateLimit: Rate limiting
13
  """
14
+ from sqlalchemy import Column, Integer, String, Text, DateTime, JSON, Boolean, ForeignKey, Index
15
  from sqlalchemy.orm import relationship
16
  from sqlalchemy.sql import func
17
  from core.database import Base
 
29
  """
30
  __tablename__ = "users"
31
 
32
+ id = Column(Integer, primary_key=True, autoincrement=True)
33
+ user_id = Column(String(50), unique=True, index=True, nullable=False) # Backend generated UUID (public-facing)
34
  email = Column(String(255), unique=True, index=True, nullable=False)
35
 
36
  # Google OAuth fields
 
72
  """
73
  __tablename__ = "client_users"
74
 
75
+ id = Column(Integer, primary_key=True, autoincrement=True)
76
+ user_id = Column(Integer, ForeignKey("users.id"), index=True, nullable=True) # FK to users.id (nullable for anonymous)
77
  client_user_id = Column(String(100), index=True, nullable=True) # Client-side temp identifier
78
 
79
+ # IP tracking for network/location correlation (standardized to single column)
80
+ ip_address = Column(String(45), nullable=True, index=True) # Supports both IPv4 and IPv6
 
81
 
82
  # Device identification
83
  device_fingerprint = Column(String(255), nullable=True, index=True) # Browser fingerprint hash
 
109
  """
110
  __tablename__ = "audit_logs"
111
 
112
+ id = Column(Integer, primary_key=True, autoincrement=True)
113
  log_type = Column(String(20), index=True, nullable=False) # "client" or "server"
114
 
115
  # User tracking
116
+ user_id = Column(Integer, ForeignKey("users.id"), nullable=True, index=True) # FK to users.id
117
  client_user_id = Column(String(100), nullable=True, index=True) # For anonymous client logs
118
 
119
  # Event details
 
149
  Uses priority-tier system for worker assignment.
150
  """
151
  __tablename__ = "gemini_jobs"
152
+
153
+ # Composite index for efficient queue polling: WHERE status = X AND next_process_at <= NOW() ORDER BY priority
154
+ __table_args__ = (
155
+ Index('ix_jobs_queue_poll', 'status', 'next_process_at', 'priority'),
156
+ )
157
 
158
+ id = Column(Integer, primary_key=True, autoincrement=True)
159
  job_id = Column(String(100), unique=True, index=True, nullable=False) # Our ID for client
160
+ user_id = Column(Integer, ForeignKey("users.id"), index=True, nullable=False) # FK to users.id
161
  job_type = Column(String(20), index=True, nullable=False) # video, image, text, analyze
162
  third_party_id = Column(String(255), nullable=True) # Gemini operation name (for video)
163
+ status = Column(String(20), default="queued") # queued, processing, completed, failed, cancelled
164
 
165
+ # Priority-tier worker system (indexed via composite index above)
166
+ priority = Column(String(10), default="fast") # fast (5s), medium (30s), slow (60s)
167
+ next_process_at = Column(DateTime(timezone=True), nullable=True) # When worker should pick up again
168
  retry_count = Column(Integer, default=0) # Number of status check retries
169
 
170
  input_data = Column(JSON, nullable=True) # Request details (prompt, settings, etc.)
 
197
  """
198
  __tablename__ = "payment_transactions"
199
 
200
+ id = Column(Integer, primary_key=True, autoincrement=True)
201
  transaction_id = Column(String(50), unique=True, index=True, nullable=False) # Our internal ID
202
+ user_id = Column(Integer, ForeignKey("users.id"), index=True, nullable=False) # FK to users.id
203
 
204
  # Order details
205
  gateway = Column(String(20), nullable=False) # razorpay, stripe
 
243
  """
244
  __tablename__ = "contacts"
245
 
246
+ id = Column(Integer, primary_key=True, autoincrement=True)
247
+ user_id = Column(Integer, ForeignKey("users.id"), index=True, nullable=False) # FK to users.id
248
  email = Column(String(255), nullable=False, index=True) # User's email
249
  subject = Column(String(500), nullable=True)
250
  message = Column(Text, nullable=False)
 
264
  """
265
  __tablename__ = "rate_limits"
266
 
267
+ id = Column(Integer, primary_key=True, autoincrement=True)
268
  identifier = Column(String(255), index=True, nullable=False) # IP or email
269
  endpoint = Column(String(255), index=True, nullable=False)
270
  attempts = Column(Integer, default=0)
routers/auth.py CHANGED
@@ -145,7 +145,7 @@ async def google_auth(
145
  if request.temp_user_id:
146
  # Check if this client mapping exists
147
  client_query = select(ClientUser).where(
148
- ClientUser.user_id == user.user_id,
149
  ClientUser.client_user_id == request.temp_user_id
150
  )
151
  client_result = await db.execute(client_query)
@@ -154,10 +154,9 @@ async def google_auth(
154
  if not existing_client:
155
  # Create new client user mapping
156
  client_user = ClientUser(
157
- user_id=user.user_id,
158
  client_user_id=request.temp_user_id,
159
- ipv4_address=ip if ":" not in ip else None,
160
- ipv6_address=ip if ":" in ip else None,
161
  last_seen_at=datetime.utcnow()
162
  )
163
  db.add(client_user)
@@ -181,10 +180,9 @@ async def google_auth(
181
  # Create client user mapping if temp_user_id provided
182
  if request.temp_user_id:
183
  client_user = ClientUser(
184
- user_id=user.user_id,
185
  client_user_id=request.temp_user_id,
186
- ipv4_address=ip if ":" not in ip else None,
187
- ipv6_address=ip if ":" in ip else None,
188
  last_seen_at=datetime.utcnow()
189
  )
190
  db.add(client_user)
@@ -192,7 +190,7 @@ async def google_auth(
192
  # Log successful auth
193
  audit_log = AuditLog(
194
  log_type="server",
195
- user_id=user.user_id,
196
  client_user_id=request.temp_user_id,
197
  action="google_auth",
198
  ip_address=ip,
@@ -332,7 +330,7 @@ async def logout(
332
  # Log logout
333
  audit_log = AuditLog(
334
  log_type="server",
335
- user_id=user.user_id,
336
  action="logout",
337
  ip_address=ip,
338
  status="success"
 
145
  if request.temp_user_id:
146
  # Check if this client mapping exists
147
  client_query = select(ClientUser).where(
148
+ ClientUser.user_id == user.id, # Integer FK comparison
149
  ClientUser.client_user_id == request.temp_user_id
150
  )
151
  client_result = await db.execute(client_query)
 
154
  if not existing_client:
155
  # Create new client user mapping
156
  client_user = ClientUser(
157
+ user_id=user.id, # Integer FK to users.id
158
  client_user_id=request.temp_user_id,
159
+ ip_address=ip, # Standardized IP column
 
160
  last_seen_at=datetime.utcnow()
161
  )
162
  db.add(client_user)
 
180
  # Create client user mapping if temp_user_id provided
181
  if request.temp_user_id:
182
  client_user = ClientUser(
183
+ user_id=user.id, # Integer FK to users.id (will be set after flush)
184
  client_user_id=request.temp_user_id,
185
+ ip_address=ip, # Standardized IP column
 
186
  last_seen_at=datetime.utcnow()
187
  )
188
  db.add(client_user)
 
190
  # Log successful auth
191
  audit_log = AuditLog(
192
  log_type="server",
193
+ user_id=user.id, # Integer FK to users.id
194
  client_user_id=request.temp_user_id,
195
  action="google_auth",
196
  ip_address=ip,
 
330
  # Log logout
331
  audit_log = AuditLog(
332
  log_type="server",
333
+ user_id=user.id, # Integer FK to users.id
334
  action="logout",
335
  ip_address=ip,
336
  status="success"
routers/blink.py CHANGED
@@ -177,8 +177,7 @@ async def get_client_users(
177
  "id": item.id,
178
  "user_id": item.user_id,
179
  "client_user_id": item.client_user_id,
180
- "ipv4_address": item.ipv4_address,
181
- "ipv6_address": item.ipv6_address,
182
  "device_fingerprint": item.device_fingerprint,
183
  "device_info": item.device_info,
184
  "created_at": item.created_at.isoformat() if item.created_at else None,
@@ -495,14 +494,13 @@ async def blink(
495
  country, region = await get_geolocation(ip_address)
496
 
497
  # Determine server user_id (if authenticated)
498
- server_user_id = current_user.user_id if current_user else None
499
 
500
  # Always create a ClientUser entry (user_id is None for anonymous users)
501
  new_client_user = ClientUser(
502
- user_id=server_user_id, # None if anonymous, user_id if authenticated
503
  client_user_id=client_user_id,
504
- ipv4_address=ipv4_address,
505
- ipv6_address=ipv6_address,
506
  device_info={"user_agent": user_agent} if user_agent else None
507
  )
508
  db.add(new_client_user)
 
177
  "id": item.id,
178
  "user_id": item.user_id,
179
  "client_user_id": item.client_user_id,
180
+ "ip_address": item.ip_address, # Standardized IP column
 
181
  "device_fingerprint": item.device_fingerprint,
182
  "device_info": item.device_info,
183
  "created_at": item.created_at.isoformat() if item.created_at else None,
 
494
  country, region = await get_geolocation(ip_address)
495
 
496
  # Determine server user_id (if authenticated)
497
+ server_user_id = current_user.id if current_user else None # Integer FK or None
498
 
499
  # Always create a ClientUser entry (user_id is None for anonymous users)
500
  new_client_user = ClientUser(
501
+ user_id=server_user_id, # Integer FK (None if anonymous)
502
  client_user_id=client_user_id,
503
+ ip_address=ip_address, # Standardized IP column
 
504
  device_info={"user_agent": user_agent} if user_agent else None
505
  )
506
  db.add(new_client_user)
routers/contact.py CHANGED
@@ -68,7 +68,7 @@ async def submit_contact(
68
  try:
69
  # Create contact record
70
  contact = Contact(
71
- user_id=user.user_id,
72
  email=user.email,
73
  subject=request_body.subject.strip() if request_body.subject else None,
74
  message=request_body.message.strip(),
 
68
  try:
69
  # Create contact record
70
  contact = Contact(
71
+ user_id=user.id, # Integer FK to users.id
72
  email=user.email,
73
  subject=request_body.subject.strip() if request_body.subject else None,
74
  message=request_body.message.strip(),
routers/gemini.py CHANGED
@@ -83,7 +83,7 @@ async def create_job(
83
 
84
  job = GeminiJob(
85
  job_id=job_id,
86
- user_id=user.user_id,
87
  job_type=job_type,
88
  status="queued",
89
  priority=priority,
@@ -277,14 +277,14 @@ async def get_jobs(
277
 
278
  # Query jobs for the current user
279
  query = select(GeminiJob).where(
280
- GeminiJob.user_id == user.user_id
281
  ).order_by(GeminiJob.created_at.desc()).offset(offset).limit(limit)
282
 
283
  result = await db.execute(query)
284
  jobs = result.scalars().all()
285
 
286
  # Get total count
287
- count_query = select(func.count()).where(GeminiJob.user_id == user.user_id)
288
  count_result = await db.execute(count_query)
289
  total_count = count_result.scalar()
290
 
@@ -336,7 +336,7 @@ async def get_job_status(
336
  """
337
  query = select(GeminiJob).where(
338
  GeminiJob.job_id == job_id,
339
- GeminiJob.user_id == user.user_id
340
  )
341
  result = await db.execute(query)
342
  job = result.scalar_one_or_none()
@@ -404,7 +404,7 @@ async def download_video(
404
 
405
  query = select(GeminiJob).where(
406
  GeminiJob.job_id == job_id,
407
- GeminiJob.user_id == user.user_id,
408
  GeminiJob.job_type == "video"
409
  )
410
  result = await db.execute(query)
@@ -480,7 +480,7 @@ async def cancel_job(
480
  """
481
  query = select(GeminiJob).where(
482
  GeminiJob.job_id == job_id,
483
- GeminiJob.user_id == user.user_id
484
  )
485
  result = await db.execute(query)
486
  job = result.scalar_one_or_none()
@@ -522,7 +522,7 @@ async def delete_job(
522
  """
523
  query = select(GeminiJob).where(
524
  GeminiJob.job_id == job_id,
525
- GeminiJob.user_id == user.user_id
526
  )
527
  result = await db.execute(query)
528
  job = result.scalar_one_or_none()
 
83
 
84
  job = GeminiJob(
85
  job_id=job_id,
86
+ user_id=user.id, # Integer FK to users.id
87
  job_type=job_type,
88
  status="queued",
89
  priority=priority,
 
277
 
278
  # Query jobs for the current user
279
  query = select(GeminiJob).where(
280
+ GeminiJob.user_id == user.id # Integer FK comparison
281
  ).order_by(GeminiJob.created_at.desc()).offset(offset).limit(limit)
282
 
283
  result = await db.execute(query)
284
  jobs = result.scalars().all()
285
 
286
  # Get total count
287
+ count_query = select(func.count()).where(GeminiJob.user_id == user.id) # Integer FK comparison
288
  count_result = await db.execute(count_query)
289
  total_count = count_result.scalar()
290
 
 
336
  """
337
  query = select(GeminiJob).where(
338
  GeminiJob.job_id == job_id,
339
+ GeminiJob.user_id == user.id # Integer FK comparison
340
  )
341
  result = await db.execute(query)
342
  job = result.scalar_one_or_none()
 
404
 
405
  query = select(GeminiJob).where(
406
  GeminiJob.job_id == job_id,
407
+ GeminiJob.user_id == user.id, # Integer FK comparison
408
  GeminiJob.job_type == "video"
409
  )
410
  result = await db.execute(query)
 
480
  """
481
  query = select(GeminiJob).where(
482
  GeminiJob.job_id == job_id,
483
+ GeminiJob.user_id == user.id # Integer FK comparison
484
  )
485
  result = await db.execute(query)
486
  job = result.scalar_one_or_none()
 
522
  """
523
  query = select(GeminiJob).where(
524
  GeminiJob.job_id == job_id,
525
+ GeminiJob.user_id == user.id # Integer FK comparison
526
  )
527
  result = await db.execute(query)
528
  job = result.scalar_one_or_none()
routers/payments.py CHANGED
@@ -271,7 +271,7 @@ async def create_order(
271
  # Save transaction to database
272
  transaction = PaymentTransaction(
273
  transaction_id=transaction_id,
274
- user_id=user.user_id,
275
  gateway="razorpay",
276
  gateway_order_id=order["id"],
277
  package_id=package.id,
@@ -333,7 +333,7 @@ async def verify_payment(
333
  result = await db.execute(
334
  select(PaymentTransaction).where(
335
  PaymentTransaction.gateway_order_id == request.razorpay_order_id,
336
- PaymentTransaction.user_id == user.user_id
337
  )
338
  )
339
  transaction = result.scalar_one_or_none()
@@ -487,7 +487,7 @@ async def razorpay_webhook(
487
  if transaction:
488
  # Find user
489
  user_result = await db.execute(
490
- select(User).where(User.user_id == transaction.user_id)
491
  )
492
  user = user_result.scalar_one_or_none()
493
 
@@ -570,7 +570,7 @@ async def get_payment_history(
570
  # Get total count
571
  count_result = await db.execute(
572
  select(func.count(PaymentTransaction.id))
573
- .where(PaymentTransaction.user_id == user.user_id)
574
  )
575
  total_count = count_result.scalar() or 0
576
 
@@ -580,7 +580,7 @@ async def get_payment_history(
580
  # Get paginated transactions
581
  result = await db.execute(
582
  select(PaymentTransaction)
583
- .where(PaymentTransaction.user_id == user.user_id)
584
  .order_by(desc(PaymentTransaction.created_at))
585
  .offset(offset)
586
  .limit(limit)
 
271
  # Save transaction to database
272
  transaction = PaymentTransaction(
273
  transaction_id=transaction_id,
274
+ user_id=user.id, # Integer FK to users.id
275
  gateway="razorpay",
276
  gateway_order_id=order["id"],
277
  package_id=package.id,
 
333
  result = await db.execute(
334
  select(PaymentTransaction).where(
335
  PaymentTransaction.gateway_order_id == request.razorpay_order_id,
336
+ PaymentTransaction.user_id == user.id # Integer FK comparison
337
  )
338
  )
339
  transaction = result.scalar_one_or_none()
 
487
  if transaction:
488
  # Find user
489
  user_result = await db.execute(
490
+ select(User).where(User.id == transaction.user_id) # Integer FK lookup
491
  )
492
  user = user_result.scalar_one_or_none()
493
 
 
570
  # Get total count
571
  count_result = await db.execute(
572
  select(func.count(PaymentTransaction.id))
573
+ .where(PaymentTransaction.user_id == user.id) # Integer FK comparison
574
  )
575
  total_count = count_result.scalar() or 0
576
 
 
580
  # Get paginated transactions
581
  result = await db.execute(
582
  select(PaymentTransaction)
583
+ .where(PaymentTransaction.user_id == user.id) # Integer FK comparison
584
  .order_by(desc(PaymentTransaction.created_at))
585
  .offset(offset)
586
  .limit(limit)
services/credit_service.py CHANGED
@@ -162,7 +162,7 @@ async def refund_credit(session: AsyncSession, job, reason: str) -> bool:
162
  from core.models import User
163
 
164
  result = await session.execute(
165
- select(User).where(User.user_id == job.user_id)
166
  )
167
  user = result.scalar_one_or_none()
168
 
 
162
  from core.models import User
163
 
164
  result = await session.execute(
165
+ select(User).where(User.id == job.user_id)
166
  )
167
  user = result.scalar_one_or_none()
168