Skip to content

RonDB REST API Server#

The RonDB REST API Server gives access to data in RonDB databases over HTTP, so that applications written in Python, Julia, Go or any other language can use RonDB without the C++ or Java APIs. It first appeared in RonDB 21.04, written in Go. RonDB 24.10 reimplemented it in C++ as rdrs2, with much higher performance and without the gRPC interface.

REST API#

The REST API Server supports these operations:

  • Primary key reads, of one row (pk-read) or of many rows from many tables (batch).

  • Index scans with filters (scan), since RonDB 25.10.

  • Writes and deletes by primary key, in batches (batchwrite, batchdelete), since RonDB 26.10.

  • SQL queries executed by RonSQL (ronsql).

  • Feature vector lookups for the Hopsworks Feature Store (feature_store, batch_feature_store).

  • Health checks (health, ping) and Prometheus metrics (/metrics).

  • Control and metrics of the TTL purge worker (ttl-purge) and diagnostics of slow scans (slow-scans).

The sections below describe the API versions, the primary key reads, writes and deletes, and the RonSQL queries. Scans and slow scans are described in the chapter on the REST scan API, and the TTL purge endpoints in the chapter on the TTL purge REST API.

API Versions#

The path of each endpoint starts with the API version, 0.1.0 or 0.2.0, for example /0.2.0/batchwrite. The scan, ttl-purge and slow-scans endpoints exist only under 0.1.0, and the Prometheus metrics are served at /metrics, without a version.

Under both versions an endpoint takes the same requests and returns the same results. The batch, batchwrite and batchdelete endpoints differ in how they report errors: under 0.1.0 the body of an error response is a plain text message, under 0.2.0 it is a JSON object:

{
  "error": {
    "code": 400,
    "message": "No operations defined",
    "status": 0,
    "classification": 0,
    "ndbCode": 0,
    "mysqlCode": 0
  }
}

code is the HTTP status code and message the error message. For an error from the data nodes, ndbCode and mysqlCode are the NDB and MySQL error codes, and status and classification are the status and classification of the NDB error. The other endpoints return the same error responses under both API versions.

Supported Data Types#

Default mappings of MySQL data types to JSON data types are as follows:

MySQL Data Types JSON Data Type
TINYINT, SMALLINT, MEDIUMINT, INT, BIGINT number
FLOAT, DOUBLE, DECIMAL number
CHAR, VARCHAR escaped string
BINARY, VARBINARY base64 encoded string
DATE, DATETIME, TIME, TIMESTAMP string
YEAR number
BIT base64 encoded string

Primary Key Read Operations#

It is used to perform a primary key read operation. Assume we have the following table.

CREATE TABLE `my_table` (                                            
  `id0` int NOT NULL,                                                 
  `id1` int unsigned NOT NULL,                                        
  `col0` int DEFAULT NULL,                                            
  `col1` int unsigned DEFAULT NULL,                                   
  PRIMARY KEY (`id0`,`id1`)                                           
) ENGINE=ndbcluster DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci

Primary Key Read Request#

A primary key read request would look something like this

POST /{api-version}/test_db/my_table/pk-read

Body:

{
  "filters": [
    {
      "column": "id0",
      "value": 0
    },
    {
      "column": "id1",
      "value": 0
    }
  ],
  "readColumns": [
    {
      "column": "col0",
      "dataReturnType": "default"
    },
    {
      "column": "col1",
      "dataReturnType": "default"
    }
  ],
  "operationId": "ABC123"
}

Parameters:

  • filters: (required) This is an array of objects one for each column that forms the primary key.

  • readColumns: (optional) This is used to perform projections. If it is omitted, all the columns of the table will be read

  • dataReturnType: (optional) This can be used to control in which format the data is returned, for example, hex, base64, etc. However, in this version (0.1.0) we only support the default return type.

  • operationId: (optional) A string parameter, up to Internal.OperationIDMaxSize bytes long (default 256).

Primary Key Read Response#

Body:

{
  "operationId": "ABC123",
  "data": {
    "col0": 123,
    "col1": 456
  }
}

Primary Key Read Error Handling#

Code Error Message
200 OK
400 Bad Request. The error message will contain more information about which part of the request is invalid. For example, if incomplete primary key is specified
401 Access denied due to invalid API KEY
404 Not Found. No row has the given primary key
429 Too Many Requests. A rate limit or quota was exceeded
500 Server Error. For example, database failure

