Benchmark de Performance des Bases de Données Graphe
Terminégoneo4jmemgraphtigergraphdocker

Benchmark de Performance des Bases de Données Graphe

Suite de tests mesurant la latence, la mémoire et le débit de requêtes entre moteurs relationnels et orientés graphe.

Problème et solution

Déterminer la solution de base de données optimale pour des graphes sociaux et des systèmes de recommandation.

Fonctionnalités

  • Tests automatisés de traversée sur plusieurs niveaux de profondeur
  • Comparaison des centiles de latence (p50, p95, p99)
  • Suivi détaillé de l'utilisation de la mémoire et du CPU
  • Environnements de test reproductibles avec Docker

Stack technique

TechnologieVersionObjectifPourquoi ce choix
Go1.25.1Primary languageStrong concurrency primitives, fast compilation, good driver ecosystem
neo4j-go-driver/v6v6.2.0Bolt protocol client for CognoDB, Neo4j, MemgraphOfficial driver, supports Bolt 5.x, parameterized queries
go-echarts/v2v2.7.2HTML chart generationGo-native, generates standalone HTML
godotenvv1.5.1Environment configurationStandard .env loading
net/httpstdlibREST client for TigerGraphNo external dependency needed

Architecture

Benchmark Engine

  • Adapter pattern with common GraphDatabase interface (16 methods)
  • 5 database adapters (4 active, FalkorDB disabled due to OOM)
  • Sequential workload executor with warm-up and measured iterations
  • Concurrent workload executor with N goroutines and sync.WaitGroup
  • Factory function for adapter creation from configuration

CLI Pipeline

  • 10 CLI entry points: benchmark, captest, charts, cleanup, dataset, load, runall, summary, verify, verify-load
  • JSONL append-only result output for crash resilience
  • CSV summary aggregation with percentile calculation
  • HTML chart generation via go-echarts
  • Pipeline automation via runall (verify → load → benchmark → report)

Structure du projet

cmd
benchmark
captest
charts
cleanup
dataset
load
runall
summary
verify
verify-load
internal
benchmark
concurrent.go
measurement.go
runner.go
safe.go
database
cognodb
memgraph
neo4j
tigergraph
types
factory.go
dataset
parser.go

Points d'API

Query Workloads (port 0)

MéthodeRouteObjectif
CypherMATCH (n:Person {id: $id}) RETURN n.id AS idPoint lookup — single node by ID
CypherMATCH (n:Person {region: $val}) RETURN n.id AS id LIMIT 10Indexed lookup — nodes by region
CypherMATCH (n:Person {id: $id})-[:FRIEND]->(m:Person) RETURN collect(m.id)1-hop traversal — direct friends
CypherMATCH (n:Person {id: $id})-[:FRIEND]->()-[:FRIEND]->(m:Person) RETURN collect(DISTINCT m.id)2-hop traversal — friends of friends
CypherMATCH (n:Person {id: $id})-[:FRIEND]->()-[:FRIEND]->()-[:FRIEND]->(m:Person) RETURN collect(DISTINCT m.id)3-hop traversal — extended network
CypherMATCH (n:Person) RETURN n.region AS region, count(n) AS cnt ORDER BY cnt DESC LIMIT 10Aggregation — region distribution
CypherCREATE (n:Person {id: $id, region: $region, ...})Write — create new node

Conception de la base de données

Person (node label)

ColonneTypeNotes
idPKSTRINGUnique constraint, indexed
regionSTRINGIndexed
genderSTRINGNot indexed
ageSTRINGIndexed (CognoDB, Neo4j, Memgraph only)
completion_percentageSTRINGNot indexed

49,683 nodes extracted from SNAP soc-Pokec dataset. All properties are strings.

FRIEND (relationship type)

ColonneTypeNotes
source_idSTRINGPerson.id (from)
target_idSTRINGPerson.id (to)

100,000 directed relationships. No properties on edges. Many-to-many cardinality.

Directed graph: Person --[FRIEND]--> Person. Many-to-many via graph relationships (no junction table). Relationships are first-class citizens in the graph model.

Décisions de conception clés

Single Bolt Driver for 3 Databases

Décision : Use neo4j-go-driver/v6 for CognoDB, Neo4j, and Memgraph

Pourquoi : All three support Bolt protocol; Cypher is logically equivalent

Avantages : Code reuse, consistent behavior across databases, adapter pattern isolates DB-specific behavior

Compromis : Cannot test database-specific optimizations; Memgraph has its own Go driver

MaxPoolSize = 1

Décision : Single connection per benchmark run despite constant defining 10

Pourquoi : Measure single-connection latency; avoid connection pool effects

Avantages : Clean latency measurement without pool contention

Compromis : Doesn't test connection pooling behavior

JSONL for Raw Results

Décision : One JSON object per line, append-only output

Pourquoi : Stream results without loading all into memory; easy to append across runs

Avantages : Resilient to crashes (partial results preserved)

Compromis : Requires full file scan for aggregation

No Retries on Measured Queries

Décision : Zero retries for benchmark queries to capture true latency

Pourquoi : Network blips and failures are part of real-world performance

Avantages : Honest performance measurement including failure modes

Compromis : Network blips cause false negatives

Goroutine-Per-Client Concurrency

Décision : Each concurrent client creates its own database connection

Pourquoi : Simulates real-world multi-client scenarios; avoids shared connection contention

Avantages : Realistic concurrency testing with independent connections

Compromis : Higher resource usage; connection limits become the bottleneck

MERGE for Idempotent Node Loading

Décision : Use MERGE (upsert) instead of CREATE for node loading

Pourquoi : Makes loading idempotent — running multiple times doesn't create duplicates

Avantages : Safe re-runs, consistent dataset state

Compromis : MERGE is slightly slower than CREATE due to existence checks

Tests

None (no _test.go files)•0 tests unitaires

Ce qui est testé

  • Capability test matrix (cmd/captest/) — tests all 8 workload types against all databases with small 10-node graph
  • Connection verification (cmd/verify/) — tests basic connectivity and parameterized queries
  • Data integrity verification (cmd/verify-load/) — verifies node/relationship counts (49,683 nodes, 100,000 relationships)

Couverture manquante

  • No unit tests for individual functions (_test.go files do not exist)
  • No tests for statistical functions (percentile, mean, stddev calculation)
  • No tests for CSV parsing edge cases (empty datasets, malformed CSVs)
  • No tests for concurrent behavior
  • No tests for chart generation correctness
  • No test framework installed (no testify, gomock)

Défis clés

  • Assurer des conditions d'évaluation équitables entre des modèles hétérogènes
  • Éviter les saturations mémoire lors de traversées profondes

Apprentissages clés

  • Compromis concrets entre moteurs de graphes natifs et RDBMS
  • Méthodologie d'analyse des centiles de latence sous forte charge