|
| 1 | +# ⚡ PERFORMANCE ISSUES - Priority: MEDIUM |
| 2 | + |
| 3 | +--- |
| 4 | + |
| 5 | +## Issue #39: Inefficient Prime Checking Algorithm |
| 6 | + |
| 7 | +### Title |
| 8 | +[PERFORMANCE]: Optimize prime number checking algorithm |
| 9 | + |
| 10 | +### Description |
| 11 | + |
| 12 | +**What:** Prime checking uses O(n) algorithm instead of O(√n). |
| 13 | + |
| 14 | +**Why it matters:** |
| 15 | +- Very slow for large numbers |
| 16 | +- Teaches inefficient approach |
| 17 | +- Unnecessary CPU usage |
| 18 | + |
| 19 | +**Where:** `basics/03_control_flow/02_check_prime.py`, line 9 |
| 20 | + |
| 21 | +**Current behavior:** |
| 22 | +```python |
| 23 | +for i in range(2, int(num * 0.5) + 1): # Checks up to n/2 |
| 24 | + if((num % i) == 0): |
| 25 | + prime = False |
| 26 | + break |
| 27 | +``` |
| 28 | + |
| 29 | +**Expected behavior:** |
| 30 | +```python |
| 31 | +import math |
| 32 | + |
| 33 | +for i in range(2, int(math.sqrt(num)) + 1): # Check only to √n |
| 34 | + if num % i == 0: |
| 35 | + return False |
| 36 | +``` |
| 37 | + |
| 38 | +**Performance comparison:** |
| 39 | +- For num=1000003 (prime): |
| 40 | + - Current: 500,000 iterations |
| 41 | + - Optimized: 1,000 iterations |
| 42 | + - **500x faster!** |
| 43 | + |
| 44 | +**Suggested approach:** |
| 45 | +```python |
| 46 | +import math |
| 47 | + |
| 48 | +def is_prime(num: int) -> bool: |
| 49 | + if num <= 1: |
| 50 | + return False |
| 51 | + if num <= 3: |
| 52 | + return True |
| 53 | + if num % 2 == 0 or num % 3 == 0: |
| 54 | + return False |
| 55 | + |
| 56 | + # Check using 6k±1 optimization |
| 57 | + i = 5 |
| 58 | + while i * i <= num: |
| 59 | + if num % i == 0 or num % (i + 2) == 0: |
| 60 | + return False |
| 61 | + i += 6 |
| 62 | + return True |
| 63 | +``` |
| 64 | + |
| 65 | +**Acceptance criteria:** |
| 66 | +- [ ] Implement √n optimization |
| 67 | +- [ ] Add early exit for even numbers |
| 68 | +- [ ] Add docstring with complexity O(√n) |
| 69 | +- [ ] Add performance comparison comment |
| 70 | +- [ ] Test with large primes (>1M) |
| 71 | + |
| 72 | +**Labels:** `performance`, `enhancement`, `good first issue` |
| 73 | + |
| 74 | +--- |
| 75 | + |
| 76 | +## Issue #40: No Database Indexes in Flask API |
| 77 | + |
| 78 | +### Title |
| 79 | +[PERFORMANCE]: Add database indexes for frequently queried columns |
| 80 | + |
| 81 | +### Description |
| 82 | + |
| 83 | +**What:** Database tables have no indexes, causing full table scans. |
| 84 | + |
| 85 | +**Why it matters:** |
| 86 | +- Queries slow down as data grows |
| 87 | +- O(n) instead of O(log n) lookups |
| 88 | +- Poor user experience |
| 89 | + |
| 90 | +**Where:** `rest_api/main.py`, Destination model |
| 91 | + |
| 92 | +**Current behavior:** |
| 93 | +```python |
| 94 | +class Destination(db.Model): |
| 95 | + id = db.Column(db.Integer, primary_key=True) # Auto-indexed |
| 96 | + destination = db.Column(db.String(100), nullable=False) # No index! |
| 97 | + country = db.Column(db.String(50), nullable=False) # No index! |
| 98 | + rating = db.Column(db.Float, nullable=False) # No index! |
| 99 | +``` |
| 100 | + |
| 101 | +**Expected behavior:** |
| 102 | +```python |
| 103 | +class Destination(db.Model): |
| 104 | + id = db.Column(db.Integer, primary_key=True) |
| 105 | + destination = db.Column(db.String(100), nullable=False, index=True) |
| 106 | + country = db.Column(db.String(50), nullable=False, index=True) |
| 107 | + rating = db.Column(db.Float, nullable=False, index=True) |
| 108 | + |
| 109 | + # Composite index for common queries |
| 110 | + __table_args__ = ( |
| 111 | + db.Index('idx_country_rating', 'country', 'rating'), |
| 112 | + ) |
| 113 | +``` |
| 114 | + |
| 115 | +**Performance impact:** |
| 116 | +- Without index: O(n) - scans all rows |
| 117 | +- With index: O(log n) - binary search |
| 118 | +- For 1M rows: 1,000,000 vs 20 operations |
| 119 | + |
| 120 | +**Acceptance criteria:** |
| 121 | +- [ ] Add index=True to searchable columns |
| 122 | +- [ ] Add composite indexes for common queries |
| 123 | +- [ ] Create migration script |
| 124 | +- [ ] Test query performance before/after |
| 125 | +- [ ] Document index strategy |
| 126 | + |
| 127 | +**Labels:** `performance`, `database`, `priority: medium` |
| 128 | + |
| 129 | +--- |
| 130 | + |
| 131 | +## Issue #41: N+1 Query Pattern in API |
| 132 | + |
| 133 | +### Title |
| 134 | +[PERFORMANCE]: Fix N+1 query pattern in list endpoints |
| 135 | + |
| 136 | +### Description |
| 137 | + |
| 138 | +**What:** List endpoints may trigger N+1 queries when serializing. |
| 139 | + |
| 140 | +**Why it matters:** |
| 141 | +- Database overload |
| 142 | +- Slow response times |
| 143 | +- Doesn't scale |
| 144 | + |
| 145 | +**Where:** `rest_api/main.py`, GET /destinations |
| 146 | + |
| 147 | +**Current behavior:** |
| 148 | +```python |
| 149 | +@app.route("/destinations", methods=["GET"]) |
| 150 | +def get_destinations(): |
| 151 | + destinations = Destination.query.all() # 1 query |
| 152 | + return jsonify([destination.to_dict() for destination in destinations]) |
| 153 | + # to_dict() might trigger additional queries! |
| 154 | +``` |
| 155 | + |
| 156 | +**Suggested approach:** |
| 157 | +```python |
| 158 | +@app.route("/destinations", methods=["GET"]) |
| 159 | +def get_destinations(): |
| 160 | + # Eager load all needed data |
| 161 | + destinations = Destination.query.all() |
| 162 | + |
| 163 | + # Use dictionary comprehension (faster than list comp) |
| 164 | + result = [ |
| 165 | + { |
| 166 | + "id": d.id, |
| 167 | + "destination": d.destination, |
| 168 | + "country": d.country, |
| 169 | + "rating": d.rating |
| 170 | + } |
| 171 | + for d in destinations |
| 172 | + ] |
| 173 | + return jsonify(result) |
| 174 | +``` |
| 175 | + |
| 176 | +For relationships: |
| 177 | +```python |
| 178 | +# Use joinedload for relationships |
| 179 | +from sqlalchemy.orm import joinedload |
| 180 | + |
| 181 | +destinations = Destination.query.options( |
| 182 | + joinedload(Destination.related_table) |
| 183 | +).all() |
| 184 | +``` |
| 185 | + |
| 186 | +**Acceptance criteria:** |
| 187 | +- [ ] Audit all list endpoints |
| 188 | +- [ ] Use eager loading for relationships |
| 189 | +- [ ] Minimize queries in serialization |
| 190 | +- [ ] Add query logging for debugging |
| 191 | +- [ ] Test with EXPLAIN ANALYZE |
| 192 | + |
| 193 | +**Labels:** `performance`, `database`, `priority: medium` |
| 194 | + |
| 195 | +--- |
| 196 | + |
| 197 | +## Issue #42: No Caching Strategy |
| 198 | + |
| 199 | +### Title |
| 200 | +[PERFORMANCE]: Implement caching for frequently accessed data |
| 201 | + |
| 202 | +### Description |
| 203 | + |
| 204 | +**What:** No caching layer for read-heavy operations. |
| 205 | + |
| 206 | +**Why it matters:** |
| 207 | +- Same data fetched repeatedly |
| 208 | +- Unnecessary database load |
| 209 | +- Slow response times |
| 210 | + |
| 211 | +**Where:** Both API applications |
| 212 | + |
| 213 | +**Suggested approach:** |
| 214 | + |
| 215 | +For FastAPI with Redis: |
| 216 | +```python |
| 217 | +from fastapi_cache import FastAPICache |
| 218 | +from fastapi_cache.backends.redis import RedisBackend |
| 219 | +from fastapi_cache.decorator import cache |
| 220 | + |
| 221 | +@app.get("/campaigns") |
| 222 | +@cache(expire=60) # Cache for 60 seconds |
| 223 | +async def list_campaigns(): |
| 224 | + return campaigns_db |
| 225 | +``` |
| 226 | + |
| 227 | +For Flask with Flask-Caching: |
| 228 | +```python |
| 229 | +from flask_caching import Cache |
| 230 | + |
| 231 | +cache = Cache(app, config={'CACHE_TYPE': 'redis'}) |
| 232 | + |
| 233 | +@app.route("/destinations") |
| 234 | +@cache.cached(timeout=60) |
| 235 | +def get_destinations(): |
| 236 | + destinations = Destination.query.all() |
| 237 | + return jsonify([d.to_dict() for d in destinations]) |
| 238 | +``` |
| 239 | + |
| 240 | +**Acceptance criteria:** |
| 241 | +- [ ] Add Redis dependency |
| 242 | +- [ ] Configure caching layer |
| 243 | +- [ ] Cache GET endpoints (60s default) |
| 244 | +- [ ] Invalidate cache on POST/PUT/DELETE |
| 245 | +- [ ] Add cache headers to responses |
| 246 | +- [ ] Document caching strategy |
| 247 | + |
| 248 | +**Labels:** `performance`, `enhancement`, `priority: low` |
| 249 | + |
| 250 | +--- |
| 251 | + |
| 252 | +## Issue #43: Inefficient File Reading |
| 253 | + |
| 254 | +### Title |
| 255 | +[PERFORMANCE]: Optimize file reading for large files |
| 256 | + |
| 257 | +### Description |
| 258 | + |
| 259 | +**What:** File handling examples read entire files into memory. |
| 260 | + |
| 261 | +**Why it matters:** |
| 262 | +- Memory exhaustion with large files |
| 263 | +- Slow for large datasets |
| 264 | +- Doesn't scale |
| 265 | + |
| 266 | +**Where:** `basics/07_file_handling/` |
| 267 | + |
| 268 | +**Current behavior:** |
| 269 | +```python |
| 270 | +with open("large_file.txt", "r") as f: |
| 271 | + data = f.read() # Loads entire file into memory! |
| 272 | +``` |
| 273 | + |
| 274 | +**Suggested approach:** |
| 275 | +```python |
| 276 | +# Line by line (memory efficient) |
| 277 | +with open("large_file.txt", "r") as f: |
| 278 | + for line in f: |
| 279 | + process(line) |
| 280 | + |
| 281 | +# Or chunked reading |
| 282 | +def read_in_chunks(file_path, chunk_size=8192): |
| 283 | + with open(file_path, "r") as f: |
| 284 | + while chunk := f.read(chunk_size): |
| 285 | + process(chunk) |
| 286 | +``` |
| 287 | + |
| 288 | +**Acceptance criteria:** |
| 289 | +- [ ] Update examples to use line-by-line reading |
| 290 | +- [ ] Add chunked reading example |
| 291 | +- [ ] Document memory implications |
| 292 | +- [ ] Add benchmark comparing approaches |
| 293 | +- [ ] Test with 1GB+ files |
| 294 | + |
| 295 | +**Labels:** `performance`, `code quality`, `priority: low` |
| 296 | + |
| 297 | +--- |
| 298 | + |
| 299 | +## Issue #44: No Connection Pooling |
| 300 | + |
| 301 | +### Title |
| 302 | +[PERFORMANCE]: Configure database connection pooling |
| 303 | + |
| 304 | +### Description |
| 305 | + |
| 306 | +**What:** Database connections created per-request without pooling. |
| 307 | + |
| 308 | +**Why it matters:** |
| 309 | +- Connection overhead on every request |
| 310 | +- Database resource exhaustion |
| 311 | +- Slow response times |
| 312 | + |
| 313 | +**Where:** `rest_api/main.py` |
| 314 | + |
| 315 | +**Current behavior:** |
| 316 | +```python |
| 317 | +app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///travel.db" |
| 318 | +db = SQLAlchemy(app) |
| 319 | +# Default pool settings may not be optimal |
| 320 | +``` |
| 321 | + |
| 322 | +**Suggested approach:** |
| 323 | +```python |
| 324 | +app.config["SQLALCHEMY_ENGINE_OPTIONS"] = { |
| 325 | + "pool_size": 10, # Number of connections to keep |
| 326 | + "pool_recycle": 3600, # Recycle connections after 1 hour |
| 327 | + "pool_pre_ping": True, # Verify connection before use |
| 328 | + "max_overflow": 20, # Allow 20 extra connections |
| 329 | +} |
| 330 | +``` |
| 331 | + |
| 332 | +**Acceptance criteria:** |
| 333 | +- [ ] Configure pool_size (10-20) |
| 334 | +- [ ] Set pool_recycle (3600s) |
| 335 | +- [ ] Enable pool_pre_ping |
| 336 | +- [ ] Monitor connection usage |
| 337 | +- [ ] Document pool tuning |
| 338 | + |
| 339 | +**Labels:** `performance`, `database`, `priority: low` |
| 340 | + |
| 341 | +--- |
0 commit comments