c889a57b6b
Test Suites / Build CI Environment (push) Has been cancelled
Test Suites / Basic Tests (push) Has been cancelled
Test Suites / End-to-End Tests (push) Has been cancelled
Test Suites / CLI Tests (push) Has been cancelled
Test Suites / Slow End-to-End Tests (push) Has been cancelled
Test Suites / Graph Database Tests (push) Has been cancelled
Test Suites / Vector DB Tests (push) Has been cancelled
Test Suites / Temporal Graph Test (push) Has been cancelled
Test Suites / Search Test on Different DBs (push) Has been cancelled
Test Suites / Example Tests (push) Has been cancelled
Test Suites / Notebook Tests (push) Has been cancelled
Test Suites / OS and Python Tests Ubuntu (push) Has been cancelled
Test Suites / OS and Python Tests Extended (push) Has been cancelled
Test Suites / LLM Test Suite (push) Has been cancelled
Test Suites / S3 File Storage Test (push) Has been cancelled
Test Suites / Run Integration Tests (push) Has been cancelled
Test Suites / MCP Tests (push) Has been cancelled
Test Suites / Docker Compose Test (push) Has been cancelled
Test Suites / Docker CI test (push) Has been cancelled
Test Suites / Relational DB Migration Tests (push) Has been cancelled
Test Suites / Distributed Cognee Test (push) Has been cancelled
Test Suites / DB Examples Tests (push) Has been cancelled
Test Suites / Test Completion Status (push) Has been cancelled
Test Suites / Claude Code Review (push) Has been cancelled
Test Suites / basic checks (push) Has been cancelled
build | Build and Push Cognee MCP Docker Image to dockerhub / docker-build-and-push (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
build | Build and Push Docker Image to dockerhub / docker-build-and-push (push) Has been cancelled
Weighted Edges Tests / Test Weighted Edges Core Functionality (3.11) (push) Has been cancelled
Weighted Edges Tests / Test Weighted Edges Core Functionality (3.12) (push) Has been cancelled
Weighted Edges Tests / Test Weighted Edges with Different Graph Databases (kuzu, kuzu) (push) Has been cancelled
Weighted Edges Tests / Test Weighted Edges with Different Graph Databases (neo4j, neo4j) (push) Has been cancelled
Weighted Edges Tests / Test Weighted Edges Examples (push) Has been cancelled
Weighted Edges Tests / Code Quality for Weighted Edges (push) Has been cancelled
237 lines
7.7 KiB
Python
237 lines
7.7 KiB
Python
"""
|
|
Batch Add + Cognify Performance Test
|
|
|
|
Starts the Cognee server, creates an API key, adds 200 files to a dataset,
|
|
calls cognify, and logs timing for every operation.
|
|
|
|
Usage:
|
|
python -m cognee.tests.performance.batch_add_cognify_test
|
|
"""
|
|
|
|
import io
|
|
import os
|
|
import random
|
|
import signal
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
import time
|
|
import uuid
|
|
import urllib.error
|
|
import urllib.request
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
|
|
import requests
|
|
|
|
TOPICS = [
|
|
"quantum computing",
|
|
"machine learning",
|
|
"climate change",
|
|
"renewable energy",
|
|
"space exploration",
|
|
"genetic engineering",
|
|
"blockchain technology",
|
|
"artificial intelligence",
|
|
"ocean conservation",
|
|
"urban planning",
|
|
"medieval history",
|
|
"philosophy of mind",
|
|
"distributed systems",
|
|
"neuroscience",
|
|
"economic theory",
|
|
]
|
|
|
|
SUBTOPICS = [
|
|
"data analysis",
|
|
"pattern recognition",
|
|
"resource allocation",
|
|
"risk assessment",
|
|
"optimization algorithms",
|
|
"predictive modeling",
|
|
"system integration",
|
|
"scalability",
|
|
"error correction",
|
|
"signal processing",
|
|
"network topology",
|
|
"feedback loops",
|
|
"energy efficiency",
|
|
"material science",
|
|
"behavioral adaptation",
|
|
"information theory",
|
|
]
|
|
|
|
SENTENCE_TEMPLATES = [
|
|
"The field of {topic} has seen remarkable advances in recent years, particularly in the area of {subtopic}.",
|
|
"Researchers studying {topic} have discovered that {subtopic} plays a crucial role in understanding the broader implications.",
|
|
"A comprehensive review of {topic} literature reveals that {subtopic} remains one of the most debated aspects.",
|
|
"Recent experiments in {topic} demonstrate a strong correlation between {subtopic} and observed outcomes.",
|
|
"The intersection of {topic} and {subtopic} opens new possibilities for practical applications.",
|
|
"Experts in {topic} argue that {subtopic} will be the defining challenge of the next decade.",
|
|
"Historical analysis shows that {topic} has always been influenced by developments in {subtopic}.",
|
|
"New computational models for {topic} suggest that {subtopic} can be optimized through iterative approaches.",
|
|
"The economic impact of {topic} is closely tied to advancements in {subtopic}, according to recent studies.",
|
|
"Collaborative efforts in {topic} have led to breakthroughs in {subtopic} that were previously thought impossible.",
|
|
"Understanding {topic} requires a deep appreciation of how {subtopic} interacts with existing frameworks.",
|
|
"Policy makers are increasingly turning to {topic} research to inform decisions about {subtopic}.",
|
|
]
|
|
|
|
|
|
def generate_paragraph(topic: str, num_sentences: int = 5) -> str:
|
|
sentences = []
|
|
for _ in range(num_sentences):
|
|
template = random.choice(SENTENCE_TEMPLATES)
|
|
subtopic = random.choice(SUBTOPICS)
|
|
sentences.append(template.format(topic=topic, subtopic=subtopic))
|
|
return " ".join(sentences)
|
|
|
|
|
|
def generate_document(num_paragraphs: int = 3) -> tuple:
|
|
topic = random.choice(TOPICS)
|
|
paragraphs = [
|
|
generate_paragraph(topic, num_sentences=random.randint(50, 100))
|
|
for _ in range(num_paragraphs)
|
|
]
|
|
paragraphs.append(str(uuid.uuid4()))
|
|
return "\n\n".join(paragraphs), topic
|
|
|
|
|
|
NUM_FILES = 200
|
|
DATASET_NAME = f"batch_test_{uuid.uuid4().hex[:8]}"
|
|
PROJECT_ROOT = Path(__file__).resolve().parent.parent.parent.parent
|
|
VENV_PYTHON = str(PROJECT_ROOT / ".venv" / "bin" / "python")
|
|
|
|
|
|
def wait_for_server(url: str, timeout: float = 240.0) -> None:
|
|
deadline = time.time() + timeout
|
|
while time.time() < deadline:
|
|
try:
|
|
with urllib.request.urlopen(url, timeout=2) as resp:
|
|
if resp.status == 200:
|
|
return
|
|
except (urllib.error.URLError, ConnectionError, TimeoutError):
|
|
pass
|
|
time.sleep(0.5)
|
|
raise SystemExit(f"Server at {url} did not become ready in {timeout}s")
|
|
|
|
|
|
def log(msg: str) -> None:
|
|
ts = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]
|
|
print(f"[{ts}] {msg}", flush=True)
|
|
|
|
|
|
def add_files_batch(base_url: str, api_key: str, count: int) -> float:
|
|
log(f" Generating {count} documents...")
|
|
files = []
|
|
for i in range(1, count + 1):
|
|
text, _ = generate_document(num_paragraphs=random.randint(2, 5))
|
|
files.append(
|
|
("data", (f"document_{i}.txt", io.BytesIO(text.encode("utf-8")), "text/plain"))
|
|
)
|
|
log(f" Uploading {count} files in a single request...")
|
|
|
|
start = time.time()
|
|
resp = requests.post(
|
|
f"{base_url}/api/v1/add",
|
|
data={"datasetName": DATASET_NAME},
|
|
files=files,
|
|
headers={"X-Api-Key": api_key},
|
|
timeout=1800,
|
|
)
|
|
elapsed = time.time() - start
|
|
|
|
if resp.status_code != 200:
|
|
log(f" ERROR: {resp.status_code} - {resp.text[:300]}")
|
|
return elapsed
|
|
|
|
|
|
def cognify(base_url: str, api_key: str) -> float:
|
|
headers = {"X-Api-Key": api_key, "Content-Type": "application/json"}
|
|
payload = {"datasets": [DATASET_NAME], "data_per_batch": NUM_FILES, "runInBackground": False}
|
|
|
|
start = time.time()
|
|
resp = requests.post(
|
|
f"{base_url}/api/v1/cognify",
|
|
json=payload,
|
|
headers=headers,
|
|
timeout=36000,
|
|
)
|
|
elapsed = time.time() - start
|
|
|
|
if resp.status_code != 200:
|
|
log(f" Cognify ERROR: {resp.status_code} - {resp.text[:300]}")
|
|
return elapsed
|
|
|
|
|
|
def main() -> None:
|
|
host = os.environ.get("HTTP_API_HOST", "localhost")
|
|
port = os.environ.get("HTTP_API_PORT", "8000")
|
|
base_url = f"http://{host}:{port}"
|
|
|
|
perf_dir = str(Path(__file__).resolve().parent)
|
|
key_path = tempfile.NamedTemporaryFile(suffix=".key", delete=False).name
|
|
|
|
log("=== Bootstrapping: pruning data, creating user & API key ===")
|
|
bootstrap_start = time.time()
|
|
try:
|
|
subprocess.run(
|
|
[VENV_PYTHON, "-m", "utils.bootstrap_script", key_path],
|
|
check=True,
|
|
cwd=perf_dir,
|
|
)
|
|
api_key = Path(key_path).read_text().strip()
|
|
finally:
|
|
try:
|
|
os.unlink(key_path)
|
|
except FileNotFoundError:
|
|
pass
|
|
log(f"Bootstrap done in {time.time() - bootstrap_start:.1f}s")
|
|
|
|
log(f"Starting Cognee server on {base_url}")
|
|
server_proc = subprocess.Popen(
|
|
[VENV_PYTHON, "-m", "uvicorn", "cognee.api.client:app", "--host", host, "--port", port],
|
|
stdout=subprocess.DEVNULL,
|
|
stderr=subprocess.DEVNULL,
|
|
start_new_session=True,
|
|
)
|
|
|
|
try:
|
|
wait_for_server(f"{base_url}/health")
|
|
log("Server is ready")
|
|
|
|
log(f"=== Adding {NUM_FILES} files to dataset '{DATASET_NAME}' (single request) ===")
|
|
total_add_time = add_files_batch(base_url, api_key, NUM_FILES)
|
|
log("=== Add phase complete ===")
|
|
log(f" Total: {total_add_time:.1f}s")
|
|
log(f" Average: {total_add_time / NUM_FILES:.3f}s per file")
|
|
|
|
log(f"=== Running cognify on dataset '{DATASET_NAME}' ===")
|
|
cognify_time = cognify(base_url, api_key)
|
|
log("=== Cognify complete ===")
|
|
log(f" Time: {cognify_time:.1f}s")
|
|
|
|
log("=== Summary ===")
|
|
log(f" Files added: {NUM_FILES}")
|
|
log(
|
|
f" Add total time: {total_add_time:.1f}s ({total_add_time / NUM_FILES:.3f}s avg per file)"
|
|
)
|
|
log(f" Cognify time: {cognify_time:.1f}s")
|
|
log(f" Total time: {total_add_time + cognify_time:.1f}s")
|
|
|
|
finally:
|
|
log("Shutting down server")
|
|
try:
|
|
os.killpg(server_proc.pid, signal.SIGTERM)
|
|
server_proc.wait(timeout=10)
|
|
except ProcessLookupError:
|
|
pass
|
|
except subprocess.TimeoutExpired:
|
|
try:
|
|
os.killpg(server_proc.pid, signal.SIGKILL)
|
|
except ProcessLookupError:
|
|
pass
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|