Batched Primary Key#

You can use this to batch multiple primary key operations across different databases.

Batched Primary Key Request#

POST /{api-version}/batch

Body:

{
  "operations": [
    {
      "method": "POST",
      "relative-url": "my_database_1/my_table_1/pk-read",
      "body": {
        "filters": [
          {
            "column": "id0",
            "value": 0
          },
          {
            "column": "id1",
            "value": 0
          }
        ],
        "readColumns": [
          {
            "column": "col0",
            "dataReturnType": "default"
          },
          {
            "column": "col1",
            "dataReturnType": "default"
          }
        ],
        "operationId": "1"
      },
    },
    {
      "method": "POST",
      "relative-url": "my_database_2/my_table_2/pk-read",
      "body": {
        "filters": [
          {
            "column": "id0",
            "value": 1
          },
          {
            "column": "id1",
            "value": 1
          }
        ],
      },
    },
  ]
}

Parameters:

  • relative-url: (required) This represents the url the given pk-read would have in a single request (omitting the api-version).

Batched Primary Key Response#

Body:

{
  "result": [
    {
      "code": 200,
      "body": {
        "operationId": "1",
        "data": {
          "col0": 0,
          "col1": 0
        }
      }
    },
    {
      "code": 200,
      "body": {
        "operationId": "",
        "data": {
          "col0": 1,
          "col1": 1
        }
      }
    }
  ]
}

Batch Key Read Error Handling#

Code Error Message
200 OK. The batch operation was executed and an array of sub-responses is returned. Each sub response contains its own status code. For example, some suboperations may return code 200 while other may return code 404
400 Bad Batch Request. Server fails for unmarshal the request
401 Access denied due to invalid API KEY
500 Server Error. For example, database failure

Batched Primary Key Writes#

You can use this to insert, update and write rows by primary key, in one or more tables and databases. With REST.UseSingleTransaction set to true (the default), all operations of a request run in one transaction.

Batched Primary Key Write Request#

POST /{api-version}/batchwrite

Body:

{
  "operations": [
    {
      "method": "POST",
      "relative-url": "my_database_1/my_table_1/pk-write",
      "body": {
        "filters": [
          {
            "column": "id0",
            "value": 0
          },
          {
            "column": "id1",
            "value": 0
          }
        ],
        "writeColumns": [
          {
            "column": "col0",
            "value": 100
          },
          {
            "column": "col1",
            "value": 200
          }
        ],
        "operationId": "1"
      }
    },
    {
      "method": "POST",
      "relative-url": "my_database_2/my_table_2/pk-update",
      "body": {
        "filters": [
          {
            "column": "id0",
            "value": 1
          },
          {
            "column": "id1",
            "value": 1
          }
        ],
        "writeColumns": [
          {
            "column": "col0",
            "value": 101
          }
        ],
        "operationId": "2"
      }
    }
  ]
}

Parameters:

  • relative-url: (required) The database, the table and the operation, separated by slashes. The operation is one of:

    • pk-write: Insert the row, or update it if a row with the primary key exists (NDB writeTuple).

    • pk-update: Update an existing row (NDB updateTuple).

    • pk-insert: Insert a new row (NDB insertTuple).

  • method: (optional) If given, it must be POST.

  • filters: (required) An array of objects, one for each column of the primary key.

  • writeColumns: (required) An array of objects, each with the name and the new value of a column to write.

  • operationId: (optional) A string parameter, up to Internal.OperationIDMaxSize bytes long (default 256).

Batched Primary Key Write Response#

Body:

{
  "result": [
    {
      "code": 200,
      "body": {
        "operationId": "1",
        "data": {}
      }
    },
    {
      "code": 404,
      "body": {
        "operationId": "2",
        "data": {}
      }
    }
  ]
}

Batched Primary Key Write Error Handling#

Code Error Message
200 OK. The batch was executed and an array of sub-responses is returned, each with its own status code: 200 when the operation succeeded, 404 when a pk-update found no row with the primary key
400 Bad Request. For example, a relative-url that does not end in pk-write, pk-update or pk-insert
401 Access denied due to invalid API KEY
409 Conflict. A pk-insert found a row with the same primary key
429 Too Many Requests. A rate limit or quota was exceeded
500 Server Error. For example, database failure

Batched Primary Key Deletes#

You can use this to delete rows by primary key, in one or more tables and databases. With REST.UseSingleTransaction set to true (the default), all operations of a request run in one transaction.

Batched Primary Key Delete Request#

DELETE /{api-version}/batchdelete

Body:

{
  "operations": [
    {
      "method": "POST",
      "relative-url": "my_database_1/my_table_1/pk-delete",
      "body": {
        "filters": [
          {
            "column": "id0",
            "value": 0
          },
          {
            "column": "id1",
            "value": 0
          }
        ],
        "operationId": "1"
      }
    },
    {
      "method": "POST",
      "relative-url": "my_database_2/my_table_2/pk-delete",
      "body": {
        "filters": [
          {
            "column": "id0",
            "value": 1
          },
          {
            "column": "id1",
            "value": 1
          }
        ],
        "operationId": "2"
      }
    }
  ]
}

Parameters:

  • relative-url: (required) The database, the table and pk-delete, separated by slashes.

  • method: (optional) If given, it must be POST, although the request itself uses DELETE.

  • filters: (required) An array of objects, one for each column of the primary key.

  • operationId: (optional) A string parameter, up to Internal.OperationIDMaxSize bytes long (default 256).

Batched Primary Key Delete Response#

Body:

{
  "result": [
    {
      "code": 200,
      "body": {
        "operationId": "1",
        "data": {}
      }
    },
    {
      "code": 404,
      "body": {
        "operationId": "2",
        "data": {}
      }
    }
  ]
}

Batched Primary Key Delete Error Handling#

Code Error Message
200 OK. The batch was executed and an array of sub-responses is returned, each with its own status code: 200 when the row was deleted, 404 when no row has the primary key
400 Bad Request. For example, a relative-url that does not end in pk-delete
401 Access denied due to invalid API KEY
429 Too Many Requests. A rate limit or quota was exceeded
500 Server Error. For example, database failure

RonSQL query execution#

RonSQL is used to execute SQL queries against a RonDB cluster using pushdown technology, thus achieving higher performance than what is possible via MySQL Server. See the RonSQL documentation for a description of the supported queries, EXPLAIN output, error handling and configuration.

Assume we have the following table.

CREATE TABLE `my_table` (
  `id0` varchar(10) NOT NULL,
  `id1` int unsigned NOT NULL,
  `col0` int DEFAULT NULL,
  `col1` int unsigned DEFAULT NULL
) ENGINE=ndbcluster DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci

Query request#

A RonSQL query can be submitted like this: POST /{api-version}/ronsql, where the API version is 0.1.0 or 0.2.0.

Body:

{
  "query": "SELECT id0, id1, SUM(col0), MAX(col1) FROM my_table WHERE id1 > 7 GROUP BY id0, id1;",
  "database": "test_db",
  "explainMode": "ALLOW",
  "outputFormat": "JSON",
  "operationId": "ABC123"
}

Note that a JSON string cannot contain raw line breaks, so the query must be written on a single line or with \n escape sequences.

Parameters:

  • query: (required) The RonSQL query to be executed, terminated by a semicolon. See the RonSQL documentation for a description of supported queries.

  • database: (required) Name of the database containing the tables read by the query.

  • explainMode: (optional) How to handle EXPLAIN queries. The value can be one of:

    • ALLOW: (default) Allow all query types, currently meaning SELECT and EXPLAIN SELECT.

    • FORBID: Allow only SELECT queries. Return an error code if given an EXPLAIN SELECT query.

    • REQUIRE: Allow only EXPLAIN queries. Return an error code if given a SELECT query.

    • REMOVE: Reinterpret any EXPLAIN SELECT query as a SELECT query.

    • FORCE: Reinterpret any SELECT query as an EXPLAIN SELECT query.

  • outputFormat: (optional) Output format. The value can be one of:

    • JSON: (default) JSON format with UTF-8 encoding, and with most characters represented by themselves. Set response content type to application/json; charset=utf-8.

    • JSON_ASCII: JSON format with ASCII encoding. Use \u escape sequences. The output stream will only contain characters 0x0a, 0x20 -- 0x7e. Set response content type to application/json; charset=US-ASCII.

    • TEXT: For query output, mimic mysql tab-separated output with headers and set response content type to text/tab-separated-values; charset=utf-8; header=present. For explain output, use a plain text format and set response content type to text/plain; charset=utf-8.

    • TEXT_NOHEADER: For query output, mimic mysql tab-separated output without headers and set response content type to text/tab-separated-values; charset=utf-8; header=absent. For explain output, use a plain text format and set response content type to text/plain; charset=utf-8.

    Explain output is only available with TEXT and TEXT_NOHEADER.

  • operationId: (optional) A string parameter, up to Internal.OperationIDMaxSize bytes long (default 256). Can only be supplied when outputFormat is JSON or JSON_ASCII.

Query response#

The query response depends on whether the query was executed as SELECT or EXPLAIN SELECT, and on the requested output format.

Response body to SELECT query with "outputFormat": "JSON":

{"operationId": "ABC123",
"data":
[{"id0":"EE","id1":10,"SUM(col0)":13,"MAX(col1)":180}
,{"id0":"AÅ","id1":25,"SUM(col0)":83,"MAX(col1)":357}
]
}

Response body to SELECT query with "outputFormat": "JSON_ASCII":

{"operationId": "ABC123",
"data":
[{"id0":"EE","id1":10,"SUM(col0)":13,"MAX(col1)":180}
,{"id0":"AÅ","id1":25,"SUM(col0)":83,"MAX(col1)":357}
]
}

Response body to SELECT query with "outputFormat": "TEXT":

id0 id1 SUM(col0)   MAX(col1)
EE  10  13  180
AÅ  25  83  357

Response body to SELECT query with "outputFormat": "TEXT_NOHEADER":

EE  10  13  180
AÅ  25  83  357

Response body to EXPLAIN SELECT query with "outputFormat": "TEXT":

Query parse tree:
SELECT
  Out_0:`id0`
   = C0:`id0`
  Out_1:`id1`
   = C1:`id1`
  Out_2:`SUM(col0)`
   = A0:Sum(`col0`)
  Out_3:`MAX(col1)`
   = A1:Max(`col1`)
FROM my_table
WHERE
  >
  ├─ `id1`
  ╰─ 7
GROUP BY
  C0:`id0`
  C1:`id1`

Aggregation program (4 instructions):
Instr. DEST SRC DESCRIPTION
Load   r00  C02 r00 = C02:`col0`
Sum    A00  r00 A00:SUM <- r00:`col0`
Load   r00  C03 r00 = C03:`col1`
Max    A01  r00 A01:MAX <- r00:`col1`

Execute as table scan.
FILTERS:
╰─ >
   ├─ `id1`
   ╰─ 7

Output in mysql-style tab separated format.
The program for post-processing and output has 14 instructions.

See the RonSQL documentation for a description of the explain output, and of the error responses and HTTP status codes of the /ronsql endpoint.

Configuration Parameters#

The REST API Server reads its configuration from a JSON file given with --config. Keys beginning with # are ignored and can be used as comments. The command rdrs2 --print-config prints the effective configuration together with explanations. A configuration with all default values looks like this:

{
    "Internal": {
        "maxReqSize": 4194304,
        "MaxRespSize": 67108864,
        "SchemaCacheTTLSecs": 10,
        "ReqBufferSize": 1048576,
        "RespBufferSize": 5242880,
        "ScanRespBufferSize": 262144,
        "ScanTimingEnabled": false,
        "SlowScanThresholdUs": 10000,
        "SlowScanBufferSize": 1000,
        "PreAllocatedBuffers": 32,
        "BatchMaxSize": 128,
        "OperationIDMaxSize": 256
    },
    "PIDFile": "",
    "RateLimit": {
        "Enable": false,
        "Identity": "apikey",
        "FullAPIKey": false
    },
    "REST": {
        "Enable": true,
        "UseCompression": true,
        "ServerIP": "0.0.0.0",
        "ServerPort": 4406,
        "NumThreads": 64,
        "HealthRequiresAuth": false,
        "PingRequiresAuth": false,
        "UseSingleTransaction": true,
        "MaxKeepaliveRequests": 0,
        "IdleConnectionTimeoutS": 0,
        "ProbeEnable": true,
        "ProbePort": 4407,
        "UploadPath": ""
    },
    "RonSQL": {
        "NumThreads": 16,
        "MaxQueuedRequests": 1024
    },
    "Rondis": {
        "Enable": false,
        "ServerIP": "0.0.0.0",
        "ServerPort": 6379,
        "NumThreads": 16,
        "NumDatabases": 1,
        "Databases": [
            {
                "Index": 0,
                "FastHCOUNT": false,
                "OptimizeSmallValues": true,
                "Mode": "RONDB",
                "DirtyINCRDECR": false
            }
        ],
        "CreateTables": true,
        "RequireTablesOnStartup": true,
        "MySQLHost": "localhost",
        "MySQLPort": 3306,
        "MySQLUser": "root",
        "MySQLPassword": ""
    },
    "RonDB": {
        "Mgmds": [
            {
                "IP": "localhost",
                "Port": 1186
            }
        ],
        "ConnectionPoolSize": 1,
        "NodeIDs": [],
        "ConnectionRetries": 5,
        "ConnectionRetryDelayInSec": 5,
        "OpRetryOnTransientErrorsCount": 3,
        "OpRetryInitialDelayInMS": 500,
        "OpRetryJitterInMS": 100
    },
    "RonDBMetadataCluster": {
        "Mgmds": [
            {
                "IP": "localhost",
                "Port": 1186
            }
        ],
        "ConnectionPoolSize": 0,
        "NodeIDs": [],
        "ConnectionRetries": 5,
        "ConnectionRetryDelayInSec": 5,
        "OpRetryOnTransientErrorsCount": 3,
        "OpRetryInitialDelayInMS": 500,
        "OpRetryJitterInMS": 100
    },
    "Security": {
        "InsecureAllowAll": false,
        "TLS": {
            "EnableTLS": false,
            "RequireClientCert": false,
            "CertificateFile": "",
            "PrivateKeyFile": "",
            "RootCACertFile": ""
        },
        "APIKey": {
            "UseHopsworksAPIKeys": true,
            "CacheRefreshIntervalMS": 180000,
            "PreloadThreads": 3
        }
    },
    "FeatureStore": {
        "FeatureStoreMetadataCache": {
            "PreloadThreads": 3
        }
    },
    "TTLPurge": {
        "Enable": true,
        "ActiveWindow": ""
    },
    "Log": {
        "Level": "warn",
        "FilePath": "",
        "MaxSizeMb": 100,
        "MaxBackups": 10,
        "MaxAge": 30,
        "LogQueries": false
    },
    "MySQLRouter": {
        "Enable": false,
        "ServerIP": "0.0.0.0",
        "ServerPort": 3307,
        "NumThreads": 4,
        "BackendHost": "127.0.0.1",
        "BackendPort": 3306,
        "DebugLogging": false
    }
}

Internal#

  • maxReqSize: The maximum HTTP body size for REST requests, in bytes. Must be at least 256. This is also installed as the HTTP server’s client body limit (requests above it are refused with 413 before assembly), and it sizes the per-REST-thread JSON parse buffers, so NumThreads * maxReqSize bytes are pre-allocated. The default is 4194304.

  • MaxRespSize: The maximum HTTP response body size RDRS will build, in bytes; 0 = unlimited. Currently enforced on the /ronsql endpoint, whose responses can grow with the query’s result set; a capped query fails with a clear error instead of exhausting server memory. The default is 67108864.

  • SchemaCacheTTLSecs: Seconds before a cached per-table index list (the RonSQL schema cache) is refreshed from the NDB dictionary. The refresh is what makes newly CREATED indexes visible to a warm RDRS: index creation does not bump the base table’s schema version, so version checks alone never detect them (drops self-heal via schema errors). 0 = never expire (refresh only on schema-version change or schema-error invalidation). The default is 10.

  • ReqBufferSize: Size in bytes of the internal buffer that holds an encoded request for the RonDB data access layer. The default is 1048576.

  • RespBufferSize: Size in bytes of the internal buffer that holds a response from the RonDB data access layer. The default is 5242880.

  • ScanRespBufferSize: Initial buffer reservation for scan response JSON, in bytes. The buffer will grow automatically if the response exceeds this size. The default is 262144.

  • ScanTimingEnabled: Enable scan timing metrics collection. When enabled, records per-scan timing breakdown and maintains a slow scan buffer for diagnostics. The default is false.

  • SlowScanThresholdUs: Threshold in microseconds for recording slow scans. Scans exceeding this threshold are recorded in the slow scan buffer. Default is 10000 (10ms).

  • SlowScanBufferSize: Maximum number of slow scan entries to keep in the circular buffer. The default is 1000.

  • PreAllocatedBuffers: Number of request and response buffers allocated at startup. The default is 32.

  • BatchMaxSize: Maximum number of requests contained in a batch request. The default is 128.

  • OperationIDMaxSize: Maximum length of operation ID strings. The default is 256.

