Skip to content

dreamware-nz/loveliness

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

151 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Loveliness

CI Go License: MIT Docker

A clustered graph database built on LadybugDB — like Elasticsearch is to Lucene, Loveliness is to LadybugDB.

A "loveliness" is the collective noun for a group of ladybugs.

LadybugDB is a Kuzu fork — a fast, embedded, columnar graph engine with Cypher support. Loveliness wraps it in a distributed layer: hash-based sharding, Raft consensus, Bloom filter routing, edge-cut replication, and Neo4j Bolt protocol compatibility. Single Go binary, no external dependencies.

Architecture

graph TB
    HTTP[HTTP :8080] --> Router
    Bolt[Bolt :7687] --> Router

    Router[Query Router<br/>Bloom filter → route/scatter]

    Router --> S0[Shard 0]
    Router --> S1[Shard 1]
    Router --> SN[Shard N]
    Router -- remote --> TCP[TCP+MsgPack<br/>to peer nodes]

    S0 --- WAL[WAL + Backup + Ingest Queue]
Loading

Performance

Local (15.7M nodes, 10M edges, 4 shards — Apple M1 Pro)

Query Type P50 QPS
Point lookup 425us 10,758
1-hop traversal 673us 1,106
Single write 365us 2,526
Aggregation 57ms 16
2-hop traversal 162ms 5
Bulk load 70–190K nodes/sec

Cluster (50M nodes, 50M edges, 12 shards — 4-node Fly.io)

Tested on 4× performance-4x machines (8 vCPU, 16GB RAM each), sjc region. 200 iterations, 16 concurrent workers. Benchmark binary runs inside the cluster on localhost for true in-network latencies.

Query Type P50 P95 QPS
Point lookup 2.2ms 3.2ms 427
Point lookup (16 workers) 5.2ms 12.2ms 2,642
Range filter 3.2ms 3.7ms 290
Count all nodes 8.4ms 11.0ms 112
Count filtered 284ms 310ms 3
Aggregation (avg) 295ms 321ms 3
Group-by aggregation 769ms 814ms 1
1-hop traversal 2.9ms 380ms 17
2-hop traversal 241ms 268ms 4
Variable-length path (1..3) 2.3ms 3.3ms 414
Friend-of-friend count 476ms 494ms 2
Mutual friends 2.4ms 3.0ms 394
Shortest path (1..6) 29ms 40ms 33
Single write 1.8ms 2.2ms 542
Merge upsert 2.4ms 3.0ms 413
Read-after-write 3.3ms 4.0ms 305
Bulk node load 197K/sec
Bulk edge load 373K/sec

Scale progression

Dataset Nodes Shards Point lookup P50 Concurrent QPS Group-by P50 Write P50
10M / 10M 3 3 1.4ms 1,792
20M / 20M 3 3 863us 1,991 838ms
23M / 23M 3 6 1.2ms 6,831 246ms 1.8ms
50M / 50M 4 12 2.2ms 2,642 769ms 1.8ms

Point lookups stay sub-3ms through 50M nodes. Writes hold steady at 1.8ms regardless of dataset size. Bulk loading in-cluster hits 197K nodes/sec and 373K edges/sec.

Reproduce Benchmarks

# Full comparison: Loveliness (1-node, 3-node) vs Neo4j CE
./bench/run.sh

# Quick check: single-node Loveliness only
./bench/run.sh --quick

# Custom dataset size
./bench/run.sh --nodes=500000 --edges=500000

# 50M-scale on Fly.io (4 nodes, 12 shards)
./bench/run-50m.sh

Results land in bench/results/<timestamp>/ with JSON data, SVG charts, and a markdown comparison report. CI runs the full comparison on each release and opens a PR with updated results.

Full benchmarks and comparisons with Neo4j, Memgraph, TigerGraph, Neptune, and JanusGraph: docs/benchmarks.md

Neo4j Driver Compatibility

Speaks Bolt v4.x on :7687. Use any Neo4j driver — just change the URL. Both bolt:// (direct) and neo4j:// (routing with automatic failover) are supported.

