140 lines
4.7 KiB
Python
140 lines
4.7 KiB
Python
|
|
#!/usr/bin/env python3
|
||
|
|
"""Seed performance test data — creates N contacts in the test database.
|
||
|
|
|
||
|
|
Usage:
|
||
|
|
python scripts/seed_perf_data.py --count 200000
|
||
|
|
python scripts/seed_perf_data.py --count 5000 --tenant-id <uuid>
|
||
|
|
|
||
|
|
Requires DATABASE_URL environment variable or .env file.
|
||
|
|
"""
|
||
|
|
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
import argparse
|
||
|
|
import asyncio
|
||
|
|
import os
|
||
|
|
import sys
|
||
|
|
import uuid
|
||
|
|
from datetime import UTC, datetime
|
||
|
|
|
||
|
|
# Add project root to path
|
||
|
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||
|
|
|
||
|
|
from sqlalchemy import select
|
||
|
|
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
|
||
|
|
|
||
|
|
from app.config import get_settings
|
||
|
|
from app.models.contact import Contact
|
||
|
|
from app.models.tenant import Tenant
|
||
|
|
from app.models.user import User
|
||
|
|
|
||
|
|
|
||
|
|
async def seed_contacts(count: int, tenant_id_str: str | None = None) -> None:
|
||
|
|
"""Seed `count` contacts into the database for performance testing."""
|
||
|
|
settings = get_settings()
|
||
|
|
engine = create_async_engine(
|
||
|
|
settings.database_url,
|
||
|
|
pool_size=settings.db_pool_size,
|
||
|
|
max_overflow=settings.db_max_overflow,
|
||
|
|
)
|
||
|
|
session_factory = async_sessionmaker(bind=engine, expire_on_commit=False)
|
||
|
|
|
||
|
|
async with session_factory() as session:
|
||
|
|
# Get or create a tenant
|
||
|
|
if tenant_id_str:
|
||
|
|
tenant_id = uuid.UUID(tenant_id_str)
|
||
|
|
result = await session.execute(select(Tenant).where(Tenant.id == tenant_id))
|
||
|
|
tenant = result.scalar_one_or_none()
|
||
|
|
if not tenant:
|
||
|
|
print(f"ERROR: Tenant {tenant_id_str} not found")
|
||
|
|
return
|
||
|
|
else:
|
||
|
|
# Get first tenant or create one
|
||
|
|
result = await session.execute(select(Tenant).limit(1))
|
||
|
|
tenant = result.scalar_one_or_none()
|
||
|
|
if not tenant:
|
||
|
|
tenant = Tenant(name="Perf Test Tenant", slug="perf-test")
|
||
|
|
session.add(tenant)
|
||
|
|
await session.flush()
|
||
|
|
|
||
|
|
# Get or create a user for this tenant
|
||
|
|
result = await session.execute(
|
||
|
|
select(User).where(User.tenant_id == tenant.id).limit(1)
|
||
|
|
)
|
||
|
|
user = result.scalar_one_or_none()
|
||
|
|
if not user:
|
||
|
|
from app.core.auth import hash_password
|
||
|
|
|
||
|
|
user = User(
|
||
|
|
tenant_id=tenant.id,
|
||
|
|
email="perf@test.local",
|
||
|
|
name="Perf Test User",
|
||
|
|
password_hash=hash_password("TestPass123!"),
|
||
|
|
role="admin",
|
||
|
|
is_active=True,
|
||
|
|
preferences={},
|
||
|
|
)
|
||
|
|
session.add(user)
|
||
|
|
await session.flush()
|
||
|
|
|
||
|
|
tenant_id = tenant.id
|
||
|
|
user_id = user.id
|
||
|
|
|
||
|
|
# Seed contacts in batches
|
||
|
|
batch_size = 1000
|
||
|
|
total_seeded = 0
|
||
|
|
first_names = ["Hans", "Maria", "Peter", "Anna", "Klaus", "Ursula", "Werner", "Brigitte", "Jürgen", "Helga"]
|
||
|
|
last_names = ["Müller", "Schmidt", "Schneider", "Fischer", "Weber", "Meyer", "Wagner", "Becker", "Hoffmann", "Schäfer"]
|
||
|
|
|
||
|
|
while total_seeded < count:
|
||
|
|
batch = []
|
||
|
|
batch_count = min(batch_size, count - total_seeded)
|
||
|
|
for i in range(batch_count):
|
||
|
|
idx = total_seeded + i
|
||
|
|
fn = first_names[idx % len(first_names)]
|
||
|
|
ln = last_names[idx % len(last_names)]
|
||
|
|
batch.append(Contact(
|
||
|
|
tenant_id=tenant_id,
|
||
|
|
first_name=f"{fn}-{idx}",
|
||
|
|
last_name=f"{ln}-{idx}",
|
||
|
|
email=f"{fn.lower()}.{ln.lower()}-{idx}@example.com",
|
||
|
|
phone=f"+49-555-{idx:06d}",
|
||
|
|
created_by=user_id,
|
||
|
|
updated_by=user_id,
|
||
|
|
))
|
||
|
|
session.add_all(batch)
|
||
|
|
await session.commit()
|
||
|
|
total_seeded += batch_count
|
||
|
|
if total_seeded % 10000 == 0 or total_seeded == count:
|
||
|
|
print(f"Seeded {total_seeded}/{count} contacts...")
|
||
|
|
|
||
|
|
await engine.dispose()
|
||
|
|
print(f"Done: {total_seeded} contacts seeded.")
|
||
|
|
|
||
|
|
|
||
|
|
def main():
|
||
|
|
"""Parse arguments and run the seeding script."""
|
||
|
|
parser = argparse.ArgumentParser(
|
||
|
|
description="Seed performance test data into the LeoCRM database."
|
||
|
|
)
|
||
|
|
parser.add_argument(
|
||
|
|
"--count",
|
||
|
|
type=int,
|
||
|
|
default=200000,
|
||
|
|
help="Number of contacts to create (default: 200000)",
|
||
|
|
)
|
||
|
|
parser.add_argument(
|
||
|
|
"--tenant-id",
|
||
|
|
type=str,
|
||
|
|
default=None,
|
||
|
|
help="Tenant UUID to seed data into (default: first available tenant)",
|
||
|
|
)
|
||
|
|
args = parser.parse_args()
|
||
|
|
|
||
|
|
print(f"Starting seed: {args.count} contacts")
|
||
|
|
asyncio.run(seed_contacts(args.count, args.tenant_id))
|
||
|
|
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
main()
|