PIDFile#

Path to .pid file. The process ID will be written on startup, and the file will be deleted on exit.

The default is "".

RateLimit#

USER rate limiting policy for this server’s NDB traffic (RONDB-978).

  • Enable: Set to true to tag NDB transactions with a rate limit identity so that the data nodes enforce USER rate limits. The data nodes must also run with ActivateRateLimits, and an identity with no provisioned USER entity runs unmetered. Currently applies to REST requests only; Rondis traffic is never tagged. The default is false.

  • Identity: Identity type used for rate limiting. Currently only ’apikey’ (the API key of the request) is supported. The default is "apikey".

  • FullAPIKey: Set to true to use the full API key as the rate limit identity. By default only the API key prefix is used.

REST#

REST server settings.

  • Enable: Whether to enable the REST server. The default is true.

  • UseCompression: Whether to send response gzip compressed. The default is true.

  • ServerIP: The IP address to listen on. The default is "0.0.0.0".

  • ServerPort: TCP port to listen on. The default is 4406.

  • NumThreads: Number of threads handling REST requests. The default is 64.

  • HealthRequiresAuth: Set to true to require authentication for the health endpoint on the main port (ServerPort). The probe port (ProbePort) never authenticates, so this setting is rejected at startup unless ProbeEnable is false; see ProbeEnable. The default is false.

  • PingRequiresAuth: Set to true to require authentication for the ping endpoint on the main port (ServerPort). The probe port (ProbePort) never authenticates, so this setting is rejected at startup unless ProbeEnable is false; see ProbeEnable. The default is false.

  • UseSingleTransaction: Set to true to use single transaction for entire batch. The default is true.

  • MaxKeepaliveRequests: Maximum number of requests served over one keep-alive connection on the main port before the server closes it; 0 (the default) disables the limit. A Kubernetes Service balances per TCP connection, not per request, so long-lived pooled connections stick to whichever pod they first landed on - after a fleet restart the first-ready pod keeps most of the load forever. With a limit, every close forces the client to reconnect and be re-balanced, and the busiest pod sheds connections fastest, so skew levels out on its own. Does not apply to the probe port.

  • IdleConnectionTimeoutS: Seconds a keep-alive connection on the main port may sit idle before the server closes it; 0 (the default) keeps the built-in 60 second timeout. Complements MaxKeepaliveRequests for clients that hold connections open but send rarely. Does not apply to the probe port.

  • ProbeEnable: Whether to serve ping and health on a dedicated probe port. The probe port runs on its own thread so it keeps answering while every request thread is busy or blocked, which is what Kubernetes probes need. The auth contract: the probe port is ALWAYS unauthenticated - API-key validation reads RonDB and can stall for tens of seconds - while the main port (ServerPort) endpoints honour PingRequiresAuth and HealthRequiresAuth. Combining ProbeEnable with either of those flags (with UseHopsworksAPIKeys) is therefore rejected at startup: if authenticated ping or health is required, set this to false and use the main port, accepting that its endpoints share the request threads. Ping on the probe port answers 503 until the main port accepts connections and 200 forever after, so a Kubernetes startup probe can use it with main-port semantics. The default is true.

  • ProbePort: TCP port of the dedicated probe listener (see ProbeEnable). Serves only GET/HEAD of the ping and health endpoints, always without authentication. TLS mirrors the main REST listener, except a client certificate is never required. The default is 4407.

  • UploadPath: Directory for the HTTP server’s temporary request-body files: at startup it creates 256 subdirectories under \<UploadPath>/tmp and any request body larger than the in-memory limit is buffered in a file there. Empty (the default) keeps the built-in location, ./uploads relative to the working directory. Set this to a writable directory when the working directory is not writable, otherwise startup logs 256 ’Permission denied’ errors and oversized request bodies are silently read as empty.

RonSQL#

