1 changed files with 171 additions and 0 deletions
-
171main.py
@ -0,0 +1,171 @@ |
|||||
|
#!/usr/bin/env python3 |
||||
|
""" |
||||
|
Redis vs PostgreSQL Read Benchmark - Fair Comparison |
||||
|
""" |
||||
|
import os |
||||
|
import sys |
||||
|
import time |
||||
|
import random |
||||
|
import logging |
||||
|
import csv |
||||
|
import redis |
||||
|
import psycopg2 |
||||
|
from locust import User, task, events |
||||
|
|
||||
|
# ─── Logging ─── |
||||
|
logging.basicConfig( |
||||
|
level=logging.INFO, |
||||
|
format='%(asctime)s - %(message)s', |
||||
|
handlers=[ |
||||
|
logging.FileHandler('debug.log'), |
||||
|
logging.StreamHandler(sys.stdout) |
||||
|
] |
||||
|
) |
||||
|
logger = logging.getLogger() |
||||
|
|
||||
|
# ─── Config ─── |
||||
|
TOTAL_RECORDS = 125_000_000 |
||||
|
PHONE_MIN = 100_000_000 |
||||
|
PHONE_MAX = 224_999_999 |
||||
|
|
||||
|
REDIS_CONFIG = { |
||||
|
"host": "localhost", |
||||
|
"port": 6379, |
||||
|
"db": 0, |
||||
|
"decode_responses": False, |
||||
|
"socket_connect_timeout": 5, |
||||
|
"socket_timeout": 10, |
||||
|
} |
||||
|
|
||||
|
POSTGRES_CONFIG = { |
||||
|
"host": "localhost", |
||||
|
"port": 5432, |
||||
|
"database": "numbers_db", |
||||
|
"user": "myuser", |
||||
|
"password": os.environ.get("POSTGRES_PASSWORD", "mypassword"), |
||||
|
} |
||||
|
|
||||
|
|
||||
|
# ───────────────────────────────────────────── |
||||
|
# Redis Benchmark - با GET |
||||
|
# ───────────────────────────────────────────── |
||||
|
class RedisReadUser(User): |
||||
|
_pool = None |
||||
|
|
||||
|
def __init__(self, *args, **kwargs): |
||||
|
super().__init__(*args, **kwargs) |
||||
|
if RedisReadUser._pool is None: |
||||
|
RedisReadUser._pool = redis.ConnectionPool(**REDIS_CONFIG) |
||||
|
# هر User یک client اختصاصی از pool میگیره |
||||
|
self.client = redis.Redis(connection_pool=RedisReadUser._pool) |
||||
|
|
||||
|
@task |
||||
|
def get_random(self): |
||||
|
phone_number = random.randint(PHONE_MIN, PHONE_MAX) |
||||
|
start = time.perf_counter() |
||||
|
try: |
||||
|
four_digit_code = self.client.get(phone_number) |
||||
|
elapsed = (time.perf_counter() - start) * 1000 |
||||
|
if four_digit_code: |
||||
|
print(f"Redis | Phone: {phone_number} | Code: {four_digit_code} | Time: {elapsed:.3f}ms") |
||||
|
else: |
||||
|
print(f"Redis | Phone: {phone_number} | Code: NOT FOUND | Time: {elapsed:.3f}ms") |
||||
|
self.environment.events.request.fire( |
||||
|
request_type="GET", |
||||
|
name="redis_get", |
||||
|
response_time=elapsed, |
||||
|
response_length=len(four_digit_code) if four_digit_code else 0, |
||||
|
exception=None, |
||||
|
context={}, |
||||
|
) |
||||
|
except Exception as e: |
||||
|
elapsed = (time.perf_counter() - start) * 1000 |
||||
|
print(f"❌ Redis ERROR: {e} | Phone: {phone_number} | Time: {elapsed:.3f}ms") |
||||
|
self.environment.events.request.fire( |
||||
|
request_type="GET", |
||||
|
name="redis_get", |
||||
|
response_time=elapsed, |
||||
|
response_length=0, |
||||
|
exception=e, |
||||
|
context={}, |
||||
|
) |
||||
|
|
||||
|
|
||||
|
# ───────────────────────────────────────────── |
||||
|
# PostgreSQL Benchmark - کانکشن اختصاصی برای هر User |
||||
|
# ───────────────────────────────────────────── |
||||
|
class PostgresReadUser(User): |
||||
|
def __init__(self, *args, **kwargs): |
||||
|
super().__init__(*args, **kwargs) |
||||
|
# ✅ هر User instance یک کانکشن اختصاصی دارد (مثل Redis) |
||||
|
self._connection = psycopg2.connect( |
||||
|
host=POSTGRES_CONFIG['host'], |
||||
|
port=POSTGRES_CONFIG['port'], |
||||
|
database=POSTGRES_CONFIG['database'], |
||||
|
user=POSTGRES_CONFIG['user'], |
||||
|
password=POSTGRES_CONFIG['password'] |
||||
|
) |
||||
|
# تنظیمات برای عملکرد بهتر |
||||
|
self._connection.set_session(autocommit=True) |
||||
|
|
||||
|
def on_stop(self): |
||||
|
"""آزادسازی کانکشن هنگام توقف User""" |
||||
|
if self._connection is not None and not self._connection.closed: |
||||
|
self._connection.close() |
||||
|
self._connection = None |
||||
|
|
||||
|
@task |
||||
|
def get_random(self): |
||||
|
phone_number = random.randint(PHONE_MIN, PHONE_MAX) |
||||
|
start = time.perf_counter() |
||||
|
try: |
||||
|
with self._connection.cursor() as cur: |
||||
|
cur.execute( |
||||
|
"SELECT random_code FROM phone_numbers WHERE phone_number = %s", |
||||
|
(phone_number,) |
||||
|
) |
||||
|
result = cur.fetchone() |
||||
|
elapsed = (time.perf_counter() - start) * 1000 |
||||
|
|
||||
|
if result: |
||||
|
random_code = result[0] |
||||
|
print(f"Postgres | Phone: {phone_number} | Code: {random_code} | Time: {elapsed:.3f}ms") |
||||
|
else: |
||||
|
print(f"Postgres | Phone: {phone_number} | Code: NOT FOUND | Time: {elapsed:.3f}ms") |
||||
|
|
||||
|
self.environment.events.request.fire( |
||||
|
request_type="GET", |
||||
|
name="postgres_select", |
||||
|
response_time=elapsed, |
||||
|
response_length=4, |
||||
|
exception=None, |
||||
|
context={}, |
||||
|
) |
||||
|
except Exception as e: |
||||
|
elapsed = (time.perf_counter() - start) * 1000 |
||||
|
print(f"❌ Postgres ERROR: {e} | Phone: {phone_number} | Time: {elapsed:.3f}ms") |
||||
|
self.environment.events.request.fire( |
||||
|
request_type="GET", |
||||
|
name="postgres_select", |
||||
|
response_time=elapsed, |
||||
|
response_length=0, |
||||
|
exception=e, |
||||
|
context={}, |
||||
|
) |
||||
|
|
||||
|
|
||||
|
# ───────────────────────────────────────────── |
||||
|
# اجرا |
||||
|
# ───────────────────────────────────────────── |
||||
|
if __name__ == "__main__": |
||||
|
print(f""" |
||||
|
╔══════════════════════════════════════════════════════════════╗ |
||||
|
║ Redis vs PostgreSQL READ Benchmark (125M) ║ |
||||
|
╠══════════════════════════════════════════════════════════════╣ |
||||
|
║ ║ |
||||
|
║ Total Records: {TOTAL_RECORDS:,} ║ |
||||
|
║ ║ |
||||
|
║ ║ |
||||
|
║ ⚠️ Response Time = فقط زمان Query (بدون Connection) ║ |
||||
|
╚══════════════════════════════════════════════════════════════╝ |
||||
|
""") |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue