Bluetape4k Graph Part 2: Core API and Execution Models

Part 1 covered graph storage selection. This part looks at graph-core, the common layer I wanted before the adapters
grew too much service-specific code. The goal is simple: service code should speak in vertices, edges, traversal, and
algorithms while graph storage adapters handle driver and query details. Without that boundary, every service slowly
grows its own tiny graph framework. Most of those frameworks are loved only on the day they are born.

What GraphOperations Combines
Section titled “What GraphOperations Combines”GraphOperations combines GraphSession, GraphVertexRepository, GraphEdgeRepository, and
GraphGenericRepository. GraphGenericRepository is the narrower composite for traversal and
algorithm operations.
| Area | Representative methods | What service code gets |
|---|---|---|
| Session | createGraph, dropGraph, graphExists | graph lifecycle for fixtures or tenant graphs |
| Vertex | createVertex, createVertices, findVertexById, findVerticesByLabel, updateVertex, deleteVertex, countVertices | node CRUD without direct driver code |
| Edge | createEdge, createEdges, findEdgesByLabel, findEdgesByStartId, findEdgesByEndId, deleteEdge | relationship CRUD and evidence lookup |
| Traversal | neighbors, shortestPath, allPaths, aStarPath | shared identity, recommendations, and path queries |
| Algorithm | pageRank, degreeCentrality, connectedComponents, bfs, dfs, detectCycles | ranking, components, traversal visits, and cycle checks |
val people = ops.createVertices( "Person", listOf( mapOf("email" to "alice@example.com", "name" to "Alice"), mapOf("email" to "bob@example.com", "name" to "Bob"), ))
ops.createEdges( "KNOWS", listOf(BatchEdge(people[0].id, people[1].id, mapOf("since" to 2026))))The default batch methods call the single-row APIs sequentially. If a call fails halfway through, previously created vertices or edges can remain. This is a compatibility baseline, not an atomicity guarantee. Production adapters can override it with native batch writes, but callers still need to verify the transaction boundary of the selected implementation.
Schema DSL
Section titled “Schema DSL”Schema is declared with VertexLabel and EdgeLabel, similar to Exposed table objects.
object PersonLabel : VertexLabel("Person") { val email = string("email") val name = string("name")}
object KnowsLabel : EdgeLabel("KNOWS", PersonLabel, PersonLabel) { val since = integer("since")}Schema management is not equally portable across graph storage engines. schemaManager() returns
a manager only when the selected adapter implements GraphSchemaManagementOperations. An adapter
without that capability throws UnsupportedOperationException instead of reporting a no-op as
success. UnsupportedGraphSchemaManager returns empty metadata lists for reads but also rejects
schema mutations explicitly.
Transactions and merge
Section titled “Transactions and merge”The transaction DSL exposes vertex and edge CRUD only. Graph lifecycle commands stay outside the
block because DDL and auto-commit semantics differ by backend. An implementation that does not
provide GraphTransactionalOperations fails instead of silently falling back to auto-commit.

Merge follows the same rule. Implementations call GraphMergeValidation; mergeVertex requires
non-empty match properties, and set properties cannot overwrite identity keys. If the selected
storage does not implement GraphMergeOperations, the extension fails rather than pretending a
read-then-write fallback is safe under concurrency.
val alice = ops.mergeVertex( label = "Person", matchProperties = mapOf("email" to "alice@example.com"), setProperties = mapOf("name" to "Alice"),)Execution models and benchmark shape
Section titled “Execution models and benchmark shape”| Model | Good fit |
|---|---|
| Sync | Single operations, tests, ordinary blocking drivers |
| Virtual Threads | Blocking drivers under higher concurrency |
| Coroutine | Ktor or coroutine-native service composition |

The ApiModelBenchmark smoke run used a shared TinkerGraph fixture, so it excludes network and
external database I/O. It ran on GraalVM JDK 25.0.3 with one JMH fork, one warmup iteration, three
measurement iterations, one second per iteration, and -prof gc.
| Scenario | API model | Mean | Error | Allocation |
|---|---|---|---|---|
| PageRank throughput | Sync | 138,943.484 ops/s | ±40,362.146 | 28,451 B/op |
| PageRank throughput | Virtual Thread | 40,283.460 ops/s | ±9,678.720 | 29,456 B/op |
| PageRank throughput | Coroutine Flow | 36,879.554 ops/s | ±85,084.781 | 29,516 B/op |
| BFS depth=5 | Sync | 4.724 us/op | ±3.022 | 21,990 B/op |
| BFS depth=5 | Virtual Thread | 18.668 us/op | ±8.229 | 23,152 B/op |
| BFS depth=5 | Coroutine Flow | 20.244 us/op | ±11.268 | 23,455 B/op |
| BFS 100-way | Virtual Thread | 240.903 us/op | ±167.502 | 2,318,801 B/op |
| BFS 100-way | Coroutine async | 279.828 us/op | ±329.942 | 2,367,754 B/op |
| 100-way launch/create | Virtual Thread | 51.042 us/op | ±173.745 | 61,464 B/op |
| 100-way launch/create | Coroutine async | 5.916 us/op | ±3.127 | 28,373 B/op |
These are short smoke results with wide error intervals. They do not establish a release-grade or general performance ranking.
java -jar benchmark/graph-benchmark/build/benchmarks/main/jars/graph-benchmark-main-jmh-*-JMH.jar \ '.*ApiModelBenchmark.*' \ -wi 1 -i 3 -r 1s -w 1s -f 1 \ -prof gc \ -rf json \ -rff docs/benchmark/2026-05-21-api-model-jmh.jsonSo coroutines are not the “always faster” model. Use them when Kotlin composition, cancellation, and structured concurrency matter. Use virtual threads when a Spring MVC service needs to run blocking graph drivers at higher concurrency. Execution model selection is not a preference poll; it follows the request path and the driver behavior.
Part 3 covers graph-io, which imports and exports graph data as CSV, NDJSON, and GraphML, and
explains how to read its benchmark results. OkIO is treated separately as the layer that composes
buffering, compression, and asynchronous I/O rather than as a data format.
Sources
Section titled “Sources”- GraphOperations
- GraphVertexRepository
- GraphEdgeRepository
- GraphTraversalRepository
- GraphAlgorithmRepository
- GraphMergeOperations
- GraphTransactionScope
- API model benchmark
Comments
Leave a note or reaction with your GitHub account.