RonDB training · module 21 of 25 · 18 min
The REST API
Key reads, batches, writes and scans over HTTP and JSON, the feature store endpoints, and running the REST server in production.
rdrs2 · the RonDB REST API server
rdrs2: one C++ process serves REST, RonSQL, Rondis and TTL purging
The Go server of RonDB 21.04 was rewritten in C++ as rdrs2 in 24.10. Scans came in 25.10; batch writes and deletes and API 0.2.0 in 26.10.
code: storage/ndb/rest-server2 (config_structs_def.hpp: ServerPort 4406, NumThreads 64, ProbePort 4407, RonSQL NumThreads 16; MySQLRouter is myrouter, a testing tool) · CMakeLists.txt (Drogon) · notes: apis.md (history from release notes)
Endpoints · served under /0.1.0 and /0.2.0
REST endpoints: eleven, from a key read to SQL
| Method and path | Does |
|---|---|
| POST /{v}/{db}/{table}/pk-read | Read one row by primary key |
| POST /{v}/batch | Many key reads, any tables and databases |
| POST /{v}/batchwrite | Inserts, updates and upserts by key |
| DELETE /{v}/batchdelete | Deletes by key |
| POST /0.1.0/{db}/{table}/scan | Filtered scan, optionally on an index range |
| POST /{v}/ronsql | SQL queries, module 22 |
| POST /{v}/feature_store · batch_feature_store | Feature vectors for online inference |
| GET /{v}/ping · health · GET /metrics | Liveness, readiness, Prometheus metrics |
0.1.0 returns errors as text; 0.2.0 as JSON objects with HTTP code, NDB and MySQL error codes. New clients should use 0.2.0.
code: rest-server2/server/src/constants.hpp (API_VERSION 0.1.0, API_VERSION_2 0.2.0, the *_PATH definitions) · rest-server2/CLAUDE.md (0.2.0 error format) · docs: rondb_rest_api.md (REST API)
pk-read · one row
pk-read: name the full primary key, and optionally the columns to return
POST /0.2.0/test_db/my_table/pk-read
{
"filters": [
{"column": "id0", "value": 0},
{"column": "id1", "value": 0}
],
"readColumns": [
{"column": "col0"},
{"column": "col1"}
],
"operationId": "ABC123"
}
200 OK
{
"operationId": "ABC123",
"data": {
"col0": 123,
"col1": 456
}
}
- filters: every primary key column, required.
- Without readColumns, all columns come back.
- operationId: echoed back, up to 256 bytes.
docs: rondb_rest_api.md (Primary Key Read Request and Response; dataReturnType left out, it only supports default) · code: rest-server2/server/src/config_structs_def.hpp (OperationIDMaxSize 256)
batch · many reads in one request
A batch is a list of pk-reads, each with its own table and answer
POST /0.2.0/batch
{
"operations": [
{
"method": "POST",
"relative-url": "db_1/table_1/pk-read",
"body": { "filters": [ … ],
"operationId": "1" }
},
{
"method": "POST",
"relative-url": "db_2/table_2/pk-read",
"body": { "filters": [ … ],
"operationId": "2" }
}
]
}
- Reads across databases and tables, executed together: one round trip to the data nodes (module 20).
- The response has one result per operation, each with its own status: a missing row does not fail the batch.
- Up to BatchMaxSize operations per round trip: 128 by default, 50 to 512; larger batches take several.
docs: rondb_rest_api.md (Batched Primary Key) · code: rest-server2/server/src/config_structs_def.hpp (BatchMaxSize 128, 50–512) · db_operations/pk/pkr_operation.cpp (batches run in chunks of BatchMaxSize)
Writes · new in 26.10
batchwrite and batchdelete change rows by primary key
POST /0.2.0/batchwrite
{
"operations": [
{ "method": "POST",
"relative-url": "db/t/pk-write",
"body": { "filters": [ … ],
"writeColumns": [ … ] } },
{ "method": "POST",
"relative-url": "db/t/pk-update", … },
{ "method": "POST",
"relative-url": "db/t/pk-insert", … }
]
}
| Suffix | NDB API | Error |
|---|---|---|
| pk-write | writeTuple | upsert |
| pk-update | updateTuple | 404 if missing |
| pk-insert | insertTuple | 409 if it exists |
| pk-delete | deleteTuple | with HTTP DELETE /batchdelete |
code: rest-server2/CLAUDE.md (Batch Write, Batch Delete, URL suffixes and NDB operations) · notes: apis.md (404 and 409)
scan · filters and index ranges
REST scan: a filter tree, an optional index range, and a limit
POST /0.1.0/test_db/t/scan
{ "limit": 10,
"readColumns": [{"column": "pk"}, {"column": "val_1"}],
"filters": { "op": "AND", "args": [
{ "op": "ISNOTNULL", "column": "content" },
{ "op": "CMP", "column": "pk",
"cond": "GT", "value": 2 } ] },
"index": { "name": "idx_val",
"key_columns": ["val_1", "val_2"],
"ranges": [ {
"lower": {"values": [10, 100], "inclusive": true},
"upper": {"values": [70, 700], … } } ] } }
- The filter becomes an NdbScanFilter: evaluated in the data nodes (module 20).
- AND and OR nodes take exactly two arguments; up to 32 levels deep.
- Up to 64 ranges on an ordered index.
- With ScanTimingEnabled, scans slower than 10 ms are listed at
/0.1.0/slow-scans.
docs: rest_scan_api.md (request shortened) · code: rest-server2 json_parser.cpp (two arguments, depth 32, 64 ranges) · config_structs_def.hpp (ScanTimingEnabled false, SlowScanThresholdUs 10000)
PART 1
Feature store endpoints
A feature vector in one request, for online inference.
feature_store · Hopsworks feature views
feature_store: ask for a feature view by entity key, get the whole feature vector
POST /0.2.0/feature_store { "featureStoreName": "fs", "featureViewName": "fraud_fv", "featureViewVersion": 1, "entries": { "cc_num": 4423 }, "passedFeatures": { }, "metadataOptions": { … } } → { "features": [ … ], "status": "COMPLETE" }
- A feature view joins feature groups, each a RonDB table, on the entity key.
- Its definition is cached in rdrs2: a request is as cheap as a pk-read per feature group.
- Status COMPLETE, MISSING when a group has no row, or ERROR.
batch_feature_storetakes many entries at once.
code: rest-server2/server/src (featureStoreName, featureViewName, featureViewVersion, entries, passedFeatures, metadataOptions; FeatureStatus COMPLETE, MISSING, ERROR) · docs: rest_fs_bench.md · values are illustrations
Measured · one REST server
One REST server: hundreds of thousands of requests/s, millions of rows batched
| Benchmark | Batch | Threads | Rows/s | P99 |
|---|---|---|---|---|
| pk-read | 1 | 64 | 236 875 | 0.72 ms |
| batch of pk-reads | 256 | 32 | 2 146 081 | 6.10 ms |
| feature_store, one feature group | 1 | 64 | 212 656 | 0.94 ms |
| feature_store, two feature groups | 1 | 64 | 159 827 | 1.17 ms |
Single requests take tens of microseconds at low load: 0.04 ms median for one feature group on one thread. Batching multiplies rows per request.
docs: rest_bench.md · rest_fs_bench.md (the rows quoted)
PART 2
Running the REST server
Health, security, limits, sizing.
Health · for load balancers and Kubernetes
Health checks: a separate probe port answers even when REST threads are busy
ping
The process is up and answering.
health
"1" and 200 when connected to the cluster with at least one data node started; otherwise "0" and 503.
Probe port 4407
ping and health only, GET or HEAD, no API key: made for Kubernetes probes.
GET /metrics exposes Prometheus metrics for dashboards.
code: rest-server2 (health_ctrl.cpp, probe_server.hpp, config_structs_def.hpp ProbePort 4407) · notes: apis.md
Security · and fair use
Security: API keys say who calls; rate limits keep one tenant from starving others
Access
- Header
X-API-KEY, checked against Hopsworks API keys. - Keys are cached and refreshed every 180 s.
- TLS is available, off by default.
Rate limits and quotas
- Per database: quotas on memory, disk and request rate, set with ndb_mgm.
- Enforced in the data nodes when
ActivateRateLimits=1. - A limited request gets HTTP 429: back off and retry.
code: rest-server2/server/src/constants.hpp (x-api-key) · config_structs_def.hpp (CacheRefreshIntervalMS 180000, TLS) · ConfigInfo.cpp (ActivateRateLimits false) · docs: rondb_rate_limits_quotas.md
Sizing · the settings that matter
REST server sizing: enough CPUs per server; add servers to scale
| Setting | Default | Note |
|---|---|---|
| REST NumThreads | 64 | Request threads |
| RonSQL NumThreads | 16 | RonSQL workers, with a request queue |
| BatchMaxSize | 128 | Batch operations per round trip |
| maxReqSize | 4 MB | Largest request body |
The measurements above used about 16 CPUs per REST server. Run several behind a load balancer; Kubernetes can scale them on CPU (module 19).
code: rest-server2/server/src/config_structs_def.hpp · docs: rest_bench.md · rondb_helm.md
Recap · module 21
What to take away
- rdrs2: REST on 4406, probes on 4407, plus RonSQL, Rondis and TTL purging.
- pk-read and batch for reads; batchwrite and batchdelete for changes; scan with pushed filters.
- Use API 0.2.0: JSON errors with NDB and MySQL codes.
- feature_store returns a whole feature vector from cached view definitions.
- API keys, rate limits with 429, and several servers of about 16 CPUs each.
RonDB training · 25 modules · you are at 21