from neo4j import GraphDatabase

# Direct connection
driver = GraphDatabase.driver("bolt://localhost:7687")

# Routing + automatic failover (recommended for clusters)
driver = GraphDatabase.driver("neo4j://localhost:7687")

with driver.session() as session:
    result = session.run("MATCH (p:Person {name: $name}) RETURN p.name, p.age", name="Alice")

72/72 exhaustive tests pass with the official Python driver. Details: docs/bolt.md

Quick Start

Fastest way — one command, real 3-node Raft cluster on your laptop:

loveliness up 3

That's it. Three nodes, auto-configured ports, auto-bootstrap. Connect at bolt://localhost:7687 or http://localhost:8080.

From source:

git clone https://github.com/dreamware-nz/loveliness.git && cd loveliness

make build      # requires LadybugDB: curl -fsSL https://install.ladybugdb.com | sh
make run        # single node → :8080 (HTTP), :7687 (Bolt)
make docker     # 3-node cluster via Docker Compose
make test       # 260 tests across 12 packages

Deploy to Fly.io — zero-config cloud cluster in under 5 minutes:

cd deploy/fly
fly launch
fly scale count 3

DNS auto-discovery handles peer finding. No manual peer configuration needed. See Fly.io deployment docs for details.

Usage

CLI:

loveliness help                                           # show all commands
loveliness up 3                                           # 3-node local cluster
loveliness query "MATCH (p:Person {name: 'Alice'}) RETURN p"  # query a running server
loveliness version                                        # show version

Set LOVELINESS_URL to query a remote server (default: http://localhost:8080).

HTTP API:

# Schema (broadcast to all shards)
curl -s localhost:8080/cypher -d "CREATE NODE TABLE Person(name STRING, age INT64, PRIMARY KEY(name))"

# Write (routed to owning shard)
curl -s localhost:8080/cypher -d "CREATE (p:Person {name: 'Alice', age: 30})"

# Read (Bloom filter → single shard)
curl -s localhost:8080/cypher -d "MATCH (p:Person {name: 'Alice'}) RETURN p"

# Bulk load
curl -s localhost:8080/bulk/nodes -H "X-Table: Person" --data-binary @persons.csv

# Async ingest (returns 202 with job ID)
curl -s -X POST localhost:8080/ingest/nodes -H "X-Table: Person" --data-binary @persons.csv

Full API reference: docs/api.md

Docker

# Single image
docker run -p 8080:8080 -p 7687:7687 dreamwarenz/loveliness

# 3-node cluster
docker compose up

MCP (LLM agents)

Loveliness ships a Model Context Protocol server so any MCP-aware LLM client (Claude Code, Claude Desktop, Cursor, Zed) can query and write through typed, schema-aware tools.

# one-shot: build the binary, register it with Claude, install the skill
make install-mcp

# or, manually:
claude mcp add loveliness -e LOVELINESS_URL=http://localhost:8080 -- loveliness-mcp

make install-mcp runs scripts/install-mcp.sh, which builds loveliness-mcp, runs claude mcp add (or prints the manual JSON if claude isn't on PATH), and links the bundled skill at skills/loveliness/SKILL.md into ~/.claude/skills/loveliness/ so agents get prose guidance on how to use the tools.

Full install snippets for Claude Desktop / Zed, tool reference, readonly and auth patterns: docs/mcp.md.

Kubernetes

kubectl apply -f deploy/k8s/namespace.yml
kubectl apply -f deploy/k8s/service.yml
kubectl apply -f deploy/k8s/statefulset.yml

StatefulSet with headless service, persistent volumes, health probes. Details: docs/kubernetes.md

High Availability

What happens when a node goes down?

Loveliness uses Raft consensus with tight timeouts (1s heartbeat, 1s election). When a node fails:

  1. Leader failure: a new leader is elected within ~1-2 seconds. Writes fail during the election window, then resume on the new leader.
  2. Follower failure: reads and writes continue on remaining nodes. Shards that had replicas on the failed node become degraded but remain queryable from their primary.
  3. Connected node failure: your client gets a connection error. Reconnect to any other node in the cluster to continue.

Every node can serve both reads and writes. Reads execute locally via scatter-gather (the node forwards sub-queries to peer nodes holding remote shards). Writes to a non-leader node return a NOT_LEADER error that includes the current leader's address, so your client knows where to retry.

Client-side HA setup

Put a load balancer (HAProxy, Nginx, cloud LB) in front of all nodes and use the health endpoint for routing:

GET /health → {"status": "ok", "role": "leader", "shards": {...}}
Setup How
Load balancer (recommended) Point your client at the LB. Health check: GET /health returns 200 when the node is healthy. Works for both HTTP :8080 and Bolt :7687.
Client-side retry Connect to any node. On connection failure, round-robin through the other nodes. All nodes accept all queries.
Kubernetes The headless service (deploy/k8s/service.yml) already provides DNS-based discovery. The LoadBalancer service handles external traffic.

Example HAProxy health check:

backend loveliness
    option httpchk GET /health
    http-check expect status 200
    server node1 10.0.0.1:8080 check
    server node2 10.0.0.2:8080 check
    server node3 10.0.0.3:8080 check

For Bolt connections, you have two options:

Scheme Behavior
neo4j://lb:7687 Driver sends a ROUTE message, discovers all cluster nodes, and handles failover automatically. Recommended for HA.
bolt://lb:7687 Direct connection to a single node via the load balancer. The LB handles failover.

The neo4j:// scheme works because Loveliness responds to ROUTE with all alive cluster members — the leader as the WRITE server, all nodes as READ and ROUTE servers. The driver automatically reconnects to another node if the current one goes down. TTL is 30 seconds, so topology changes propagate quickly.

from neo4j import GraphDatabase

# Automatic failover via driver routing (recommended)
driver = GraphDatabase.driver("neo4j://any-node:7687")

# Or via load balancer
driver = GraphDatabase.driver("neo4j://lb:7687")

Security

Authentication

One env var enables token auth across HTTP and Bolt:

export LOVELINESS_AUTH_TOKEN=my-secret
  • HTTP: all endpoints except /health require Authorization: Bearer <token>
  • Bolt: pass the token as the password in your driver auth (auth=("neo4j", "my-secret"))
  • Disabled by default: empty token = open access (dev mode)

Details: docs/configuration.md

TLS

All transports support TLS. Three env vars enable it:

export LOVELINESS_TLS_CERT=/path/to/server.crt
export LOVELINESS_TLS_KEY=/path/to/server.key
export LOVELINESS_TLS_CA=/path/to/ca.crt   # enables mTLS for inter-node traffic
export LOVELINESS_TLS_MODE=required
Boundary What's encrypted TLS type
Client → Node HTTP :8080, Bolt :7687 Server TLS
Node → Node TCP transport :9001, Raft mTLS (cluster CA)

Without these vars, all listeners run plaintext (dev default). Details: docs/configuration.md

Documentation

Doc Contents
Architecture System design, query/write lifecycle, optimization phases, edge-cut replication, CGo safety
Benchmarks Performance numbers, comparisons, transport benchmarks
Bolt Protocol Neo4j driver compatibility, examples, test results
API Reference HTTP endpoints, bulk loading, ingest queue, DR, consistency
Configuration Environment variables, TLS, DNS discovery, shard count guidance
Fly.io Deploy One-command cloud deployment with DNS auto-discovery
Kubernetes StatefulSet deployment, scaling, backup to S3
Project Structure Package layout and file descriptions
Arrow Type Mapping Cypher → Arrow IPC type mapping reference
Disaster Recovery Backup, restore, manifest integrity, S3
Metrics Prometheus metrics catalogue and label budget
Contributing Development setup, PR guidelines

License

MIT

About

Loveliness — a clustered graph database on LadybugDB. Like Elasticsearch is to Lucene.

Resources

License

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages