MemCP for Microservices

From MemCP
Revision as of 11:59, 28 August 2026 by Wikiservice (talk | contribs) (Refresh MemCP documentation: accuracy, operational guidance, performance profile and maintained API reference)
Jump to navigation Jump to search

MemCP for Microservices

MemCP can combine database storage, SQL-over-HTTP, and custom Scheme handlers in one process. This removes an application-to-database network hop and can simplify small services, but it is an architectural option rather than a latency guarantee.

Deployment patterns

Pattern Benefit Trade-off
Ordinary service plus MySQL protocol Familiar drivers, connection pools, and separation of concerns Network/serialization hop remains
Service plus SQL over HTTP Minimal client dependency and streamable JSONL General SQL endpoint needs strict network and grant controls
Embedded Scheme handler Prepared plans and storage access in one process; custom JSON or WebSocket API Application failure and database failure share a process and deployment lifecycle

Good candidates include small internal APIs, real-time dashboards, local/edge services, event lookup, and low-latency state whose data model remains relational. A large product with independent teams, many language-specific libraries, complex application logic, or strict process-isolation requirements may be easier to operate with a conventional separate service.

Preparing work outside the request path

The repository's apps/keyvalue.scm example creates a table and parses fixed SELECT/upsert statements once during startup. Each request creates a session, binds the key/value, and evaluates the prepared formula. That avoids parsing and planning the same statement on every request while keeping caller values separate from SQL text.

<syntaxhighlight lang="scheme"> (set item_get (parse_sql "keyvalue" "SELECT value FROM kv WHERE key = @key")) (set item_set (parse_sql "keyvalue" "INSERT INTO kv(key, value) VALUES (@key, @value) ON DUPLICATE KEY UPDATE value = @value")) </syntaxhighlight>

The complete example installs a request handler and starts its own HTTP listener:

<syntaxhighlight lang="scheme"> (createdatabase "keyvalue" true) (eval (parse_sql "keyvalue" "CREATE TABLE IF NOT EXISTS kv(key TEXT, value TEXT, UNIQUE KEY PRIMARY(key))"))

(set item_get (parse_sql "keyvalue" "SELECT value FROM kv WHERE key = @key")) (set item_set (parse_sql "keyvalue" "INSERT INTO kv(key,value) VALUES (@key,@value) ON DUPLICATE KEY UPDATE value=@value"))

(define http_handler (lambda (req res) (begin (set session (newsession)) (session "key" (req "path")) (if (equal? (req "method") "GET") (begin (set resultrow (lambda (row) ((res "print") (row "value")))) (eval item_get)) (begin (session "value" ((req "body"))) (eval item_set) ((res "print") "ok"))))))

(serve 1266 (lambda (req res) (http_handler req res))) </syntaxhighlight>

Run the maintained repository version with ./memcp apps/keyvalue.scm, store a value using curl --data-binary value http://localhost:1266/key, and retrieve it with curl http://localhost:1266/key. This demonstration intentionally omits production authentication and limits; do not expose it unchanged.

Use SQL constraints and one-statement atomic patterns even inside an embedded handler. In-process access removes transport overhead; it does not remove concurrent requests, transaction conflicts, authorization, or durability decisions.

Historical HTTP microbenchmark

An early AMD Ryzen 9 7900X3D run used ApacheBench against a tiny embedded endpoint with concurrency 10. It reported 1,000,000 completed requests, zero failures, 42.083 seconds, 23,762.78 requests/s, 0.421 ms mean request latency (0.042 ms across concurrent requests), p95 1 ms and maximum 2 ms. The author also observed that ab saturated one client core while MemCP used roughly 20% across other cores.

The original record does not include the exact command, commit, response validation beyond ApacheBench's failure count, server build or repeat runs. It demonstrates that the embedded path can be very small, but it is not a database-query or current-release throughput guarantee. A new comparison must include the handler code, successful body validation, client/server CPU profiles and several raw samples.

Production checklist

Choose each table ENGINE according to data-loss tolerance, configure total and persistent RAM budgets, and provide health checks, backups, monitoring, and restart validation. Set a strong root password before exposing either the HTTP or MySQL port. Persistent data should use safe unless the documented risks of another engine are explicitly acceptable.

Handler latency depends on query shape, compilation and plan-cache state, concurrency, memory pressure, and storage reloads. Benchmark the full authenticated request path with successful-response validation instead of quoting a fixed sub-millisecond figure.

Run embedded services under a supervisor, use --no-repl, implement external liveness/readiness checks, limit bodies and queues, propagate cancellation, and test restart/restore. Scaling the process horizontally requires an explicit ownership, replication, or routing design; the current local shard engine is not an automatic multi-node database.

See In-Database WebApps and REST Services, Persistency and Performance Guarantees, Memory Management and Eviction, Security and Authentication, and Performance Measurement.