RonSQL execution settings for the /ronsql REST endpoint.

  • NumThreads: Number of RonSQL worker threads. A /ronsql request is parsed and authorized on the REST thread that received it and then executed by one of these workers, each with its own NDB object, so a long statement does not stall the other connections of that REST thread. It is also the number of RonSQL statements this server executes at once. 0 executes the statements on the REST threads (no pool). The default is 16.

  • MaxQueuedRequests: Maximum number of /ronsql requests waiting for a RonSQL worker. A request arriving when the queue is full fails at once with 503 and error class ’resource’ (temporary; retry later). The default is 1024.

Rondis#

An object describing configuration for the rondis server.

  • Enable: Whether to enable the Rondis server. The default is false.

  • ServerIP: The IP address to listen on. The default is "0.0.0.0".

  • ServerPort: TCP port to listen on. The default is 6379.

  • NumThreads: Number of rondis threads. The default is 16.

  • NumDatabases: Number of rondis databases. The default is 1.

  • Databases: Database-specific configuration. A list of objects with these fields:

    • Index: The name of the database, in the form of an integer. The first database is named 0.

    • FastHCOUNT: Not used in RonDB 26.10.

    • OptimizeSmallValues: Whether to optimize for small values. The default is true.

    • Mode: Database mode: COMPATIBLE, CLUSTER or RONDB. The default is "RONDB".

    • DirtyINCRDECR: Whether to use dirty write for INCR and DECR. The default is false.

  • CreateTables: Whether to create Rondis databases and tables on startup if they don’t exist. The default is true.

  • RequireTablesOnStartup: Whether to require Rondis tables to exist on startup. Set to false for MTR tests where tables are created by the test itself. The default is true.

  • MySQLHost: The MySQL server host for creating Rondis tables. The default is "localhost".

  • MySQLPort: The MySQL server port for creating Rondis tables. The default is 3306.

  • MySQLUser: The MySQL user for creating Rondis tables. The default is "root".

  • MySQLPassword: The MySQL password for creating Rondis tables. The default is "".

RonDB#

An object describing the connection to a RonDB cluster used to store data.

  • Mgmds: An array of ndb_mgmd servers in use by the cluster. A list of objects with these fields:

    • IP: The IP address for an ndb_mgmd server. The default is "localhost".

    • Port: The TCP port for an ndb_mgmd server. The default is 1186.

  • ConnectionPoolSize: Number of RonDB cluster connections used to access data. The default is 1.

  • NodeIDs: An array of ConnectionPoolSize or zero length. Each element is a RonDB node ID, forcing the corresponding connection to be assigned to a specific node ID. If length is zero, node IDs are instead assigned by RonDB. The default is [].

  • ConnectionRetries: Connection retry attempts. The default is 5.

  • ConnectionRetryDelayInSec: After a failed connection attempt, wait this number of seconds before trying again. The default is 5.

  • OpRetryOnTransientErrorsCount: Transient error retry count. The default is 3.

  • OpRetryInitialDelayInMS: Transient error initial delay. The default is 500.

  • OpRetryJitterInMS: Random jitter in milliseconds added to the retry delay, so that failed operations are not all retried at the same time. The default is 100.

RonDBMetadataCluster#

An object describing the connection to a RonDB cluster used to store metadata. It has the same schema as .RonDB. If it is not present in the config file, then it will be set to .RonDB.

  • Mgmds: An array of ndb_mgmd servers in use by the cluster. A list of objects with these fields:

    • IP: The IP address for an ndb_mgmd server. The default is "localhost".

    • Port: The TCP port for an ndb_mgmd server. The default is 1186.

  • ConnectionPoolSize: Number of RonDB cluster connections used to access metadata. If set to 0, then the first RonDB cluster connection for data will be used for metadata as well. The default is 0.

  • NodeIDs: An array of ConnectionPoolSize or zero length. Each element is a RonDB node ID, forcing the corresponding connection to be assigned to a specific node ID. If length is zero, node IDs are instead assigned by RonDB. The default is [].

  • ConnectionRetries: Connection retry attempts. The default is 5.

  • ConnectionRetryDelayInSec: After a failed connection attempt, wait this number of seconds before trying again. The default is 5.

  • OpRetryOnTransientErrorsCount: Transient error retry count. The default is 3.

  • OpRetryInitialDelayInMS: Transient error initial delay. The default is 500.

  • OpRetryJitterInMS: Random jitter in milliseconds added to the retry delay, so that failed operations are not all retried at the same time. The default is 100.

Security#

  • InsecureAllowAll: WARNING, INSECURE! Authorize everyone to do everything, even without authentication. Exactly one of InsecureAllowAll and APIKey.UseHopsworksAPIKeys must be true. The default is false.

  • TLS

    • EnableTLS: Whether to enable TLS for the REST server. The default is false.

    • RequireClientCert: Whether to require REST clients to provide a client certificate. The default is false.

    • CertificateFile: Path to the server certificate file. The default is "".

    • PrivateKeyFile: Path to the private key corresponding to the server certificate. The default is "".

    • RootCACertFile: Path to the CA certificate used to sign the server certificate. The default is "".

  • APIKey

    • UseHopsworksAPIKeys: Whether to allow authentication via Hopsworks API keys. The default is true.

    • CacheRefreshIntervalMS: How often, in milliseconds, the cached API keys are refreshed. The default is 180000.

    • PreloadThreads: Number of threads for parallel API key preloading at startup. The default is 3.

FeatureStore#

  • FeatureStoreMetadataCache

    • PreloadThreads: Number of threads for parallel feature view preloading at startup. The default is 3.

TTLPurge#

TTL purge worker settings. Both settings can be changed at runtime with the TTL Purge REST API.

  • Enable: Whether this RDRS node runs the TTL purge worker. Purging can also be enabled/disabled at runtime via the /ttl-purge/config REST API; this setting provides the state at startup. The default is true.

  • ActiveWindow: Daily TTL purge active window in UTC, formatted "HH:MM-HH:MM", e.g. "03:00-05:00". The window may wrap past midnight (start > end). Purging only runs inside the window. Empty means no window: purging runs around the clock. A valid cluster-wide window in mysql.ttl_purge_ctrl (ctrl_id 2/3) takes precedence over this setting. The default is "".

Log#

  • Level: Not used in RonDB 26.10: rdrs2 writes its log through the RonDB event logger.

  • FilePath: Not used in RonDB 26.10: rdrs2 writes its log through the RonDB event logger.

  • MaxSizeMb: Not used in RonDB 26.10: rdrs2 writes its log through the RonDB event logger.

  • MaxBackups: Not used in RonDB 26.10: rdrs2 writes its log through the RonDB event logger.

  • MaxAge: Not used in RonDB 26.10: rdrs2 writes its log through the RonDB event logger.

  • LogQueries: Not used in RonDB 26.10: rdrs2 writes its log through the RonDB event logger.

MySQLRouter#

Configuration of myrouter, the MySQL protocol router built into rdrs2. It is not the MySQL Router product, even though the key in the configuration file is MySQLRouter. myrouter is currently only a testing tool. When enabled, myrouter accepts MySQL client connections. Aggregate SELECT queries outside transactions are executed with RonSQL when RonSQL supports them; all other statements are passed on to the backend MySQL Server set by BackendHost and BackendPort.

  • Enable: Whether to enable myrouter. The default is false.

  • ServerIP: The IP address to listen on. The default is "0.0.0.0".

  • ServerPort: TCP port on which myrouter listens for MySQL clients. The default is 3307.

  • NumThreads: Number of myrouter worker threads. The default is 4.

  • BackendHost: Host of the backend MySQL Server. The default is "127.0.0.1".

  • BackendPort: Port of the backend MySQL Server. The default is 3306.

  • DebugLogging: Log each SQL statement with its routing decision (PROXY, RONSQL, FALLBACK). The default is false.

Running RonDB REST Server#

The REST Server binary is named rdrs2 (rdrs in RonDB 22.10) and it is packaged in the bin of the RonDB tar ball. You can run the REST server follows

$ ./rdrs2 --config /path/to/config.json

Security and Authentication#

Currently, the REST API server only supports Hopsworks API Keys for authentication and authorization. In the future, we plan to extend MySQL server users and privileges to the REST API. Add the API key to the HTTP request using the X-API-KEY header. Of course, you should enable TLS when using API Keys to ensure that eavesdroppers do not steal your API Key

RonDB REST API at Hopsworks AB#

Using RonDB REST API Hopsworks has built REST API for Feature Stores. Feature Store is a platform that connects enterprise data to analytical and operational ML systems. The REST API is used to retrieve data for real-time inference from the online feature store and RonDB databases.